:root{
  /* =====================================================================
   * COLOUR — expressed as DIALS, not literals.
   *
   * Every brand colour below is derived from the dial trio for its role
   * (hue / chroma / lightness) via oklch(), exactly as the v2 template
   * does. The kit's hex values are recorded in the comments only as the
   * target each formula reproduces — nothing here is a hard-coded hex.
   *
   * Verified: each oklch() below round-trips to its kit hex exactly.
   *   primary   oklch(0.4566 0.0383 144.9) -> #4A5D4A
   *   secondary oklch(0.6275 0.0507 137.4) -> #7A9072
   *   tertiary  oklch(0.4708 0.1121  50.8) -> #8B4513
   *   ink       oklch(0.3840 0.0197  73.3) -> #4A4238
   *   accent    oklch(0.9215 0.0284 117.8) -> #E3E8D3
   * ===================================================================== */

  /* --- Layer 1: anchors ---------------------------------------------- */
  --palette-hue: 144.9;      /* sage green — the kit's primary */
  --palette-chroma: 0.0383;  /* muted; a practice green, not a signal green */
  --l-solid: 0.4566;

  --hue-primary: var(--palette-hue);

  /* --- Layer 2: per-role -------------------------------------------- *
   * The kit's secondary sits only 7.5 deg from the primary but is 0.17
   * lighter and 1.3x the chroma — it is a designer's lighter sage, not a
   * rotation. That is exactly the case the per-role dials exist for, so
   * the shift is small and the lightness/chroma carry the difference. */
  --palette-secondary-shift: -7.5;
  --hue-secondary: calc(var(--palette-hue) + var(--palette-secondary-shift));
  --chroma-secondary: 0.0507;
  --l-secondary: 0.6275;

  /* Tertiary: the kit's "Button Brown 1". A genuine third brand colour,
   * 94 deg off the brand hue and ~3x its chroma — unreachable as a tint,
   * which is why it needs its own trio. NOTE: the source site defines
   * both browns in its kit but applies neither on any of the 16 pages,
   * so this role is currently declared and unexercised. Brown 1 is the
   * darker of the pair (L 0.47 vs 0.66) and is the one that holds a 4.5:1
   * contrast on the light grounds this site uses. */
  --palette-tertiary-shift: -94.1;
  --hue-tertiary: calc(var(--palette-hue) + var(--palette-tertiary-shift));
  --chroma-tertiary: 0.1121;
  --l-tertiary: 0.4708;

  /* --- Layer 3: neutrals — their own hue, NOT the brand's ------------ *
   * Balwyn's ink is at hue 73.3, which is 71.6 deg off the brand hue of
   * 144.9. A green practice does not want green text, and the kit agrees:
   * its "text" and "Heading Brown" are the same warm #4A4238. Both of the
   * site's light grounds sit at hue 84.6, the same warm family. So the
   * neutrals get their own dial rather than following --palette-hue. */
  --hue-neutral: 73.3;
  --chroma-ink: 0.0197;
  --chroma-ground: 0.0114;   /* Light BG 1 #F7F3EB measures 0.0114 */

  /* --- Layer 4: polarity --------------------------------------------- *
   * 1 = dark ink on a light ground. This site is light-only: the source
   * has no dark variant and a practice site that flips under the OS
   * setting would diverge from the printed brand. No dark-mode block. */
  --scheme: 1;

  /* --- Stacking order ------------------------------------------------ *
   * Named layers rather than ad-hoc z-index values, matching the
   * template's --layer-* scale so the numbers mean something. */
  --layer-behind: -1;
  --layer-base: 1;
  --layer-raised: 2;    /* the legal card and the interlock cells */
  --layer-nav: 3;
  --layer-overlay: 20;  /* header dropdowns */
  --layer-top: 100;     /* skip link */

  /* --- Derived brand colours ----------------------------------------- */
  --primary:        oklch(var(--l-solid) var(--palette-chroma) var(--hue-primary));
  --primary-hover:  oklch(calc(var(--l-solid) - 0.07) var(--palette-chroma) var(--hue-primary));
  --primary-pale:   oklch(0.96 calc(var(--palette-chroma) * 0.25) var(--hue-primary));

  --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));
  /* A LIGHT green that still clears WCAG AA on the cream grounds. --secondary
     itself is only 3.27:1 there - fine for large display type and for UI
     chrome, below the 4.5:1 floor for anything read as body copy. This sits
     midway down the lightness dial (0.5420) and measures 4.6:1, so it reads
     as the lighter green without failing the site's own accessibility floor.
     Used for the practitioners' post-nominals. */
  --secondary-readable: oklch(0.5420 var(--chroma-secondary) var(--hue-secondary));

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

  /* Accent: the kit's pale sage panel colour, at hue 117.8 — between the
   * brand green and the warm neutrals, which is why it is its own value
   * rather than a tint of either. */
  --accent:         oklch(0.9215 0.0284 117.8);
  /* The legal pages ground. The source site uses #F5F5F5, a plain light grey
     with ZERO chroma - the only cold neutral anywhere on this otherwise warm
     site, and it read as a different site's page. Deliberately diverged to
     the site's own cream so privacy-policy and terms-and-conditions sit on
     the same ground as every other page. Kept as its own token rather than
     replacing the uses, so the source value is one line away if it is ever
     wanted back. */
  --legal-ground:   var(--light-bg-2);

  /* --- Derived neutrals ---------------------------------------------- *
   * Lightness from the --l-* ramp, hue and chroma from the neutral dials
   * above, so all three are tunable without touching these lines. */
  --l-ink:        calc(0.384 + 0.60 * (1 - var(--scheme)));
  --l-bg:         calc(0.983 - 0.80 * (1 - var(--scheme)));
  --l-bg-alt:     calc(0.965 - 0.79 * (1 - var(--scheme)));
  --l-surface:    calc(1.000 - 0.78 * (1 - var(--scheme)));
  --l-ink-strong: calc(0.2243 + 0.75 * (1 - var(--scheme)));

  --text:          oklch(var(--l-ink) var(--chroma-ink) var(--hue-neutral));
  --heading-brown: oklch(var(--l-ink) var(--chroma-ink) var(--hue-neutral));
  --dark-brown:    oklch(var(--l-ink-strong) 0.0412 56.0);   /* kit "Dark Brown" #2A1607 */
  --light-bg-1:    oklch(var(--l-bg-alt) var(--chroma-ground) 84.6);  /* #F7F3EB */
  --light-bg-2:    oklch(var(--l-bg) 0.0142 84.6);                    /* #FEF9EF */
  --bg-white:      oklch(var(--l-surface) 0 0);
  --bg-black:      oklch(calc(1 - var(--scheme)) 0 0);

  /* Legacy aliases so markup written against the kit's own names keeps
   * working. Both browns resolve through the tertiary dials. */
  --button-brown-1: var(--tertiary);
  --button-brown-2: oklch(0.656 0.1295 62.6);   /* kit "Button Brown 2" #C87D2F */

  /* Dividers are the DARK green, at full strength. This was the secondary
     green at 45% opacity - a pale wash that read as grey against the cream
     grounds and disappeared entirely on the sage bands. Every rule on the site
     resolves through this token, so the change is made once here rather than
     at each of the ~20 call sites. */
  --rule:           var(--primary);

  /* --- type --- */
  --font-head: "Rethink Sans", "Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Mulish", "Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-num:  "Manrope", "Rethink Sans", system-ui, sans-serif;

  /* --- fluid rhythm ---
     The source drives its whole type scale off a fluid root
     font-size and then uses PLAIN rem on top. We keep that exact
     mechanism (see --root below) so rem units scale by themselves;
     these spacing tokens are the only things that clamp. */
  --gap-band:   var(--space-xl-2xl);
  --pad-x:      var(--space-s-l);
  --radius-lg:  1.875rem;
  --radius-md:  1.25rem;
  --radius-pill: 3.125rem;
  /* px, NOT rem: this site's root font-size is fluid, so 1.5rem was ~20px
     of travel on a phone and ~25px at 1440 - too small to notice spread over
     600px of scrolling. A reveal has to be seen to be worth having. */
  --reveal-distance: 40px;
  --shell:      82.5rem;
}
/* ---- Fluid space scale (from _template) ---------------------------------
   Imported verbatim rather than hand-rolled: every measurement transcribed
   from the live site is a px value at ONE viewport, and pasting those in is
   what produced the "correct at 1440, overflows at 1100-1366" failures. A
   token reflows at every width instead.
   NOTE: only the SPACE scale is imported. Type stays on this site's own
   fluid root font-size, which is matched to the live site's
   (16.4992px at 1440) - importing --step-* would fight it. */
:root{
    --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);
    --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);

    /* Two steps this site genuinely needs and the standard scale does not
       have. Balwyn's transcribed rhythm lands repeatedly on 25px, 50px and
       100px - values that fall BETWEEN --space-s (20.6) and --space-m (30.9),
       and between --space-l (41.2) and --space-xl (61.9). Adding a step is
       the convention's own escape hatch, and it is the right one here: the
       alternative was 40-odd one-off rem values scattered to work around a
       token that is 20% out.

       Sized against this site's fluid root (1rem = 16.4992px at 1440), so
       --space-sm lands on 25px and --space-lg on 50px where live has them. */
    --space-sm: clamp(1.15rem, calc(1.02rem + 0.55vw), 1.515rem);   /* 25px */
    --space-lg: clamp(2.2rem, calc(1.83rem + 1.6vw), 3.03rem);      /* 50px */
    --space-2lg: clamp(3.6rem, calc(2.85rem + 3.2vw), 6.05rem);     /* 100px */

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


/* --- THE fluid root -------------------------------------------------
   Transcribed verbatim from the source:
     html { font-size: calc(0.74980458572173rem + 0.31266284523189164vw) }
   = calc(11.9969px + 0.31266vw) against a 16px initial rem.
   Measured against the original: 18px @1920, 16.4992 @1440,
   15.1985 @1024, 13.2163 @390 — matched to 0.0001px.
   Every heading below is therefore plain rem, NEVER clamp(): a
   clamp() here fights the fluid root and shrinks mobile type. */
html{
  font-size: calc(0.74980458572173rem + 0.31266284523189164vw);
  -webkit-text-size-adjust:100%;
}

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

body{
  margin:0;
  font-family:var(--font-body);
  font-size:1rem;
  line-height:1.5;
  color:var(--text);
  background:var(--light-bg-2);
  overflow-x:hidden;
}

img{max-width:100%;height:auto;display:block}
/* --secondary-readable, not --secondary: the base link colour lands on the
   cream page ground, where --secondary measures 3.3:1 - under the 4.5:1 floor
   for body-size text. Same green, one step down the lightness dial. */
a{color:var(--secondary-readable)}
ul{margin:0}

h1,h2,h3,h4,h5,h6{
  font-family:var(--font-head);
  font-weight:700;
  color:var(--heading-brown);
  margin:0;
}

/* Heading-role paragraphs (kept as <p> where the source's outline
   already had a heading at that level). Reset the UA margin — leaving
   it in silently added ~40px per occurrence. */
.hero__subtitle,.section-head__title,.section-head__lead,
.panel__title,.panel__subtitle,.panel__lead,
.fees__sub,.fee-card__kicker,.fee-card__footnote,.price-note{margin:0}

.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* ---------- shared band shell ---------- */
/* Vertical rhythm belongs on .band itself, not only on the .prose/.phero
   variants: a bare <section class="band"> (services, patient-hub,
   practice-policy, forms-and-downloads) otherwise renders flush against the
   band above and below it, with no separation at all. Same scale the other
   bands use. */
.band{padding-inline:var(--pad-x)}
section.band:not([class*=" "]){
  /* 60px, matching the site-wide section ceiling further down: --space-xl-2xl
     resolves to 82.5px, which paired with a neighbour made a 165px gap. */
  padding-block:3.6366rem;
}
/* The variants that set their own vertical padding keep it. revert-layer here
   resolved to 0 (there is no lower layer), which silently removed the padding
   .prose bands used to inherit and shortened several pages - so target the
   bare band instead and leave every variant untouched. */
/* The hero cards deliberately overhang the photo, as on the live site.
   Clip on the BAND, never on html/body - clipping the root would break
   position:sticky everywhere, and it is the overhang that needs
   containing, not the document. */
.hero{overflow-x:clip}
/* The template's shared container utility. This site had the same behaviour
   split across two rules - max-width + centring on .band__inner, the inline
   padding on .band - so .u-container is defined here as the single source and
   .band__inner is an alias of it. Anything gaining a container should use
   .u-container directly rather than re-deriving one, which is the mistake the
   nav-gutter bug came from. */
.u-container,
.band__inner{
  max-width:var(--shell);
  margin-inline:auto;
  width:100%;
}
.u-container{padding-inline:var(--pad-x)}
.band--cream{background:var(--light-bg-2)}
.band--accent{background:var(--accent)}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.5em;
  font-family:var(--font-head);font-size:1.2122rem;font-weight:600;line-height:1.5;
  /* --primary, not --secondary. White on --secondary measures 3.46:1, under the
     4.5:1 floor for a button label - the file's own note on --secondary says as
     much ("fine for large display type and for UI chrome, below the 4.5:1 floor
     for anything read as body copy"). --primary is the darker green and clears
     it comfortably, and a solid CTA is exactly what the darker tone is for. */
  color:var(--bg-white);background:var(--primary);
  padding:.606em 1.818em;border-radius:var(--radius-pill);
  text-decoration:none;text-align:center;
  /* 44px touch-target floor. The padding above is in em and the font-size in
     rem, and this site's root font-size is FLUID - so on a phone every button
     shrank below the floor: the ACCESS FORM buttons measured 37.7px at 360 and
     never reached 44 at any width, and the header/footer CTAs sat at ~39px.
     A min-height costs nothing on desktop (they already exceed it) and does
     not change any button's appearance where it was already large enough. */
  min-height:max(2.75rem,44px);
  transition:background .18s ease;
}
.btn:hover,.btn:focus-visible{background:var(--primary)}
.btn svg{width:1em;height:1em;fill:currentColor;flex:none}
.btn--bold{font-weight:700;border-radius:6.25rem;font-size:1.2728rem}

/* ---------- eyebrow ---------- */
/* Homepage section eyebrow: a small mark beside a heading. Renamed from
   .eyebrow because the inner pages' caps-label paragraph uses that name and
   the two rules were merging - the label was inheriting display:flex. */
.eyebrow-row{
  display:flex;align-items:center;gap: var(--space-2xs);
}
.eyebrow-row img{width:1.818rem;height:1.818rem;flex:none}
.eyebrow-row h2,.eyebrow-row p{
  font-family:var(--font-body);font-size:1.25rem;font-weight:700;
  line-height:1.5;color:var(--dark-brown);margin:0;
}
.eyebrow-row--center{justify-content:center}
/* Eyebrows are ALWAYS caps. Applied here rather than by capitalising the copy
   because two of them interpolate {{practice_name}}, which resolves to "Balwyn
   Health Care" in title case - the source text cannot fix that without
   hardcoding the practice name and dropping the merge tag. */
.eyebrow-row h2,.eyebrow-row p,.eyebrow{text-transform:uppercase}

/* ============================================================
   HEADER
   ============================================================ */
.site-header{background:var(--light-bg-2)}
.site-header__inner{
  /* max-width, centring and padding-inline come from .u-container */
  display:flex;align-items:center;gap:var(--space-s-l);
  /* 10px off each edge, at the owner's request - the header is fixed overhead
     on every page, so it is the cheapest 20px of vertical space on the site.
     0.6061rem = 10 / 16.4992. */
  padding-block:calc(var(--space-s-l) - 0.6061rem);
  min-height:calc(10.18rem - 1.2122rem);
}
.site-header__logo{flex:none}
/* ~21% larger than the transcribed size, at the owner's request. The source
   image is 320px wide, so even the 1920 end of this clamp only renders it at
   0.59x - there is no upscaling and the mark stays crisp. Held to a clamp
   rather than a flat bump because the binding constraint is the gap to the
   nav at 1024-1150, which stays at 13-14px here; going wider closes it. */
.site-header__logo img{width:clamp(8.5rem,5.6rem + 4.6vw,11.5rem);height:auto}

/* The hamburger is a real <button>, hidden until the nav collapses. */
.nav-toggle-btn{display:none}

/* Skip link — keyboard users land here first. */
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:var(--layer-top);
  background:var(--primary);color:var(--bg-white);
  padding:.6em 1em;border-radius:0 0 .25rem 0;text-decoration:none;
  font-family:var(--font-head);font-weight:700;
}
.skip-link:focus{left:0}

.site-nav{
  flex:1 1 auto;min-width:0;
  display:flex;align-items:center;justify-content:space-evenly;gap:2.2%;
}
.site-nav__list{
  /* Item spacing is one fluid value (--nav-sep-gap, set further down) and the
     separator is positioned from it, so the pipe is always exactly midway
     between two labels. See the ::after rule below for why it is not in flow. */
  /* The <li> must end the same distance past a dropdown item's caret GLYPH as
     it does past a plain item's TEXT (the link's padding-right, 11.38px), or
     the separator - positioned off the <li> edge - sits off centre on those
     two items. That distance is the link padding MINUS the 2px of slack inside
     the caret's own box. Declared here, not on the link: the caret is the
     link's sibling and would never inherit it. */
  --nav-pad-rem:.57rem;
  display:flex;flex-wrap:nowrap;align-items:center;
  list-style:none;padding:0;margin:0;
}
.site-nav__list > li{display:flex;align-items:center;position:relative}
/* The separator is taken OUT of the flex flow and centred in the gap between
   two items. In flow (as an ::after inside the <li>) its left space came from
   the previous link's padding-right while its right space came from the list's
   flex gap - two different values, hence the asymmetry the owner spotted. The
   only way one value can control both sides is for the pipe not to occupy any
   width itself, so it is absolutely positioned at the midpoint of the gap. */
.site-nav__list > li:not(:last-child)::after{
  content:"";position:absolute;
  right:calc(var(--nav-sep-gap) / -2);
  top:50%;transform:translate(50%,-50%);
  width:1px;height:.73rem;background:var(--rule);
  pointer-events:none;
}
.site-nav__list a{
  --nav-pad-x:.6em;
  display:flex;align-items:center;gap:.25em;white-space:nowrap;
  font-family:var(--font-head);
  /* FLUID, not stepped. The row has a hard constraint - logo + nav + CTA on
     one line - and a fixed size cannot honour it across the whole desktop
     range. At 1.15rem the nav overlapped BOOK APPOINTMENT from ~1235 down to
     1151, where a hard font-size:1rem step at 1150 rescued it with 69px to
     spare: a 2.3px cliff, and a band of genuine breakage above it.

     This clamp is solved from measurement, not taste. At each width the
     largest font-size that still leaves 10px between the nav and the CTA was
     binary-searched; the constrained band (1200-1360) fits
     0.0161*vw - 1.99px. The curve below sits 0.6-1.2px under that line for
     safety and lands on 19.2px at 1440, which is where 1.15rem already
     resolved. Below ~1120 the nav is no longer the constraint - .site-nav is
     flex:1 1 auto with space-evenly and absorbs the slack - so the lower
     bound just holds a readable size. */
  font-size:clamp(15.5px, 1.0833vw + 3.6px, 19.2px);
  font-weight:700;line-height:1.4;
  color:var(--primary);text-decoration:none;
  padding:.24em var(--nav-pad-x);margin-inline:0;border-radius:.25rem;
  transition:background .18s ease,color .18s ease;
}
.site-nav__list a:hover{background:var(--accent)}
/* The active pill is the DARKER green, not --secondary. White on --secondary
   measured 3.47:1 - under the 4.5:1 floor, and the reason the current page's
   label was the hardest one to read in the open mobile menu. White on
   --primary is 7.1:1, keeps the white-on-green treatment, and uses a colour
   already in the palette rather than introducing one. */
.site-nav__list a[aria-current="page"]{background:var(--primary);color:var(--bg-white)}
.site-nav__list a[aria-current="page"]:hover{background:var(--primary)}

/* On a dropdown item the caret is a sibling <button>, so a background on the
   <a> alone stops short of it. The highlight therefore moves to the <li>, and
   the link's own background is suppressed, so label and caret read as one
   rounded shape. The button stays a real focusable button outside the anchor. */
.site-nav__list > li.has-sub{border-radius:.25rem}
.site-nav__list > li.has-sub:hover,
.site-nav__list > li.has-sub:focus-within{background:var(--accent)}
.site-nav__list > li.has-sub:hover > a,
.site-nav__list > li.has-sub:focus-within > a,
.site-nav__list > li.has-sub:hover > .sub-toggle,
.site-nav__list > li.has-sub:focus-within > .sub-toggle{background:transparent}
.site-nav__list > li.has-sub:has(> a[aria-current="page"]){
  /* --primary, as with the other two white-on-green nav states. */
  background:var(--primary);
}
.site-nav__list > li.has-sub:has(> a[aria-current="page"]) > a,
.site-nav__list > li.has-sub:has(> a[aria-current="page"]) > .sub-toggle{
  background:transparent;color:var(--bg-white);
}

.site-nav__list svg{width:1.15em;height:1.15em;fill:currentColor;flex:none}

/* ---- Dropdown submenus -------------------------------------------------
   The original nav opens these on hover via Elementor's JS. Here they open
   three ways so no input device is left out:
     hover           :hover, below
     keyboard        :focus-within, so tabbing into the group opens it
     touch / mouse   the .sub-toggle caret button, which sets .sub-open
   The parent stays a real link to its own landing page; the caret is a
   separate button beside it, so on a phone the label still navigates. */
.site-nav__list > li.has-sub{position:relative}

.sub-toggle{
  display:grid;place-items:center;
  width:1.15em;height:1.9em;flex:none;
  /* The caret is part of its item, not a gap-filler: it sits tight against its
     label, and the <li> ends the same distance past the caret GLYPH as a plain
     item ends past its text (the link's 11.38px padding-right). Because the
     separator is positioned off the <li> edge, matching that distance is what
     makes dropdown items measure identically to plain ones - at every font
     size, with no per-breakpoint nudging. .69em tracks the type as it steps. */
  margin-inline:-.1rem var(--nav-pad-rem,.69rem);padding:0;
  border:0;background:transparent;cursor:pointer;
  color:var(--primary);border-radius:.25rem;
  transition:background .18s ease,transform .18s ease;
}
/* The caret's VISUAL size is load-bearing (see above: it makes dropdown items
   measure identically to plain ones), so the touch target is grown with a
   pseudo-element instead of by resizing the button. Measured 15x25px, against
   a 44px floor - and between 1025 and 1180 the desktop nav is showing with no
   hamburger, so on a landscape tablet this caret IS the touch affordance for
   opening a submenu. position:relative on the button so the overlay anchors
   to it; the parent <li> keeps its own stacking, so this does not cover the
   sibling link. */
.sub-toggle{position:relative}
.sub-toggle::after{
  content:"";position:absolute;
  left:50%;top:50%;transform:translate(-50%,-50%);
  width:44px;height:44px;
}
.sub-toggle svg{width:.85em;height:.85em;fill:currentColor;display:block}
.sub-toggle:hover{background:var(--accent)}
.sub-toggle:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
li.has-sub.sub-open > .sub-toggle{transform:rotate(180deg)}

.site-nav__sub{
  position:absolute;top:100%;left:0;z-index:var(--layer-overlay);
  min-width:15rem;margin:0;padding: var(--space-3xs);
  list-style:none;
  /* Light green, not cream: it distinguishes the submenu panel from the
     header's own cream ground so the dropdown reads as a separate surface.
     Link text is --primary on it at 5.67:1, above the 4.5 floor. */
  background:var(--accent);
  border:1px solid var(--rule);border-radius:.5rem;
  box-shadow:0 6px 22px -8px oklch(0.2243 0.0412 56 / .28);
  opacity:0;visibility:hidden;transform:translateY(-.35rem);
  transition:opacity .18s ease,transform .18s ease,visibility .18s;
}
.site-nav__sub > li{display:block}
.site-nav__sub a{
  /* Inherit the top level's fluid size rather than restating one. The old
     1.2728rem resolved to 21px against a top level that is 19.2px at 1440,
     so the child menu was LARGER than its parent - and because the top
     level is a clamp() solved from the header's fit constraint, any fixed
     value here only matches at one viewport anyway. (The comment this
     replaces claimed 21px matched the top level; measured, it did not.) */
  /* flex, not block: the items now carry the branded mark beside the label,
     and a flex line keeps icon and text on one baseline-aligned row without
     the text wrapping under the icon on a narrow phone menu. */
  display:flex;align-items:center;gap:.5em;
  white-space:nowrap;
  font-family:var(--font-head);font-weight:700;
  font-size:clamp(15.5px, 1.0833vw + 3.6px, 19.2px);
  margin-inline:0;padding:.45em .7em;
}

/* Open state — hover, keyboard focus, or the caret button. */
li.has-sub:hover > .site-nav__sub,
li.has-sub:focus-within > .site-nav__sub,
li.has-sub.sub-open > .site-nav__sub{
  opacity:1;visibility:visible;transform:translateY(0);
}

/* The parent of the current page carries the SAME solid treatment as a
   directly-active top-level item, so "you are here" reads the same whether the
   page sits in a dropdown or not. --accent was indistinguishable from the
   hover wash, so on every submenu page the header looked like nothing was
   selected. The caret beside it is a sibling button, so the fill goes on the
   <li> and both children turn white. */
/* --primary, matching the active pill above and for the same reason: white on
   --secondary is 3.47:1. This is the state a visitor sees on any of the four
   service pages, so it is not a rare case. */
.site-nav__list > li.has-sub.is-ancestor{background:var(--primary)}
.site-nav__list > li.has-sub.is-ancestor > a,
.site-nav__list > li.has-sub.is-ancestor > .sub-toggle{
  background:transparent;color:var(--bg-white);
}

/* Hover must not wash the solid fill back out. */
.site-nav__list > li.has-sub.is-ancestor:hover,
.site-nav__list > li.has-sub.is-ancestor:focus-within{background:var(--primary)}

@media (prefers-reduced-motion:reduce){
  .site-nav__sub{transition:none}
  li.has-sub.sub-open > .sub-toggle{transform:none}
}
.site-header .btn{flex:none;white-space:nowrap;font-size:1.091rem}  /* live header CTA: 18px */

/* ============================================================
   HERO  (band 1)
   ============================================================ */
/* The hero opts OUT of --shell only for its inline padding - the photo is
   inset by margin, not by the container. It still honours the site's max
   width: without that the photo just kept growing on wide screens and the
   hero stopped agreeing with every band below it. A little wider than --shell
   (82.5rem) because this block is the page's opening statement, but bounded. */
.hero{background:var(--light-bg-2);padding-inline:0}
.hero .band__inner{max-width:92rem;margin-inline:auto}
.hero__inner{
  display:flex;flex-direction:column;gap: var(--space-s);
  /* One Utopia step down on the top (--space-xl-2xl -> --space-l-xl): the
     header already provides separation above the h1, so the larger step read
     as a gap rather than as breathing room. The bottom keeps the bigger step,
     which is separating the hero from the next band. */
  padding-block:var(--space-l-xl) var(--space-2xl-3xl);
}
.hero__title{
  font-size:4.4851rem;line-height:1.2;text-align:center;
  color:var(--heading-brown);
  padding-inline:var(--pad-x);
}
.hero__subtitle{
  font-family:var(--font-head);font-size:3rem;font-weight:700;line-height:1.2;
  color:var(--secondary);text-align:center;margin:0;
  padding-inline:var(--pad-x);
}
/* The photo is a container BACKGROUND-IMAGE in the source, not an
   <img>. Reproduced as such — an <img> would not let the cards
   overlap it the way they do. aspect-ratio keeps it fluid. */
/* The live hero photo is genuinely edge to edge: x=0, width = the full
   viewport, rightInset 0. It achieves that by letting the info-card column
   overhang the photo's right edge (margin-right:-50px), which is also why the
   live site scrolls sideways - document.scrollWidth 1470 against a 1440
   viewport.
   
   We keep the full-bleed photo AND the overhanging cards, but contain the
   overflow on the band with overflow-x:clip. Deliberately on the band, not on
   html/body: clipping the root would break position:sticky anywhere on the
   page, and it is the overhang that needs containing, not the document. */
.hero__photo{
  margin-top: var(--space-sm);
  /* INSET, not full-bleed. Live runs this photo edge to edge, but the info
     cards are overlaid on it and a bleeding photo gives them nothing to sit
     against - and because the card column overhangs to the right, an
     edge-to-edge photo is also visually off-centre on the page. Equal inline
     margins put the photo back on the page's own centre line. */
  margin-inline: var(--pad-x);
  background-image:url("../images/practice-front.webp");
  background-size:cover;background-position:center;
  border-radius:var(--radius-md);
  padding: var(--space-s);
  display:flex;justify-content:flex-end;
  aspect-ratio:1392/876;
}
/* Cards are 252px at 1440 and overhang the photo's right edge by
   50px (source: margin-right:-50px on the card column). Kept as a
   percentage + negative margin so both scale with the band. */
.hero__cards{
  display:grid;gap: var(--space-sm);
  /* A FLOOR, not just a cap. The photo's aspect-ratio never actually applies
     on desktop - the card column is always the taller of the two, so IT sets
     the hero's height. At 18.5% the cards narrow as the viewport does, their
     copy wraps onto more lines, and the hero grows: 926px tall at 1600 but
     1085px at 1240, i.e. the hero got TALLER as the screen got smaller.
     Measured, the copy stops wrapping at 275px and the height plateaus
     (1005 -> 911 at 1440, 1099 -> 898 at 1366); past 275 there is no further
     gain. So: 18.5% to scale up on wide screens, floored at 17.5rem (275px).
     The old cap was 15.75rem = 252px - BELOW that floor, so the cap itself
     was part of the problem; raised to 19.5rem. */
  width:clamp(17.5rem, 18.5%, 19.5rem);
  margin-right:-3.03rem;   /* overhangs the photo, as the live site does */
  align-content:start;
}
.info-card{
  background:var(--accent);border-radius:var(--radius-md);
  padding: var(--space-l);text-align:center;
}
.info-card__icon{
  width:1.5rem;height:1.5rem;margin-inline:auto;
  fill:var(--primary);display:block;
}
.info-card h2{
  font-size:1.5rem;line-height:1.5;color:var(--heading-brown);
  margin: var(--space-2xs) 0 1rem;
}
/* The hours tag ends with an italic "(private billing only - no concessions
   apply)" note. It belongs in the footer, which renders the same tag, but not
   in the hero card, where it is a fourth line of small print in a summary
   block. Hidden here only - the tag itself is in .merge-tags.json, which is
   managed by the system and must not be edited. */
.hero .info-card .rich em{display:none}
/* The address tag is "4 Belmore Road / Balwyn / VIC 3103" over three lines; in
   the hero card it should read as two - street, then suburb and state on one
   line. The tag is in .merge-tags.json, which is managed by the system and
   must not be edited, so the last <br> is suppressed here and a space put in
   its place with an ::after. */
/* Two lines, not three: "4 Belmore Road" then "Balwyn VIC 3103". CSS cannot
   reliably suppress a <br> - it honours neither display:none nor content on
   the element for that purpose - so the join is done in the page's own script
   instead (see partials/scripts or the inline block on index.html). */

.info-card p{
  font-size:1.125rem;line-height:1.5;color:var(--dark-brown);margin:0;
}
/* The Opening Hours card carries the same <table class="hours-table"> the
   footer does, and the same two-column layout was breaking "Mon-Fri" and
   "8:30am-6:00pm" across lines in a card this narrow. Unwrapped to a stacked
   pair, matching .site-footer__hours. */
.info-card .hours-table,
.info-card tbody,
.info-card tr,
.info-card td{display:block;width:auto}
.info-card .hours-table{border-collapse:collapse;margin-inline:auto}
.info-card tr + tr{margin-top:var(--space-2xs)}
.info-card td:first-child{
  font-weight:700;color:var(--heading-brown);
  font-size:1.125rem;line-height:1.35;
}
/* A time range is one unit: never break it after the en dash. */
.info-card td + td{font-size:1.0625rem;line-height:1.5;white-space:nowrap}

/* ============================================================
   URGENT CARE  (band 2)
   ============================================================ */
.urgent__inner{
  display:flex;flex-wrap:wrap;gap: var(--space-lg);justify-content:center;
  padding-block:var(--space-xl-2xl);
}
.urgent__card{
  flex:1 1 26rem;
  background:var(--light-bg-2);border-radius:var(--radius-md);
  padding: var(--space-l);
  display:flex;flex-direction:column;gap: var(--space-s);
}
.urgent__card h2{
  font-size:2rem;line-height:1.3;color:var(--primary);
}
.rich{font-size:1.125rem;line-height:1.5;color:var(--text)}
.rich p{margin:0 0 .8em}
.rich p:last-child{margin-bottom:0}
.rich ul{padding-left: var(--space-l)}
/* Underlined at rest, like .prose links already are. These sit inside running
   body copy, where colour is the only other thing distinguishing them - and at
   1.3:1 against the surrounding text that is not a distinction a colour-blind
   reader can see (WCAG 1.4.1, "use of colour"). Restoring it on hover only
   helps a reader who is already pointing at the link. */
.rich a:not(.btn){color:var(--primary);text-decoration:underline}
.rich a:hover{text-decoration:none}

/* ============================================================
   SERVICES  (band 3)
   ============================================================ */
.services__inner{
  display:flex;flex-direction:column;gap: var(--space-lg);
  padding-block:var(--space-xl-2xl);
}
.section-head{
  display:flex;flex-direction:column;gap: var(--space-2xs);text-align:center;
  align-items:center;
}
.section-head__title{
  /* Live uses BOTH colours at this size and it is a per-instance choice, not
     a rule: "Comprehensive Care for Every Stage of Life" is the primary green
     rgb(74,93,74) while "Expert Care From Experienced GPs" is the brown ink
     rgb(74,66,56). Ink is the default; --green opts in. */
  font-family:var(--font-head);font-weight:700;
  font-size:2.4850rem;line-height:1.2;color:var(--heading-brown);
  margin:0;
}
.section-head__title--green{color:var(--primary)}
.section-head__lead{
  font-size:1.125rem;line-height:1.5;color:var(--text);margin:0;
  max-width:56rem;
}
.section-head .btn{margin-top: var(--space-s)}
/* Title and CTA on ONE line, pushed to opposite ends - the arrangement the
   live cosmetic-medicine page uses for "Treatments Available". The base
   .section-head is a centred column and is shared with the homepage, so this
   is a modifier rather than a change to it. Wraps to a stack below the
   tablet breakpoint, where there is no room for both on a line. */
.section-head--split{
  flex-direction:row;flex-wrap:wrap;text-align:left;
  align-items:center;justify-content:space-between;
  gap: var(--space-s);
}
.section-head--split .section-head__title{margin:0}
.section-head--split .btn{margin-top:0;flex:none}
@media (max-width:767px){
  .section-head--split{flex-direction:column;text-align:center;align-items:center}
}
/* ~80px between the lead row and the Treatments panel, not the ~165px the two
   sections' facing 82.5px paddings add up to. Halved on each side rather than
   zeroed on one, so the gap stays symmetrical if either section is reordered.
   2.4244rem = 40 / 16.4992, x2 = 80. */
/* `.band.plain-hero{padding-block:...}` further down is also (0,2,0) and comes
   LATER, so it was winning and the hero kept its full 82.5px - the measured
   gap was 25px, not the 80px intended. Matching .band here makes this (0,3,0)
   so it holds regardless of source order. */


.services__grid{
  display:grid;grid-template-columns:repeat(2,1fr);gap: var(--space-m);
}
/* Both lists hug the centre rule, so the pair reads as one centred group.
   The tick lists are far narrower than their grid tracks (the longest item is
   ~460px in a ~700px track), so with the default start-alignment the left
   column ended ~335px short of the rule and the whole block sat left-heavy.
   The left column is pushed to the track's end and the right one starts at
   its edge; each list keeps its own text left-aligned, because ragged-left
   text under tick bullets is much harder to scan. */
.services__col{display:flex}
.services__col:first-child{justify-content:flex-end}
/* the vertical rule between the two columns */
.services__col + .services__col{
  border-left:1px solid var(--rule);
  padding-left: var(--space-lg);
}
.tick-list{list-style:none;padding:0;margin:0}
.tick-list li{
  display:flex;align-items:flex-start;gap: var(--space-xs);
  padding-block:.106rem;margin-block:.106rem;
  font-size:1.125rem;line-height:1.5;color:var(--text);
}
.tick-list svg{
  width:.91rem;height:.91rem;flex:none;margin-top:.42em;
  fill:var(--primary);
}

/* ============================================================
   PROMISES  (band 4) — 4 columns split by vertical rules
   ============================================================ */
.promises__inner{
  display:grid;grid-template-columns:repeat(4,1fr);
  /* No block padding here: .promise carries its own, and having both meant
     ~62px twice over at each end. The columns' padding is the single source,
     which also lets the dividers - which are borders on .promise - match the
     content height rather than running past it. */
  padding-block:0;
}
.promise{
  /* Inline padding trimmed so the headings have room to break in two lines
     rather than three: at --space-s-l each column gave its heading only 253px
     of the 335 available, and "Same-day Appointments for Urgent Needs" needed
     a third line while its neighbours took two. 1.5152rem = 25 / 16.4992. */
  /* ~50px above and below, not --space-l-xl (~62px): with the inner's padding
     removed this is the only vertical space in the band, and it also sets the
     divider height, since the rules are borders on this element. 3.0304rem =
     50 / 16.4992. */
  /* Inline padding in px, not the fluid rem: a rem grows with the root while
     the column width is pinned by the 1500px cap, so a rem-based inset kept
     eating MORE of the column as the viewport widened - which is what made
     these read as cramped at 1600 and 1920. 30px each side leaves the longest
     heading its two lines at every width the cap allows. */
  /* 50px in px, not the 3.0304rem this was. The rem is relative to this site's
     FLUID root, so "50px" only held at 1440 - it drifted to 51.5 at 1600 and
     54.5 at 1920, which is why the vertical rules kept measuring taller than
     the content they were supposed to match. The rules are borders on this
     element, so its padding IS the divider's overhang: fixing the padding in
     px is what makes the divider track the content at every width. */
  padding:50px 30px;
  text-align:center;
  display:flex;flex-direction:column;align-items:center;
}
.promise + .promise{border-left:1px solid var(--rule)}
.promise__icon{
  width:3.03rem;height:3.03rem;border-radius:50%;
  background:var(--secondary);
  display:grid;place-items:center;flex:none;
}
.promise__icon svg{width:1.5rem;height:1.5rem;fill:var(--bg-white)}
/* Headings break into balanced lines rather than filling each line and
   dropping a short remainder - "Same-day Appointments for Urgent Needs" ran
   to three lines against its neighbours' two. text-wrap:balance distributes
   the words evenly across however many lines it needs. */
.promise h2{
  /* The break points are explicit <br>s in the markup, matching the reference.
     Neither text-wrap:balance nor a ch-based measure held across viewports,
     because this site's type is fluid - what fits on two lines at 1440 needs
     three at 1920. The <br>s are suppressed below the tablet breakpoint, where
     the columns stack and each heading has the full width.

     The size is capped in px rather than left on the fluid rem: the column
     width tops out with the 1450px content cap while a rem keeps growing with
     the root, so above ~1920 the longest heading outgrew its column and took a
     third line. The floor does the same from the other direction on a narrow
     laptop. */
  font-size:1.5rem;line-height:1.5;color:var(--heading-brown);
  margin: var(--space-2xs) 0 1rem;
}
.promise p{
  font-size:1rem;line-height:1.5;color:var(--dark-brown);margin:0;
}

/* ============================================================
   TEAM  (band 5)
   ============================================================ */
.team__inner{
  display:flex;flex-wrap:wrap;gap:var(--gap-band);
  padding-block:var(--space-2xl-3xl);
}
.team__panel{
  flex:1 1 24rem;
  background:var(--accent);border-radius:var(--radius-md);
  padding: var(--space-l);
  display:flex;flex-direction:column;gap: var(--space-s);
}
.team__panel h2.section-head__title{text-align:left;font-size:2.5rem}
.team__names{
  display:grid;grid-template-columns:repeat(2,1fr);gap: var(--space-s);
  margin-top: var(--space-2xs);
}
/* Doubled class to beat `.prose ul` (0,2,0), which sets padding-left:1.5em and
   otherwise indents the list 28.5px past the heading, lede and button beside
   it - the misalignment the owner marked on the About page. A bare .name-list
   (0,1,0) loses to it. */
.name-list,.name-list.name-list{list-style:none;padding:0;margin:0}
.name-list li{
  font-family:var(--font-head);font-size:1.25rem;font-weight:700;line-height:1.4;
  color:var(--primary);
  padding-block: calc(var(--space-xs) + var(--space-3xs));
}
.name-list li{border-bottom:1px solid var(--rule)}
/* Transparent, not removed: dropping the border made the last item shorter
   than the rest, which is what tipped the two columns to 4/5 and stopped the
   rows aligning across the gap. Reserving the space keeps every item the same
   height while still showing no rule under the final name. */
.name-list li:last-child{border-bottom-color:transparent}
.name-list--split{columns:2;column-gap:var(--space-m)}
/* A name is one unit: never break "Dr Mary-Anne Lancaster" across lines. */
.name-list--split li{break-inside:avoid;white-space:nowrap}
@media (max-width:40rem){.name-list--split{columns:1}}
.team__panel .btn{align-self:flex-start;margin-top: var(--space-sm)}

.team__grid{
  flex:1 1 30rem;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap: var(--space-s);
  align-content:start;
}
.team__col{display:flex;flex-direction:column;gap: var(--space-s)}
.doctor{
  display:grid;grid-template-columns:1fr 1fr;gap: var(--space-m);align-items:center;
}
.doctor__photo{
  width:100%;aspect-ratio:1;object-fit:cover;
  border-radius:var(--radius-md);
}
.doctor h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.2728rem;line-height:1.4;color:var(--primary);margin:0 0 .25rem;
}
.doctor p{font-size:0.9697rem;line-height:1.5;color:var(--text);margin:0}  /* live: 16px */

/* ============================================================
   SPECIAL SERVICES  (band 6) & BOOKING (band 8) — accent panels
   ============================================================ */
.panel{
  background:var(--accent);border-radius:var(--radius-lg);
  /* Inline padding is ~40px (2.4244rem = 40 / 16.4992), not the 1.21rem the
     transcription started with: at 20px the heading and the three columns sat
     hard against the panel's rounded edge, which reads as a clipping bug
     rather than as a card. Vertical padding is unchanged. */
  padding:var(--space-2xl-3xl) 2.4244rem;
  display:flex;flex-direction:column;gap:var(--space-l-2xl);
}
.panel-band{padding-block:0}
/* ~50px of air between the consultation-time cards and the booking panel
   directly below. .panel-band zeroes its own block padding, so without this
   the fee cards butt into the panel's top edge. 3.0304rem = 50 / 16.4992. */
.fees + .panel-band{margin-top:3.0304rem}
.panel__head{
  display:flex;flex-wrap:wrap;gap:var(--gap-band);
  justify-content:space-between;align-items:flex-end;
}
.panel__head-left{flex:1 1 24rem;display:flex;flex-direction:column;gap: var(--space-2xs)}
.panel__head-right{
  flex:1 1 20rem;display:flex;flex-direction:column;gap: var(--space-s);
  align-items:flex-end;justify-content:flex-end;text-align:right;
}
.panel__title{font-family:var(--font-head);font-weight:700;font-size:2.5rem;line-height:1.2;margin:0}
.panel__title--lg{font-size:3rem}      /* "Booking Information" is 49px live */
.panel__subtitle{font-family:var(--font-head);font-weight:700;font-size:2rem;line-height:1.3;color:var(--primary);margin:0}
.panel__lead{font-size:1.125rem;line-height:1.5;color:var(--text);margin:0}

.specials{
  display:grid;grid-template-columns:repeat(4,1fr);gap: var(--space-m);
}
.special{
  display:flex;flex-direction:column;align-items:center;gap: var(--space-l);
  text-align:center;
}
/* Each card carries the photo the source places directly above its heading.
   Square via aspect-ratio rather than a fixed height, so the card reflows
   with its column instead of pinning a pixel value. */
.special__photo{
  width:100%;max-width:20rem;height:auto;aspect-ratio:1/1;
  object-fit:cover;border-radius:var(--radius-md);
  flex:none;
}
.special__body{display:flex;flex-direction:column}
.special h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5152rem;line-height:1.5;color:var(--primary);margin:0 0 1rem;
}
.special p{font-size:1.125rem;line-height:1.5;color:var(--text);margin:0}
.special .btn{margin-top:auto}

/* ============================================================
   FEES  (band 7)
   ============================================================ */
.fees__inner{
  display:flex;flex-direction:column;
  padding-block:var(--space-2xl-3xl) 0;
}
.fees__head{
  display:flex;flex-wrap:wrap;gap: var(--space-s) 4.375rem;
  justify-content:space-between;align-items:flex-end;
  margin-bottom:var(--gap-band);
}
.fees__head-left{flex:1 1 28rem;display:flex;flex-direction:column;gap: var(--space-2xs)}
.fees__title{font-size:2.5rem;line-height:1.2}
.fees__sub{
  font-family:var(--font-head);font-size:1.25rem;font-weight:700;
  /* --secondary-readable: this is 1.25rem bold, which clears the large-text
     3:1 floor at desktop - but the root font-size is fluid, so on a phone it
     drops under the 18.66px large-text threshold and the 4.5:1 floor applies,
     where --secondary is 3.3:1. Failed at 390px only. */
  line-height:1.4;color:var(--secondary-readable);margin:0;font-style:italic;
}
.fees__grid{
  display:grid;grid-template-columns:repeat(2,1fr);gap: var(--space-xl);
}
.fee-card{
  background:var(--accent);border-radius:var(--radius-md);
  padding:var(--space-m-l);
  display:flex;flex-direction:column;gap: var(--space-s);
}
.fee-card__kicker{
  font-family:var(--font-head);font-size:1.25rem;font-weight:700;
  line-height:1.4;color:var(--primary);margin:0;
}
.fee-card__heading{
  display:flex;align-items:center;gap: var(--space-2xs);
}
.fee-card__heading img{width:1.818rem;height:1.818rem;flex:none}
.fee-card__heading h3{font-size:2rem;line-height:1.3;color:var(--heading-brown)}
.fee-card__heading--brown h3{color:var(--dark-brown)}
.fee-card__meta{
  display:grid;grid-template-columns:repeat(2,1fr);gap: var(--space-s);
  align-items:center;padding-left: var(--space-l);
}
.fee-card__meta strong{
  font-family:var(--font-head);font-size:1.25rem;font-weight:700;
  line-height:1.4;color:var(--text);
}
.fee-card__meta em{
  font-size:1rem;font-weight:700;font-style:italic;color:var(--text);
}
/* Price rows: title — leader rule — price. The leader is a
   flex:1 spacer carrying a border-bottom, exactly as the source. */
.price-list{list-style:none;padding:0;margin:0}
.price-list li{
  display:flex;align-items:flex-start;
  padding-block:.727em;
}
.price-list li + li{border-top:0}
.price-row{display:flex;align-items:center;gap:0;width:100%}
.price-row__title{
  font-family:var(--font-head);font-size:1.5rem;font-weight:700;
  line-height:1.5;color:var(--primary);flex:0 1 auto;
}
.price-row__leader{
  flex:1 1 auto;border-bottom:1px solid var(--rule);
  margin-inline: var(--space-xs);min-width:1.5rem;
}
.price-row__price{
  font-family:var(--font-num);font-size:1.333rem;font-weight:900;
  color:var(--primary);flex:none;
}
.price-note{
  font-family:var(--font-num);font-size:.909rem;font-weight:500;
  line-height:2;color:var(--heading-brown);margin: var(--space-2xs) 0 0;
}
.fee-card__footnote{
  font-size:1.125rem;line-height:1.1;color:var(--text);margin:0;
}

/* ============================================================
   BOOKING  (band 8)
   ============================================================ */
.booking__body{
  display:flex;flex-wrap:wrap;gap: var(--space-lg);justify-content:space-between;
  align-items:flex-start;   /* columns top-align rather than stretching */
}
/* justify-content:flex-start, NOT center: centring each column vertically made
   the three headings start at different heights - "Test Results & Follow-Up"
   sat well above "Billing & Payments", which is the misalignment the owner
   marked. The columns now start together at the top. */
.booking__col{flex:1 1 21rem;max-width:36%;display:flex;flex-direction:column;gap: var(--space-s);justify-content:flex-start}
.booking__col h3{font-size:2rem;line-height:1.3;color:var(--heading-brown)}
.booking__right{
  flex:1 1 34rem;display:flex;flex-wrap:wrap;gap: var(--space-lg);
}
/* photo is a container background-image in the source */
.booking__photo{
  flex:1 1 18rem;
  background-image:url("../images/consulting-room.webp");
  background-size:cover;background-position:center;
  border-radius:var(--radius-md);
  aspect-ratio:481/691;min-height:20rem;
  flex:1 1 20rem;
}
.booking__aside{flex:0 1 17rem;display:flex;flex-direction:column;gap: var(--space-s);justify-content:flex-start}
/* The photo above "Test Results & Follow-Up", matching the About page's
   Quality & Accreditation block. Aspect pinned so the reserved box and the
   rendered shape agree and the lazy load does not shift the column. */
.booking__aside-photo{
  width:100%;height:auto;aspect-ratio:558/400;object-fit:cover;
  border-radius:15px;display:block;
}
.booking__aside h3{font-size:2rem;line-height:1.3;color:var(--heading-brown)}
.rule{border:0;border-top:1px solid var(--rule);margin: var(--space-xs) 0}

/* ============================================================
   MORE BOOKING  (band 9) — outlined card
   ============================================================ */
.more__inner{padding-block:var(--space-xl-2xl)}
.more__card{
  border:1px solid var(--secondary);border-radius:var(--radius-lg);
  background:var(--light-bg-2);
  /* Grid, not flex. The two columns have to end up the same height with the
     TEXT deciding what that height is, and wrapped flex lines cannot do it:
     a flex line's height is set by its own items, so `align-items:stretch`
     had nothing to stretch the photo against and it stayed at its min-height
     (264px beside a 710px column at 1440). A grid row equalises for real. */
  display:grid;
  grid-template-columns:1fr 1fr;
  align-items:stretch;
}
/* The source leaves this half of the card genuinely EMPTY on
   desktop — reproduced, but collapsed away below 1024 so it does
   not become dead scroll on a phone. */
.more__spacer{flex:1 1 24rem;padding: var(--space-l);min-height:50.4rem}

/* ---- Slideshow (More Booking Information) --------------------------------
   CSS-only: five radio inputs drive which slide shows, and the labels are the
   dots. No JS and no library, and with CSS off it degrades to the five images
   stacked rather than to nothing.

   It takes the place of the empty half-panel the source had, and sits in the
   same flex slot, so it matches the body column's height automatically. */
.more__slideshow{
  /* The 40px sits on the OUTER element as margin, so the photo is inset from
     the card's border on all four sides rather than touching it. Padding
     would not do: the track is the thing being rounded and clipped, and
     padding would show the card's ground inside the slideshow's own corners. */
  min-width:0;margin:40px;
  position:relative;
  border-radius:var(--radius-lg);overflow:hidden;
  /* An aspect ratio, not the spacer's old fixed min-height: the slides are
     absolutely stacked and so have no intrinsic height, and without this the
     column collapsed to its content and then stretched the text column to
     match whatever the flex row settled on - the body's 370px of copy was
     being pulled to 1129px.

     The ratio is a FLOOR (min-height), not a fixed shape. Two things had to
     be true at once: the photo must match the text column's height, and it
     must not crop hard. A fixed aspect-ratio gave the second and broke the
     first - at 1920 it left a 456px photo beside a 719px column. Stretching
     with a fixed ratio gave the first and broke the second, because the box
     grew to the column's height and re-squared itself, which is what caused
     the ~3.6x zoom in the first place.

     The TEXT column must set the row's height and the photo must fill it, so
     the photo needs to contribute NOTHING to the row's height while still
     stretching to it. height:0 does exactly that: it takes the img's
     intrinsic height out of the flex basis, and align-items:stretch on the
     card then grows the box to the copy's height.

     The two alternatives both fail, and both were tried:
       - a fixed aspect-ratio holds its shape but not the column's height
         (456px photo beside a 719px column at 1920);
       - a min-height makes the PHOTO the taller item, so it sets the row and
         drags the text column down with it - 684x1154, and the ~3.6x crop
         comes straight back.

     With the card as a grid the box needs no height of its own at all - the
     row gives it one. The slides are taken out of flow (see .slideshow__track
     below) so nothing inside pushes back, and the photo simply fills whatever
     the copy's column turns out to be. */
}
/* Absolutely positioned, so the images contribute NO height to the grid row.
   In flow they did: the img's intrinsic height became the row's floor and the
   photo column grew to 1061px, dragging the text column with it. Out of flow,
   the text column alone decides the height and the photo fills it. */
.slideshow__track{
  position:absolute;inset:0;
  display:grid;   /* every slide in the same cell, so they stack */
}
.slideshow__track img{
  grid-area:1 / 1;
  /* min-height:0 matters: without it the img's intrinsic height becomes the
     grid item's minimum, the track grows past the box, and `cover` then crops
     against a slot far taller than what is visible - the 684x1154 img inside a
     684x639 box that read as a ~3.6x zoom. */
  width:100%;height:100%;min-height:0;object-fit:cover;display:block;
  opacity:0;transition:opacity .5s ease;
}
/* consulting-room.webp is a 962x1623 portrait among four landscapes. It is
   still `cover` like the rest - `contain` letterboxed it into a narrow strip
   and, because the slides are stacked in one grid cell and the outgoing slide
   is not fully transparent mid-crossfade, the previous photo showed through
   its empty margins. Cropping to the TOP of the frame keeps the room in shot
   rather than centring on the floor. */
.slideshow__track img[src*="consulting-room"]{
  object-position:50% 20%;
}
/* The exterior is a wide street view cropped into a much taller slot. This
   frame puts the entrance slightly right of centre, so nudging the crop that
   way keeps the doorway and the practice sign in shot. */
.slideshow__track img[src*="clinic-exterior"]{
  object-position:58% 50%;
}
/* ---- Auto-advance --------------------------------------------------------
   The slides cycle on their own via a keyframe, one 20s loop shared by all
   five with a staggered negative delay, so each is opaque for its own fifth of
   the cycle and crossfades into the next. Still CSS-only, still no library.

   There are no dots and no radio inputs any more - the rotation is the whole
   control. That removes the manual override the radios provided, so the first
   slide is no longer special-cased; every slide is purely keyframe-driven.

   Pacing: an earlier version ran a 25s loop whose crossfade was ~1s, so the
   box was a still photo for 96% of its life and read as broken - measured, not
   guessed: opacity sampled every second showed a single frame of movement per
   slide. Each slide now holds ~2.4s and crossfades ~1.6s, so a visitor
   glancing at the band during the hold still catches the NEXT transition
   within a couple of seconds rather than waiting out a four-second hold. */
@keyframes more-slide-cycle{
  0%,  12% {opacity:1}   /* hold ~2.4s of the 20s loop */
  20%, 92% {opacity:0}   /* 1.6s fade out, then wait its turn */
  100%     {opacity:1}   /* 1.6s fade back in, wrapping the loop */
}
.more__slideshow .slideshow__track img{
  animation:more-slide-cycle 20s infinite;
}
.slideshow__track img:nth-child(1){animation-delay:0s}
.slideshow__track img:nth-child(2){animation-delay:-16s}
.slideshow__track img:nth-child(3){animation-delay:-12s}
.slideshow__track img:nth-child(4){animation-delay:-8s}
.slideshow__track img:nth-child(5){animation-delay:-4s}

/* Pause on hover, so a visitor reading a photo is not moved on mid-look. */
.more__slideshow:hover .slideshow__track img{animation-play-state:paused}

@media (prefers-reduced-motion:reduce){
  /* No transition AND no rotation: an auto-advancing carousel is exactly the
     motion this setting exists to stop. With the rotation off, the first
     slide is the one left standing, so it is pinned opaque - otherwise every
     slide keeps its opacity:0 start value and the box renders empty. */
  .more__slideshow .slideshow__track img{transition:none;animation:none}
  .more__slideshow .slideshow__track img:nth-child(1){opacity:1}
}

/* The text column matches the slideshow's height, as asked. It has far less
   content than the photo is tall, so the copy is centred in the space rather
   than sitting at the top with a long tail of empty card beneath it. */
.more__card{align-items:stretch}
.more__body{justify-content:center;
  flex:1 1 24rem;
  /* Top and bottom both --space-xl-2xl. The bottom was a hardcoded 1.875rem
     while the top was the fluid token, so the copy sat 90px from the top of
     the card and only 34px from the bottom - the button looked crowded
     against the card's edge. They are the same token now, so the block stays
     optically centred at every width. */
  padding:var(--space-xl-2xl) var(--space-m)
          var(--space-xl-2xl) var(--space-s-l);
  display:flex;flex-direction:column;gap: var(--space-s);
}
.more__body h3{font-size:2rem;line-height:1.3;color:var(--primary)}
.more__body .btn{align-self:flex-start;margin-top: var(--space-s)}

/* ============================================================
   ASSURANCE  (band 10)
   ============================================================ */
.assurance__inner{
  display:flex;flex-wrap:wrap;gap: var(--space-lg);
  /* Top only. The band already supplies the bottom (and, when it is the last
     band, the whole run down to the footer), so padding on both ends here
     stacked with it and left the cards sitting high - 167px of green above
     them against 118 below. */
  padding-block:var(--space-xl-2xl) 0;
}
.assurance__card{
  flex:1 1 24rem;
  background:var(--light-bg-2);border-radius:var(--radius-md);
  padding: var(--space-l);
  display:flex;flex-direction:column;gap: var(--space-s);
}
.assurance__card h2{font-size:2rem;line-height:1.3;color:var(--heading-brown)}
/* Two classes plus an element, so this outranks .link-strong and was still
   painting --secondary (3.3:1 on the cream card). */
.assurance__card .rich a{color:var(--secondary-readable);font-weight:700}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer{background:var(--light-bg-2);padding-inline:var(--pad-x)}
.site-footer__inner{
  /* max-width, centring and padding-inline come from .u-container. This one
     previously had no padding-inline at all - the exact omission the utility
     exists to prevent. */
  display:grid;
  /* FIVE columns: logo, Visit Us, Operating Hours, Quick Links, Contact Us.
     An earlier pass collapsed this to four because Quick Links and Contact Us
     were fighting for room - the footer now spans 1450px rather than the
     82.5rem shell, which is what buys the width back. The logo column is the
     narrowest since it holds only the lockup. */
  /* The logo column is sized to the logo, not given an equal share: as a
     proportional track it left a wide empty gap between the mark and "Visit
     Us", which is what the owner marked in red. auto lets it shrink to its
     content and hands the slack to the four text columns. */
  /* Columns sized to their CONTENT, not to equal shares. Equal tracks look
     even on paper but not on the page: each column's text is a different
     width, so the whitespace a reader actually sees ran 40 / 151 / 83 / 137px.
     max-content tracks make every column exactly as wide as its widest line,
     so the gap between one column's text and the next is the same everywhere.
     justify-content spreads the leftover across the row. */
  grid-template-columns:repeat(4,max-content);
  justify-content:space-between;
  /* A tighter column gap: at --space-s the five columns drifted apart and the
     footer read as five unrelated blocks rather than one row. The row gap
     stays larger, for when the grid wraps to two columns on a tablet. */
  /* A larger column gap: at --space-2xs (10px) the address sat hard against
     the logo while the other columns, being wider than their text, looked
     further apart. An even gap plus equal tracks makes the spacing read the
     same all the way across. 2.4244rem = 40 / 16.4992. */
  gap: var(--space-m) 2.4244rem;align-items:stretch;
  /* 40px top AND bottom. This was a fluid pair that resolved asymmetrically -
     31px top against 40px bottom at 1440, 21/27 on a phone - so the footer
     content sat visibly high in its own band. px, not the fluid tokens: this
     site's root font-size is fluid, so a rem value drifts with the viewport
     and would not hold 40 anywhere but one width. */
  padding-block:40px;
}

/* The five max-content tracks above do not FIT until ~1150px, but the
   two-column fallback is gated at max-width:1024 - so 1025-1149 got five
   unshrinkable columns in a viewport too narrow for them and the footer
   overflowed the page. Measured: +86px of horizontal scroll at 1025, +68 at
   1050, +33 at 1100, clean again at 1150, with BOOK APPOINTMENT - the site's
   primary booking control - ending at x=1111 inside a 1025px viewport, i.e.
   cut off the screen. It affected all 15 pages at landscape-tablet width.

   max-content is right above 1150 (it makes the whitespace between columns
   even, which is why it was chosen); this just covers the band where it does
   not fit. Two rows of shrinkable columns rather than one impossible row. */
@media (min-width:1025px) and (max-width:1365px){
  .site-footer__inner{
    grid-template-columns:repeat(4,1fr);
    justify-content:stretch;
  }
}
/* Above the band, the five max-content tracks fit but only just, and this
   site's root font-size is fluid - so the tracks GROW with the viewport
   (measured 1263px of tracks at 1366 rising to 1303 at 1500) and a fixed
   breakpoint can only ever be right at the widths it was measured on. min()
   lets each track shrink below its max-content width if the row would not
   otherwise fit, which holds at every width rather than the tested ones. */
@media (min-width:1366px){
  .site-footer__inner > *{min-width:0}
}
/* Larger than the header's mark. In the header the logo shares a tight row
   with the nav and the CTA; in the footer it holds a column of its own beside
   four columns of text, and at the header's size it read as an afterthought
   rather than the block that anchors the row. */
/* ~22% larger, at the owner's request: 119->145px on a phone, 168->204 at
   1440. The source is 320px wide, so even the 1920 end sits at 0.75x and
   never upscales. No overflow at any width - the footer's own grid was the
   thing to watch and it has ample room. */
.site-footer__logo img{width:clamp(11rem,8rem + 5vw,16rem)}
/* The logo column now carries "Visit Us" beneath the lockup, so it needs the
   same heading rhythm the other columns get from their own h4. */

.site-footer__address{
  margin-top:0;
  /* One step up from the .9375rem the hours use: this is the practice's
     address sitting under the logo, not a secondary detail column. */
  font-size:1.0625rem;line-height:1.5;
}
/* "Visit Us" is its own column now, level with the other three headings, so
   it needs no extra top space - the logo column beside it holds only the
   lockup. */

/* 2. Operating hours. The tag arrives as ONE <p> of <br>-separated lines, so
   the day names are the only hook. Making <strong> a block gives the day its
   own line, but the <br> that FOLLOWS it in the source then adds a second
   break - which is what put the big gap between a day and its own hours,
   the reverse of what is wanted. Zero-height those trailing breaks and put
   the space on the day name's top margin instead: paragraph spacing BETWEEN
   days, plain line spacing between a day and its hours. */
.site-footer__hours{font-size:.9375rem;line-height:1.5}
/* {{opening_hours_html}} arrives as <table class="hours-table"> with one row
   per entry and two cells: the day label, then the hours. Wanted as a stacked
   pair - bold label above, hours beneath - with paragraph-sized space between
   entries rather than the tight line spacing a table gives by default.
   `display:block` on the row and cells drops the table layout while leaving
   the markup (and Rails) untouched. */
.site-footer__hours .hours-table,
.site-footer__hours tbody,
.site-footer__hours tr,
.site-footer__hours td{display:block;width:auto}
.site-footer__hours .hours-table{border-collapse:collapse}
/* The gap BETWEEN entries, not within one: margin-top on each row after the
   first keeps the label sitting tight above its own hours. */
.site-footer__hours tr + tr{margin-top:var(--space-2xs)}
.site-footer__hours td:first-child{
  font-weight:700;
  font-size:1.0625rem;
  line-height:1.35;
  color:var(--heading-brown);
}
.site-footer__hours td + td{line-height:1.5;white-space:nowrap}
.site-footer h2,.site-footer h4{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5rem;line-height:1.5;color:var(--primary);margin:0 0 .35rem;
}
/* "Visit Us" sits under the logo lockup rather than at the top of its own
   column, so it needs space above it. Must come AFTER the rule above: that
   one's `margin` shorthand resets margin-top to 0. */
.site-footer__logo h4{margin-top:var(--space-m)}
.site-footer .rich{color:var(--dark-brown)}
.footer-nav{list-style:none;padding:0;margin:0}
.footer-nav li{padding-block:0;margin-block-end:3px}
.footer-nav a{
  display:flex;align-items:center;gap:.25em;
  /* Live renders footer nav links in the HEADING face at 700, the same
     treatment as the top nav - not the body face at 400. */
  font-family:var(--font-head);
  font-size:1.1516rem;font-weight:700;line-height:1.25;
  /* Brown, matching the footer's own headings, rather than the primary
     green. That frees green to mean one thing down here - the current page. */
  color:var(--heading-brown);text-decoration:none;
}
.footer-nav a:hover{text-decoration:underline}
/* The current page is marked by GREEN TEXT alone. The links around it are
   brown, so the green reads clearly as "you are here" without a filled pill
   - which was heavy for a footer, where the nav is a reference list rather
   than the primary way to move around. */
.footer-nav a[aria-current="page"]{
  /* --primary, not --secondary. The lighter green measures 3.30:1 on the
     footer's cream - it clears the 3:1 large-text floor at 19px/700, but
     colour is now the ONLY thing marking the current page, so it should not
     be sitting on the minimum. --primary measures 7.13:1 and reads as
     clearly green against the 9.41:1 brown beside it. */
  color:var(--primary);
}
.footer-nav svg{width:1.15em;height:1.15em;fill:currentColor;flex:none}
.footer-contact{list-style:none;padding:0;margin:0}
.footer-contact li{padding-block:0;margin-block-end:6px}
/* 3. Phone and fax match in SIZE. The fax row is a <span>, not an <a>, so it
   was never picking up the link sizing and rendered visibly smaller - both
   are now styled through the shared list item.

   They must NOT match in COLOUR, though: a fax number is not dialable, and
   painting it the link colour promised a click that does nothing. The phone
   keeps the link treatment; the fax takes the body colour the rest of the
   footer uses. */
.footer-contact a,
.footer-contact > li > span{
  display:flex;align-items:center;gap: var(--space-2xs);
  font-size:1.125rem;font-weight:500;line-height:1.5;
  text-decoration:none;
}
/* --secondary-readable for the TEXT: this sits on the pale footer ground,
   where --secondary is 3.3:1. The icons below stay --secondary - they are
   decorative, so the contrast floor does not apply to them, and holding the
   original green keeps the phone and fax rows reading as one set. */
.footer-contact a{color:var(--secondary-readable)}
.footer-contact > li > span{color:var(--dark-brown);cursor:default}
.footer-contact svg{width:1.212rem;height:1.212rem;fill:currentColor;flex:none}
/* The handset points bottom-left as drawn; mirror it to point bottom-right. */
.footer-contact .icon-flip{transform:scaleX(-1)}
/* The fax ICON is green like the phone's, so the two rows read as one set.
   The NUMBER beside it stays --dark-brown (set above): a fax is not dialable,
   and painting the text the link colour promises a click that does nothing.
   Icon colour is set explicitly because the svg uses fill:currentColor, which
   would otherwise inherit the span's brown. */
.footer-contact > li > span > svg{fill:var(--secondary)}
.site-footer__cta{margin-top: var(--space-lg);align-self:flex-start;white-space:nowrap}

/* --primary, not --secondary: the pale --accent text on the lighter green
   measured 2.76:1, well under the 4.5:1 floor, and these are the privacy and
   terms links a patient may actually need. The darker green carries the same
   text comfortably. */
.site-footer__legal{background:var(--primary);padding-inline:var(--pad-x)}
.site-footer__legal-inner{
  max-width:var(--shell);margin-inline:auto;
  display:flex;flex-wrap:wrap;gap: var(--space-s) 6.25rem;
  align-items:center;justify-content:space-between;
  padding-block: var(--space-s);
}
.site-footer__legal p,.site-footer__legal a{
  /* max(), not a bare rem: .875rem on this site's FLUID root resolves to
     11.48px at 360 - the smallest type anywhere on the site, and these are
     the privacy/terms links a patient may actually need to find. The floor
     only binds on phones; at 1440 the rem is already 14.4px and wins. */
  font-size:max(.875rem,13px);line-height:1.5;color:var(--accent);margin:0;
}
.site-footer__legal p{font-weight:500}
/* A pipe between the two legal links, drawn rather than typed so it never
   ends up alone on a wrapped line. */
.site-footer__legal-links{display:flex;gap: var(--space-xs);flex-wrap:wrap;align-items:center}
.site-footer__legal-links > a + a{
  border-inline-start:1px solid currentColor;
  padding-inline-start: var(--space-xs);
}
/* 4. "Powered by" and the mark stay on ONE line - the logo was wrapping
   below the words. The inverted asset is the one to use on this dark bar:
   MEDI reads white, FLARE orange. */
.site-footer__powered{
  display:flex;align-items:center;gap:.5em;
  flex-wrap:nowrap;white-space:nowrap;
}
.site-footer__powered a{display:flex;align-items:center}
.mf-logo-light{width:auto;height:1.5rem;display:block}
.site-footer__legal a{text-decoration:none}
.site-footer__legal a:hover{text-decoration:underline}

/* ============================================================
   RESPONSIVE
   The source's own breakpoints: it collapses to a tablet layout
   at 1024 and a phone layout at 767. Matched here.
   ============================================================ */

/* Nav spacing is FLUID across the whole desktop range - deliberately not
   stepped. Two overlapping media queries used to set it, which produced a
   24 -> 7px cliff at 1300 and, worse, a NON-MONOTONIC 7 -> 15px step going
   narrower at ~1150: the menu grew more spacious as the viewport shrank.
   One clamp spanning 1024 -> 1600 removes both. Solved from the two
   endpoints (8px at 1024, 25px at 1600): 2.9514vw - 22.222px. */
@media (max-width:1300px){
  .site-header__inner{gap:var(--space-s-m)}
}

/* Between ~900 and ~1150 the nav still fits, but only once its own items
   are allowed to shrink. Without this the list keeps its intrinsic width
   and slides under the BOOK APPOINTMENT button. */
@media (max-width:1150px) and (min-width:901px){
  .site-nav__list{min-width:0;flex:1 1 auto;flex-wrap:nowrap;--nav-pad-rem:.16rem}
  .site-nav__list > li{min-width:0}
  /* The font-size STEP that used to live here is gone - the fluid clamp on
     .site-nav__list a covers this range continuously. Only the horizontal
     padding tightens, which buys width without a visible jump in type. */
  .site-nav__list a{
    --nav-pad-x:.4em;
    min-width:0;white-space:nowrap;
  }
  .site-header__inner{gap: var(--space-xs)}
  .sub-toggle{margin-inline:-.1rem var(--nav-pad-rem)}
}

/* The full nav stops fitting below ~900px, but the source's layout
   breakpoint is 767. Collapse ONLY the nav in that gap so 768-900 keeps
   the tablet layout without overflowing. */
@media (max-width:900px){
  .nav-toggle-btn{
    display:grid;place-items:center;
    width:max(2.75rem,44px);height:max(2.75rem,44px);flex:none;order:3;
    border:1px solid var(--rule);border-radius:.5rem;
    cursor:pointer;background:var(--light-bg-2);
    padding:0;
  }
  .nav-toggle-btn:focus-visible{outline:2px solid var(--primary);outline-offset:2px}
  .nav-toggle-btn > span,
  .nav-toggle-btn > span::before,
  .nav-toggle-btn > span::after{
    display:block;width:1.25rem;height:2px;background:var(--primary);
    border-radius:2px;content:"";position:relative;
    transition:transform .2s ease,opacity .2s ease;
  }
  .nav-toggle-btn > span::before{position:absolute;top:-6px}
  .nav-toggle-btn > span::after{position:absolute;top:6px}

  /* Bars fold into a cross when open. */
  .site-header.nav-open .nav-toggle-btn > span{background:transparent}
  .site-header.nav-open .nav-toggle-btn > span::before{transform:translateY(6px) rotate(45deg)}
  .site-header.nav-open .nav-toggle-btn > span::after{transform:translateY(-6px) rotate(-45deg)}

  .site-header__inner{flex-wrap:wrap;justify-content:space-between}
  .site-header__logo{order:1}
  .site-header .btn{order:2;margin-inline-start:auto}
  .site-nav{
    order:4;flex:1 1 100%;
    display:none;flex-direction:column;align-items:stretch;gap: var(--space-xs);
  }
  .site-header.nav-open .site-nav{display:flex}

  /* Collapsed nav: the submenu becomes an inline accordion rather than an
     overlay — there is no room for a floating panel, and hover does not
     exist here, so it opens only via the caret button. */
  .site-nav__sub{
    position:static;min-width:0;
    opacity:1;visibility:visible;transform:none;
    display:none;box-shadow:none;
    /* NOT transparent. When the parent is .is-ancestor - i.e. you are ON one
       of its child pages - the <li> is filled dark green, and a transparent
       submenu let that green show through behind green link text: measured
       1.0:1 contrast, completely invisible. The panel needs its own ground
       here, exactly as the floating desktop version has - and the same light
       green, so the two match. */
    background:var(--accent);border:0;
    padding:.15rem 0 .35rem .9rem;
    margin-inline-start:.6em;
    border-inline-start:2px solid var(--rule);
    border-radius:0;
  }
  li.has-sub:hover > .site-nav__sub,
  li.has-sub:focus-within > .site-nav__sub{display:none}
  li.has-sub.sub-open > .site-nav__sub{display:block}

  /* Parent row: link on the left, caret button on the right, both ≥44px. */
  .site-nav__list > li.has-sub{
    display:flex;flex-wrap:wrap;align-items:center;
  }
  .site-nav__list > li.has-sub > a{flex:1 1 auto}
  .sub-toggle{
    width:max(2.75rem,44px);height:max(2.75rem,44px);margin:0;
    border:0;background:transparent;
    color:inherit;
    border-radius:var(--radius-sm,.5rem);
  }
  /* Pressed state, not a permanent chrome: a faint wash of the row's own text
     colour, so it works on cream and on the green pill without a second rule. */
  .sub-toggle:hover,
  .sub-toggle:focus-visible{
    background:color-mix(in oklch, currentColor 12%, transparent);
  }
  li.has-sub.sub-open > .sub-toggle{
    background:color-mix(in oklch, currentColor 12%, transparent);
  }
  .sub-toggle svg{width:1em;height:1em}
  .site-nav__sub{flex:1 1 100%}
  .site-nav__list{flex-direction:column;flex-wrap:wrap;align-items:stretch}
  .site-nav__list > li{display:block}
  .site-nav__list > li:not(:last-child)::after{
    width:auto;height:1px;display:block;margin-inline:.6em;
  }
  /* min-height for the 44px touch floor: em padding on the fluid root left
     these at 40.3px. The caret buttons beside them are already exactly 44,
     so the floor was intended and the links simply missed it. */
  .site-nav__list a{margin-inline:0;padding:.6em .6em;
    display:flex;align-items:center;min-height:max(2.75rem,44px)}

  /* ACTIVE ROW, mobile. A rounded pill inset from the drawer edges rather than
     the full-bleed bar it was: edge to edge, the green read as a section header
     or a divider rather than as "this row is selected", which is why it looked
     like a different component from the rows around it. Inset and rounded, it
     spans the same width as every other row - an inset one read as
     misaligned against its neighbours rather than as deliberate. The radius is
     .25rem - what every nav link and the hover state already use - rather
     than a full pill: the drawer holds a real pill-shaped button in BOOK
     APPOINTMENT, and matching it made a selected row look like a control.
     The <li> carries the fill because the row is a flex container holding both
     the link and its caret - painting the link alone would leave the caret
     outside the highlight. */
  .site-nav__list > li:has(> a[aria-current="page"]),
  .site-nav__list > li.has-sub:has(> a[aria-current="page"]),
  .site-nav__list > li.has-sub.is-ancestor{
    position:relative;
    background:transparent;
  }
  .site-nav__list > li:has(> a[aria-current="page"])::before,
  .site-nav__list > li.has-sub.is-ancestor::before{
    content:"";
    position:absolute;
    inset-inline:0;
    top:0;
    /* The ROW's height, not the li's: once the submenu is open the li also
       wraps the expanded panel, and filling that painted a green strip down
       the left of every submenu item. */
    height:max(2.75rem,44px);
    background:var(--primary);
    border-radius:.25rem;
    z-index:0;
  }
  /* Above the fill. The <a> cannot carry the fill itself - it stops short of
     the caret, which is its flex sibling, so a submenu row would highlight
     narrower than a plain one (measured 311px against 335px). */
  .site-nav__list > li:has(> a[aria-current="page"]) > a,
  .site-nav__list > li.has-sub.is-ancestor > a,
  .site-nav__list > li:has(> a[aria-current="page"]) > .sub-toggle,
  .site-nav__list > li.has-sub.is-ancestor > .sub-toggle{
    position:relative;
    z-index:1;
    background:transparent;
  }
  /* The separator belongs between plain rows; under a filled pill it reads as
     an underline on the fill. */
  .site-nav__list > li:has(> a[aria-current="page"])::after,
  .site-nav__list > li.has-sub.is-ancestor::after{display:none}
}

/* ---- tablet: everything that must also hold on a phone ---- */
@media (max-width:1024px){
  /* the source stacks the team panel ABOVE the doctor grid here */
  .team__inner{flex-direction:column}
  .team__panel,.team__grid{flex:1 1 auto;width:100%}
  .promises__inner{grid-template-columns:repeat(2,1fr)}
  /* Narrow columns: let the headings wrap naturally rather than at the
     desktop break points. */
  /* The <br> splits these headings in two on desktop. Suppressing it here is
     right, but `display:none` removes the break WITHOUT leaving a space, so
     the words ran together - "ExperiencedIndependent GPs", "House Callsfor
     Regular Patients".

     Zero-width + a generated space is the reliable way to do this: a <br> is
     a replaced element, so `display:inline` alone does not make it render a
     space. This keeps the heading on one wrapped run with real word breaks. */
  .promise h2 br{
    display:inline;
    width:0;
  }
  .promise h2 br::before{content:" ";white-space:pre}
  .promise:nth-child(odd){border-left:0}
  .promise:nth-child(n+3){border-top:1px solid var(--rule)}

  .specials{grid-template-columns:repeat(2,1fr)}
  .fees__grid{grid-template-columns:1fr;gap: var(--space-m)}
  .more__spacer{flex:1 1 100%;min-height:30rem}
  .site-footer__inner{grid-template-columns:repeat(2,1fr);gap: var(--space-l)}
  .site-footer__logo{grid-column:1/-1}
  .booking__right{gap: var(--space-m)}
  .booking__col{max-width:none}
}

/* ---- tablet: the source keeps the hero cards INSIDE the photo in a
   narrow right-hand column and lets the PHOTO grow to fit them, rather
   than shrinking the photo. Reproduced. ---- */
@media (max-width:1200px) and (min-width:768px){
  /* the photo keeps a tall portrait-ish block here (1287px at 768 in the
     source) rather than shrinking with the viewport */
  /* margin-right ALONE was the asymmetry: the photo kept a right inset and
     no left one, so it sat off-centre by 26-36px through this whole range.
     margin-inline gives it the same gap on both sides.

     The height was min(168vw,84rem) - 1323px at 1200, against 1080px just one
     pixel wider. That step is what spread the cards so far apart. Ramping the
     ratio instead keeps the block taller than the desktop banner (the cards
     stack inside it here) without the cliff: ~1.05x the width at 1200 down to
     ~1.5x at 768, which is a continuous curve through the range. */
  .hero__photo{
    aspect-ratio:auto;align-items:stretch;margin-inline:var(--pad-x);
    min-height:clamp(34rem, 118vw - 76rem, 52rem);
  }
  .hero__cards{width:min(24%,12rem);margin-right:0;align-content:space-between}
  .info-card{padding: var(--space-m) 1.25rem}
  .info-card h2{font-size:1.35rem}
  .info-card p,.info-card .rich{font-size:1rem}
}

/* ---- phone: nav becomes a CSS-checkbox disclosure (no JS) ---- */
@media (max-width:767px){
  .site-header__inner{
    flex-wrap:wrap;gap: var(--space-xs);
    justify-content:space-between;align-items:center;
  }
  .nav-toggle-label{
    display:grid;place-items:center;
    width:max(2.75rem,44px);height:max(2.75rem,44px);flex:none;
    border:1px solid var(--rule);border-radius:.5rem;
    cursor:pointer;background:var(--light-bg-2);
    order:2;
  }
  .nav-toggle-label span,
  .nav-toggle-label span::before,
  .nav-toggle-label span::after{
    display:block;width:1.25rem;height:2px;background:var(--primary);
    border-radius:2px;content:"";position:relative;
  }
  .nav-toggle-label span::before{position:absolute;top:-6px}
  .nav-toggle-label span::after{position:absolute;top:6px}

  .site-nav{
    order:4;flex:1 1 100%;
    display:none;flex-direction:column;align-items:stretch;gap: var(--space-xs);
  }
  .nav-toggle:checked ~ .site-nav{display:flex}
  .site-nav__list{flex-direction:column;flex-wrap:wrap;align-items:stretch}
  .site-nav__list > li{display:block}
  .site-nav__list > li:not(:last-child)::after{
    width:auto;height:1px;display:block;margin-inline:.6em;
  }
  /* min-height for the 44px touch floor: em padding on the fluid root left
     these at 40.3px. The caret buttons beside them are already exactly 44,
     so the floor was intended and the links simply missed it. */
  .site-nav__list a{margin-inline:0;padding:.6em .6em;
    display:flex;align-items:center;min-height:max(2.75rem,44px)}
  /* Two rows on a phone: the LOGO alone on row 1, then the booking button and
     the hamburger together on row 2. The logo takes the full row (flex-basis
     100%) to force the wrap; the other two then share the next line, with the
     button taking the leftover space and the 44px hamburger held at its own
     size beside it. */
  .site-header__logo{order:1;flex:1 1 100%;display:flex;justify-content:center}
  /* The button sizes to its own text rather than filling the row, and the two
     controls are centred together beneath the logo. flex-wrap on the parent
     puts them on their own line; justify-content there is space-between for
     the desktop row, so the pair is centred by giving each an auto margin
     that meets in the middle - the button's margin-left and the toggle's
     margin-right push them together at the centre. */
  .site-header .btn{
    order:2;
    flex:0 0 auto;
    margin:var(--space-2xs) 0 0 0;
  }
  .nav-toggle-btn{
    order:3;
    flex:0 0 auto;
    margin:var(--space-2xs) 0 0 var(--space-2xs);
  }
  /* Centre the CTA + toggle pair on their own row. justify-content is enough
     once the auto margins are gone - an auto margin eats the free space on its
     side, so the two cannot both apply. */
  .site-header__inner{justify-content:center}
  /* A larger mark on a phone, where it is the only branding on screen and has
     a whole row to itself. 11rem against the 8.5rem floor the shared clamp
     gives here - still 0.45x of the 320px source, so no upscaling. */
  .site-header__logo img{width:11rem}

  /* hero: on a phone the photo stops being a container for the cards and
     becomes a BLOCK ALONGSIDE them - four stacked blocks of similar height
     (photo, address, contact, hours) rather than three cards floating in an
     866px photo.

     The source (and every wider breakpoint here) overlays the cards on the
     photo. That works while the photo is wide, but at 390px the column is so
     tall that the building sat almost entirely behind the cards with big
     empty gaps between them - the photo was doing no work. Taking the cards
     out of the photo lets it be seen and gives the hero an even rhythm.

     Height is matched to a card rather than fixed: 146px is what the address
     and contact cards measure at 390, so the photo reads as one of the set. */
  /* The cards are nested INSIDE .hero__photo in the markup (that is what makes
     the desktop overlay work), so they cannot simply be placed after it. The
     photo becomes a GRID whose first row is the image and whose second row is
     the card stack: `background` draws only on the first row's height, and the
     cards flow beneath it in normal document order. No markup change, so every
     wider breakpoint keeps its overlay untouched.

     Padding goes back to 0 so the card stack is flush with the photo's inline
     edges - all four blocks then share one left edge. */
  .hero__photo{
    aspect-ratio:auto;
    display:grid;
    grid-template-columns:1fr;      /* ONE column: the desktop rule justifies
                                       content to the end, which left both rows
                                       pinned to the right half at 165px wide */
    grid-template-rows:auto auto;
    justify-content:stretch;
    background-image:none;          /* moved to the ::before row below */
    padding:0;margin-inline:var(--pad-x);
    min-height:0;
  }
  /* Row 1: the photo itself, at one card's height. px not rem - this site's
     root is FLUID, so a rem value drifted to 122px against 146px cards. */
  .hero__photo::before{
    content:"";
    grid-row:1;
    /* 16:10 rather than a fixed height: the row started at 146px to match a
       card exactly, but at 355px wide that is a 2.43:1 letterbox - too squat
       to show a building. A ratio holds its proportions at 360 and 480 too,
       where a fixed height would go squat on a narrow phone and tall on a
       wide one. ~222px at 390. */
    aspect-ratio:16 / 10;
    background-image:url("../images/practice-front.webp");
    background-size:cover;background-position:center;
    border-radius:var(--radius-md);
  }
  /* Row 2: the three info cards, stacked below the photo rather than on it. */
  .hero__cards{
    grid-row:2;
    /* width:auto is not enough - the base rule sets an explicit
       clamp(17.5rem, 18.5%, 19.5rem), which still resolved to 165px here.
       100% overrides it against the photo's own width. */
    width:100%;margin-right:0;margin-inline:0;
    margin-top: var(--space-sm);
    grid-template-columns:1fr;gap: var(--space-sm);
    align-content:start;
  }
  .info-card{padding: var(--space-sm)}

  .hero__title{font-size:3.03rem}
  .hero__subtitle{font-size:2.27rem}

  .services__grid{grid-template-columns:1fr}
  .services__col + .services__col{border-left:0;padding-left:0}
  /* .services__col is a flex row and :first-child is justify-content:flex-end,
     which pushes its list against the divider on desktop. Stacked, that same
     rule shoved list one to the RIGHT while list two sat flush left, so the
     two halves of what reads as a single list did not share a left edge.
     Both flush left here. */
  .services__col:first-child{justify-content:flex-start}

  /* The section head is centred, which suits a wide band above two columns.
     On a phone the ticked items beneath it are left-aligned, so a centred
     eyebrow, title and lede sat on a different axis to the content they
     introduce. Left-align the whole head to match. */
  .services .section-head{text-align:left;align-items:flex-start}
  .services .eyebrow-row--center{justify-content:flex-start}

  .promises__inner{grid-template-columns:1fr}
  /* Measured 0px between cards here - they butt straight up against each
     other with only the hairline divider between, which is what made this
     band read as having no spacing at all.

     The extra room goes in as PADDING, not a grid gap: the divider is a
     border on .promise itself, so a gap would leave the rule floating in
     empty space away from the content it separates. --space-2lg is ~40px a
     side at 390 against the ~30px this had. */
  .promise{padding: var(--space-2lg) 1.515rem}
  .promise + .promise{border-left:0;border-top:1px solid var(--rule)}
  .promise:nth-child(n+3){border-top:1px solid var(--rule)}

  .team__grid{grid-template-columns:1fr}
  .team__names{grid-template-columns:1fr;gap:0}
  .doctor{grid-template-columns:6.5rem 1fr;gap: var(--space-s)}

  .specials{grid-template-columns:1fr;gap: var(--space-xl)}
  .panel{padding: var(--space-2lg) 1.515rem 3.03rem;gap: var(--space-2lg)}
  .panel__head-right{align-items:flex-start;text-align:left}
  .panel__head{align-items:flex-start;gap: var(--space-sm)}

  .fee-card__meta{grid-template-columns:1fr;gap:.25rem;padding-left:0}
  .price-row{flex-wrap:wrap}
  .price-row__leader{min-width:1rem}

  .booking__body{gap: var(--space-m)}
  .booking__col{max-width:none}
  .booking__right{flex-direction:column}
  .booking__photo{min-height:14rem}
  .more__spacer{flex:1 1 100%;min-height:37.8rem;padding: var(--space-lg)}

  /* Phone footer is CENTRED, per the reference screenshot: the columns are
     stacked one per row here, and a left-aligned single column on a narrow
     screen leaves the whole footer hugging one edge. Centring is only right
     BECAUSE it is one column - the desktop grid stays left-aligned. */
  .site-footer__inner{
    grid-template-columns:1fr;gap: var(--space-l);
    text-align:center;justify-items:center;
  }
  /* The address and hours blocks carry their own alignment; without this they
     stay left inside a centred column. */
  .site-footer__address,
  .site-footer__hours,
  .site-footer .rich,
  .footer-nav{text-align:center}
  /* Two columns on a phone. Seven links stacked one-per-row made the footer
     tall enough that Contact Us fell well below the fold; two columns halve
     that without shrinking the 44px tap targets. Grid rather than
     column-count so each cell is a real box that can hold its own height. */
  /* ONE centred column, matching every other footer block. Two columns were
     tried first and the problem was never the alignment WITHIN them - it was
     that a left-aligned two-column block sat inside a footer whose Visit Us,
     Operating Hours and Contact Us sections are all centred, and seven items
     over two columns orphaned "Contact" alone on the last row. One column
     reads as part of the same footer. */
  .footer-nav{
    list-style:none;padding-left:0;
    display:grid;grid-template-columns:1fr;
  }
  /* .footer-nav a is display:flex, so text-align on the ancestor does not
     move its content - the labels sat left inside 103px-wide boxes that were
     themselves centred, which reads as a ragged left edge. Centre the flex
     line itself.

     The height also has to come up: these measured 19px, well under the 44px
     touch-target floor, and a footer nav is exactly where a thumb goes. */
  .footer-nav a{
    /* Centred again now the list is a single full-width column - the links
       centre on the footer's own axis, like the address and hours above. */
    justify-content:center;
    /* 36px, not 44. The tap target was 44px around 20px of text, so each link
       carried 24px of its own padding and consecutive LABELS sat 27px apart -
       the boxes were already touching (3px), so the space a reader saw was
       entirely inside the links. 36px keeps a comfortable target on a single
       full-width row (the whole row is tappable, so the effective target is
       355px wide) while closing the visual gap to ~19px.

       px, not rem: this site's root font-size is FLUID, so 2.25rem resolved
       to 30px here rather than the 36 intended and over-tightened the list
       to a 13px label gap. */
    min-height:36px;
  }
  /* Contact rows are icon + text flex lines, so they need centring on the
     flex axis rather than text-align. The class is .footer-contact - the
     BEM-looking .site-footer__contact does not exist in the markup. */
  .footer-contact{list-style:none;padding-left:0}
  .footer-contact li,
  .footer-contact li > a,
  .footer-contact li > span{justify-content:center}
  /* align-self:flex-start on the base rule pins the button left even inside a
     centred grid cell - justify-items above cannot override an explicit
     align-self on the child. */
  .site-footer__cta{margin-top: var(--space-xs);align-self:center}
  .site-footer__legal-inner{
    flex-direction:column;align-items:center;gap: var(--space-xs);
    text-align:center;
  }
}

/* ---- per-band phone padding, measured from the source at 390.
   These do not scale uniformly with the viewport: the services band
   GROWS from 80px to 120px while the team band SHRINKS from 120px to
   40px, so a single universal clamp would be wrong for both. ---- */
@media (max-width:767px){
  .hero__inner{padding-block: var(--space-2lg)}          /* 80/80 (was 80/120) */
  .urgent__inner{padding-block: var(--space-lg)}        /* 80  -> 40  */
  .services__inner{padding-block: var(--space-3xl)}      /* 80  -> 120 */
  .promises__inner{padding-block: var(--space-xs)}       /* 60  -> 10  */
  .team__inner{padding-block: var(--space-lg)}          /* 120 -> 40  */
  .fees__inner{padding-block: var(--space-2xl)}          /* 120 -> 60  */
  .more__inner{padding-block: var(--space-lg)}          /* 80  -> 40  */
  .assurance__inner{padding-block: var(--space-2lg);gap: var(--space-xl)}  /* 80 -> 80, gap 50 */
  /* 40px here too. This phone override was a fluid pair resolving to 21px top
     against 27px bottom, so the mobile footer kept the asymmetry the base
     rule just fixed. */
  .site-footer__inner{padding-block:40px}
  .site-footer__legal-inner{padding-block: var(--space-xs)}
  .team__inner{gap: var(--space-2lg)}                    /* gap 80 at 390 */
  .urgent__inner{gap: var(--space-sm)}
}

@media (max-width:420px){
  .doctor{grid-template-columns:1fr;gap: var(--space-xs)}
  .doctor__photo{width:6.5rem}
  .rich ul{padding-left: var(--space-sm)}
}

@media (prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important}
}

/* ============================================================
   INNER PAGES

   Shared shell for the 15 non-homepage pages. Everything here is
   fluid: widths are percentages/fr/ch, heights come from content,
   and the only clamps are on band padding — same rule the homepage
   follows, so the pages reflow rather than sit at a pinned size.
   ============================================================ */

/* ---- page hero: the title band every inner page opens with ---- */
.page-hero{
  background:var(--accent);
  padding-block:var(--space-xl-2xl);
  padding-inline:var(--pad-x);
  text-align:center;
}
.page-hero__inner{max-width:var(--shell);margin-inline:auto}
.page-hero__mark{
  width:2.75rem;height:2.75rem;margin:0 auto 1rem;display:block;
}
.page-hero h1{
  font-family:var(--font-head);font-weight:700;
  font-size:2.75rem;line-height:1.2;color:var(--primary);
  margin:0 0 .5rem;
}
.page-hero p{
  font-size:1.25rem;line-height:1.5;color:var(--text);
  margin:0 auto;max-width:60ch;
}
.page-hero .btn{margin-top: var(--space-m)}

/* ---- prose: the workhorse for policy//info pages ---- */
.prose{padding-block:var(--space-xl-2xl)}
.prose__inner{max-width:var(--shell);margin-inline:auto}
.prose__body{max-width:74ch}
.prose h2{
  font-family:var(--font-head);font-weight:700;
  font-size:2.0001rem;line-height:1.25;color:var(--primary);
  margin: var(--space-m) 0 var(--space-xs);
}
.prose h2:first-child{margin-top:0}
.prose h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5152rem;line-height:1.3;color:var(--heading-brown);
  margin: var(--space-m) 0 .6rem;
}
.prose h4{
  font-family:var(--font-head);font-weight:600;
  font-size:1.2728rem;line-height:1.4;color:var(--heading-brown);
  margin: var(--space-sm) 0 .5rem;
}
.prose p{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 1em}
.prose ul,.prose ol{
  font-size:1.1516rem;line-height:1.6;color:var(--text);
  margin:0 0 1.25em;padding-left:1.5em;
}
.prose li{margin-bottom:.45em}
.prose li:last-child{margin-bottom:0}
/* A .btn inside .prose is still a button: exclude it, or this later rule
   wins on source order and paints green text on the green fill. */
.prose a:not(.btn){color:var(--primary);font-weight:600}
.prose a:not(.btn):hover{text-decoration:none}
.prose strong{color:var(--heading-brown);font-weight:700}

/* ---- card grid: repeatable "topic card" rows ---- */
.cards{
  display:grid;gap: var(--space-m);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,17rem),1fr));
}
.card{
  background:var(--bg-white);
  border:1px solid var(--rule);border-radius:var(--radius-md);
  padding:var(--space-m);
  display:flex;flex-direction:column;gap: var(--space-xs);
}
.card h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.375rem;line-height:1.3;color:var(--primary);margin:0;
}
.card p{font-size:1.0625rem;line-height:1.55;color:var(--text);margin:0}
.card ul{
  font-size:1.0625rem;line-height:1.55;color:var(--text);
  margin:0;padding-left:1.35em;
}
.card li{margin-bottom:.4em}
.card li:last-child{margin-bottom:0}
.card .btn{margin-top:auto;align-self:flex-start}
.card__photo{
  width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  border-radius:var(--radius-md);margin:-.25rem 0 .25rem;
}

/* ---- media row: text beside an image, stacking on narrow ---- */
.media-row{
  display:grid;gap:var(--space-m-l);
  grid-template-columns:1fr 1fr;align-items:center;
}
.media-row--narrow{grid-template-columns:1.25fr 1fr}
.media-row__media img{
  width:100%;height:auto;border-radius:var(--radius-md);
}
.media-row + .media-row{margin-top:var(--space-l-xl)}

/* ---- definition rows: label/value pairs (fees, contacts) ---- */
.deflist{margin:0;display:grid;gap: var(--space-2xs)}
.deflist__row{
  display:flex;flex-wrap:wrap;align-items:baseline;gap: var(--space-3xs) .75rem;
}
.deflist__row dt{
  flex:1 1 12rem;min-width:0;
  font-size:1.0625rem;line-height:1.5;color:var(--text);
}
.deflist__row dd{
  margin:0;flex:none;
  font-family:var(--font-num);font-weight:700;
  font-size:1.125rem;color:var(--primary);
}
/* The leader rule that ties a label to its price. Its own flex item so it
   absorbs the slack instead of the label stretching. */
.deflist__lead{
  flex:1 1 1.5rem;min-width:1.5rem;height:1px;
  align-self:center;
  background:var(--rule);
}

/* ---- contact strip ---- */
.contact-grid{
  display:grid;gap: var(--space-m);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,15rem),1fr));
}

/* ---- downloads list ---- */
.downloads{list-style:none;margin:0;padding:0;display:grid;gap: var(--space-xs)}
.downloads a{
  display:flex;align-items:center;gap: var(--space-xs);
  background:var(--bg-white);
  border:1px solid var(--rule);border-radius:var(--radius-md);
  padding: var(--space-xs) 1.25rem;
  font-family:var(--font-head);font-weight:600;font-size:1.0625rem;
  color:var(--primary);text-decoration:none;
  transition:background .18s ease,border-color .18s ease;
}
.downloads a:hover{background:var(--accent);border-color:var(--secondary)}
.downloads svg{width:1.25em;height:1.25em;fill:currentColor;flex:none}

/* ---- practitioner grid ---- */
.prac-grid{
  display:grid;gap: var(--space-m);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));
}
.prac{
  background:var(--bg-white);
  border:1px solid var(--rule);border-radius:var(--radius-md);
  padding:var(--space-m);
  display:flex;flex-direction:column;gap: var(--space-xs);
}
.prac__photo{
  width:8.5rem;height:8.5rem;flex:none;
  border-radius:var(--radius-round);object-fit:cover;
}
.prac h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.25rem;line-height:1.3;color:var(--primary);margin:0;
}
.prac__role{
  font-size:1rem;line-height:1.45;color:var(--secondary);
  font-weight:600;margin:0;
}
.prac p{font-size:1.0625rem;line-height:1.55;color:var(--text);margin:0}

/* ---- embed slot (Connect widget / booking iframe) ---- */
.embed-slot{min-height:22rem}

/* ---- inner-page reflow ---- */
@media (max-width:1024px){
  .media-row,.media-row--narrow{grid-template-columns:1fr}
  .page-hero h1{font-size:2.375rem}
}
@media (max-width:767px){
  .page-hero h1{font-size:2rem}
  .page-hero p{font-size:1.125rem}
  .prose h2{font-size:1.75rem}
  .prose h3{font-size:1.375rem}
  .deflist__row dt{flex:1 1 100%}
  .deflist__lead{display:none}
  .deflist__row dd{margin-left:0}
  .prac{align-items:center;text-align:center}
}

/* ============================================================
   SCROLL ENTRANCE

   Native scroll-driven animation — no library, no
   IntersectionObserver, no JS, no polyfill. Copied from the v2
   template so this site behaves like every other one.

   The whole block sits inside prefers-reduced-motion: a reveal is
   `opacity: 0` until something animates it, so scoping the rule
   means reduced-motion users simply get the content rather than
   staring at an invisible page.

   Safari (<26) and Firefox (<157) do not support animation-timeline,
   so content there is simply visible. That is the correct failure
   for a practice site: no entrance effect always beats content that
   never arrives. Deliberately NOT polyfilled.
   ============================================================ */

@media (prefers-reduced-motion: no-preference) {
  .reveal {
    animation: reveal linear both;
    animation-timeline: view();
    /* entry 10% -> cover 38%: starts a little after the element's top edge
       clears the viewport, and finishes later than the old cover 30%, so the
       fade is gentler and the movement is mostly over before the reader's eye
       arrives. Scroll-driven animation has no duration of its own - the range
       IS the timing - so "slower" means a longer range, not a longer time. */
    /* WHERE ON SCREEN the animation plays matters more than how long it runs,
       and this is what two earlier attempts got wrong.

       `entry` is measured from the moment the element's top edge touches the
       BOTTOM of the viewport. So `entry 5% -> entry 95%` ran the whole reveal
       while the element was still in the bottom third of the screen: measured,
       it began at 0% up the screen and was fully opaque by 35% up - settled
       long before it reached where anyone is actually reading. The animation
       was firing correctly every time and was simply happening off where the
       eye is, which is why it read as "no animation" no matter how much the
       distance or duration changed.

       `cover` is measured across the element's whole pass through the
       viewport, so starting at cover 18% holds the element hidden until it is
       properly on screen, and finishing at cover 55% lands it just past the
       middle - the reveal now plays across the centre of the screen. */
    animation-range: cover 18% cover 55%;
  }

  /* Neighbours in a row arrive one after another rather than all together.
     This has to be done by shifting each one's RANGE, not with
     animation-delay: a scroll-driven animation has no time axis, so a delay
     in ms is silently ignored (verified - all four promise columns reported
     identical opacity at every scroll position with delays applied).
     Starting later and ending later moves the whole fade down the scroll.

     The steps shorten as they go because even intervals read as mechanical;
     an accelerating stagger reads as one movement settling. Small on purpose:
     the fourth column is only 6% of the range behind the first. */
  .promises__inner > .promise:nth-child(2){animation-range:cover 21% cover 58%}
  .promises__inner > .promise:nth-child(3){animation-range:cover 23.5% cover 60.5%}
  .promises__inner > .promise:nth-child(4){animation-range:cover 25% cover 62%}

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

/* Vertical separation between a prose intro and the grid beneath it inside
   one band. A named step rather than the inline margin-top values that were
   otherwise needed at four call sites. */
.u-stack-top{margin-top:var(--space-m-l)}

/* A grid, list or rich block that follows prose text inside the same band
   needs separation from it. Expressed structurally rather than as a margin
   repeated at each call site, so new pages get it for free. */
.prose__body > .cards,
.prose__body > .prac-grid,
.prose__body > .contact-grid,
.prose__body > .deflist,
.prose__body > .downloads,
.prose__body > .media-row,
.prose__body > .rich,
.prose__inner > .cards,
.prose__inner > .prac-grid,
.prose__inner > .contact-grid,
.prose__inner > .downloads,
.prose__inner > .media-row{
  margin-top:var(--space-m-l);
}
/* ...but not when it is the first thing in the band. */
.prose__body > :first-child,
.prose__inner > :first-child{margin-top:0}

/* ============================================================
   LIVE-MATCHING PAGE PATTERNS

   Added when the inner pages were re-transcribed from the LIVE
   DOM rather than from the export's content outline. The export
   is still the source for assets and copy; the live site is the
   authority on layout.

   Two things drove almost every correction:
     1. The export's hero photography is CSS background-image on a
        container, not an <img>. Reading only <img> tags turned
        photographic heroes into flat colour bands.
     2. Every page opens with a THREE-PART heading - a small caps
        eyebrow, a large display title, then the lede - which had
        been collapsed into a single H1.
   ============================================================ */

/* ---- Photographic hero: full-bleed image with a card over it ----
   The live pages put the photo on the container as a background and
   float an offset content card on top. Height comes from the card's
   own content plus padding, never a pinned pixel value, so the band
   grows with its text instead of clipping it. */
.phero{
  position:relative;
  background-color:var(--accent);
  background-size:cover;
  background-position:50% 50%;
  background-repeat:no-repeat;
  display:flex;align-items:center;
  padding-block:var(--space-xl-2xl);
  padding-inline:var(--pad-x);
}
/* A scrim so white/dark card text keeps contrast over any photo. */
.phero::before{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,
    oklch(0.2243 0.0412 56 / .34) 0%,
    oklch(0.2243 0.0412 56 / .10) 55%,
    oklch(0.2243 0.0412 56 / .28) 100%);
}
.phero__inner{
  position:relative;
  max-width:var(--shell);margin-inline:auto;width:100%;
  display:flex;
}
/* Which side the card sits on. The live site uses both. */
.phero--right .phero__inner{justify-content:flex-end}
.phero--left  .phero__inner{justify-content:flex-start}
.phero--centre .phero__inner{justify-content:center}

.phero__card{
  /* Live: the hero card sits on the kit ACCENT (#E3E8D3), a pale green -
     not the cream Light BG 2. */
  background:var(--accent);
  border-radius:var(--radius-lg);
  padding:var(--space-m-l);
  max-width:min(100%,34rem);
  box-shadow:0 18px 46px -22px oklch(0.2243 0.0412 56 / .5);
}
.phero--wide .phero__card{max-width:min(100%,46rem)}

/* ---- The three-part heading -----------------------------------
   eyebrow (small, caps-ish, brand-dark) / display title / lede.
   Sizes are plain rem on the fluid root, per this site's type rule -
   never clamp(), which would fight the fluid root. */
.eyebrow{
  /* Live renders the eyebrow in MULISH (the body face) at 21px w700 - a caps
     label in the body family, not the heading face. */
  font-family:var(--font-body);font-weight:700;
  font-size:1.2728rem;line-height:1.3;
  color:var(--dark-brown);
  margin:0 0 .55rem;
}
.display-title{
  font-family:var(--font-head);font-weight:700;
  font-size:3.5153rem;line-height:1.12;
  color:var(--heading-brown);
  margin:0 0 1rem;
}
.display-title--sm{font-size:2.4850rem}   /* the service pages sit here: live 41px */
.display-title--md{font-size:2.9698rem}   /* fees: live 49px */
.lede{
  font-size:1.1516rem;line-height:1.55;
  color:var(--text);margin:0;
  max-width:52ch;
}
.phero__card .btn,.plain-hero .btn{margin-top: var(--space-m)}

/* A heading triple with no photo behind it. */
/* The live service pages put the heading triple in a CARD on the kit accent
   green, sitting inside a cream band - not a full-bleed accent band. Making
   the band itself accent is what produced a 1440px-wide "card" at x=0.
   The card's own width/position is ours by choice and is approved; only the
   colour relationship (cream band, accent card) follows the live site. */
.plain-hero{
  background:var(--light-bg-2);
  padding-block:var(--space-xl-2xl);
  padding-inline:var(--pad-x);
}
.plain-hero__inner{
  /* Live: the card is 873px wide sitting at x=40 in the container, on the kit
     accent, inside a cream band. max-width rather than width so it still
     reflows below that; margin-inline-start holds the 40px inset and the card
     stays left-aligned rather than centred. */
  max-width:min(100%,52.9117rem);
  margin-inline:0 auto;
  background:var(--accent);
  border-radius:var(--radius-lg);
  padding:var(--space-m-l);
}
@media (max-width:767px){
  .plain-hero__inner{margin-inline:0}
}
/* The lede inside the card keeps a readable measure rather than running the
   card's full width. */
.plain-hero__inner .lede{max-width:52ch}
.plain-hero--centre{text-align:center}
.plain-hero--centre .lede{margin-inline:auto}

/* ---- Two-column band: text one side, media the other ----------
   The live pages alternate these down the page. Percentage columns
   and aspect-ratio media, so the band reflows rather than pinning. */
.split{
  display:grid;gap:var(--space-m-l);
  grid-template-columns:1fr 1fr;align-items:center;
}
.split--text-wide{grid-template-columns:1.35fr 1fr}
.split--media-wide{grid-template-columns:1fr 1.35fr}
.split--flip .split__media{order:-1}
.split__media img{width:100%;height:auto;border-radius:var(--radius-md);display:block}
/* A media panel that is a background rather than an <img>. */
.split__media--bg{
  border-radius:var(--radius-md);
  background-size:cover;background-position:50% 50%;
  min-height:clamp(14rem,10rem + 16vw,26rem);
}
.split h2{
  font-family:var(--font-head);font-weight:700;
  font-size:2.0001rem;line-height:1.25;color:var(--heading-brown);margin:0 0 .75rem;
}
.split h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5152rem;line-height:1.5;color:var(--heading-brown);margin: var(--space-s) 0 .5rem;
}
.split p{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 1em}
.split p:last-child{margin-bottom:0}
.split ul{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 1em;padding-left:1.4em}
.split li{margin-bottom:.4em}

/* ---- Three-across feature columns ------------------------------ */
.trio{
  display:grid;gap:var(--space-m-l);
  grid-template-columns:repeat(3,1fr);align-items:start;
}
/* A 1px green rule between the columns - used on the fees page's policy row
   (Prescriptions / Veteran's Affairs / Pay At Consultation), which otherwise
   reads as three loose paragraphs rather than three related policies. The rule
   goes on the SECOND and THIRD children so it falls between the columns and
   never outside the group. Items stretch so the rules run the row's full
   height; below the tablet breakpoint the columns stack and the rules would
   sit across the flow, so they are dropped there. */
.trio--ruled{align-items:stretch}
@media (min-width:768px){
  /* Rules on BOTH edges of every column, not only between them: the reference
     brackets the group with an outer rule at each end, which reads as one
     bounded band rather than three columns that happen to have dividers. The
     shared inner edges collapse to a single 1px line because each column's
     left rule sits against its neighbour's right. */
  /* Left rule on every column, plus a right rule on the LAST one only. A
     border on both sides of every column renders the inner edges as two
     parallel lines, because the grid's gap holds them apart - a negative
     margin cannot close a gap it does not span. This gives one rule per
     boundary and still brackets the group at both ends. */
  .trio--ruled > *{
    border-left:1px solid var(--rule);
    padding-inline: var(--space-m);
  }
  .trio--ruled > *:last-child{border-right:1px solid var(--rule)}
}
/* Stacked below 768 the columns lose their vertical rules, so Prescriptions /
   Veteran's Affairs / Pay At Consultation ran together with nothing marking
   where one policy ends and the next begins. A rule ABOVE each item after the
   first restores the separation on the axis they now stack in - the same
   treatment .contact-grid--ruled gets when it stacks. */
@media (max-width:767px){
  .trio--ruled > * + *{
    border-top:1px solid var(--rule);
    padding-top: var(--space-m);
    margin-top: var(--space-s);
  }
}

/* The circular icon above each policy heading - the same lockup as the
   homepage's promise icons, at the size the reference shows. */
.policy__icon{
  width:2.7274rem;height:2.7274rem;border-radius:50%;
  background:var(--secondary);
  display:grid;place-items:center;flex:none;
  margin-bottom: var(--space-xs);
}
.policy__icon svg{width:1.3rem;height:1.3rem;fill:var(--bg-white)}

/* ============================================================
   APPOINTMENTS PAGE
   ============================================================ */

/* "Prefer to call?" lifts onto the hero photo rather than sitting as a flat
   strip beneath it. The band keeps its --accent ground so the card has
   something to sit against below the overlap; the negative margin does the
   lifting and the padding gives the space back, so nothing below moves up. */
.call-band{
  padding-block:0 var(--space-xl);
  background:var(--light-bg-2);
}
.call-card{
  position:relative;
  max-width:60.6091rem;                   /* 1000 / 16.4992 */
  margin-inline:0 auto;
  margin-top:calc(var(--space-2xl) * -1);
  background:var(--accent);
  border-radius:var(--radius-md);
  padding:var(--space-l);
  box-shadow:0 18px 40px rgb(74 74 44 / 0.14);
  display:grid;
  gap:var(--space-m) var(--space-l);
  grid-template-columns:1fr;
  align-items:start;
}
@media (min-width:48rem){
  /* Three columns at the same rhythm the page's content rows use, so the card
     reads as part of the page rather than an insert. */
  .call-card{
    grid-template-columns:minmax(0,auto) minmax(0,1fr) minmax(0,auto);
    align-items:center;
    gap:var(--space-l) var(--space-xl);
  }
}
.call-card__lead{display:flex;align-items:center;gap:var(--space-s)}
.call-card__icon{
  width:3.2rem;height:3.2rem;border-radius:50%;
  background:var(--primary);
  display:grid;place-items:center;flex:none;
}
.call-card__icon svg{width:1.45rem;height:1.45rem;fill:var(--bg-white)}
.call-card__number{margin:0;font-size:1.5rem;line-height:1.2;font-weight:700}
.call-card__number a{color:var(--primary);text-decoration:none}
.call-card__number a:hover{text-decoration:underline}
.call-card__copy{margin:0}
/* The hours arrive as the same <table class="hours-table"> the footer gets, so
   they take the same stacked treatment rather than a second layout. */
.call-card__hours .hours-table{border-collapse:collapse}
.call-card__hours td{padding:0;vertical-align:top}
.call-card__hours td:first-child{
  font-weight:700;color:var(--heading-brown);
  padding-inline-end:var(--space-s);
  white-space:nowrap;
}
.call-card__hours td + td{white-space:nowrap}
.call-card__hours tr + tr td{padding-block-start:var(--space-3xs)}

/* Each content item is led by an icon, matching .policy__icon on the practice
   policy page - the same nine-item shape, so the two pages agree. */
.appt-icon{
  width:3.15rem;height:3.15rem;border-radius:50%;
  background:var(--secondary);
  display:grid;place-items:center;flex:none;
  margin-bottom:var(--space-xs);
}
.appt-icon svg{width:1.5rem;height:1.5rem;fill:var(--bg-white)}
/* The rows were bare text blocks of differing length, so their tops aligned
   but nothing else did. A consistent gap under each heading and an even row
   gap gives the grid a rhythm. */
.appt-icon + h2{margin-top:0}

/* A soft light-green panel with 40px of padding, for a section that would
   otherwise sit as bare text on the page ground - the fees page's Workcover
   & TAC block. 2.4244rem = 40 / 16.4992. */
.soft-panel{
  background:var(--accent);
  border-radius:15px;
  padding:2.4244rem;
}

.trio h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5152rem;line-height:1.5;color:var(--heading-brown);margin:0 0 .6rem;
}
.trio h4{
  font-family:var(--font-head);font-weight:700;
  font-size:1.2728rem;line-height:1.35;color:var(--heading-brown);margin:0 0 .5rem;
}
.trio p{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 .85em}
.trio ul{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 .85em;padding-left:1.3em}
.trio li{margin-bottom:.35em}

/* ---- Practitioner list: ONE full-width column ------------------
   The live site lists every practitioner as a full-width row -
   152px portrait on the left, name and biography running the full
   content width beside it. This is NOT a card grid; an earlier
   build made it a 4-across grid and it read as a different site. */
/* Each doctor is a discrete entry; --space-m-l (41px) read as one continuous
   block. --space-l-xl gives them room to separate. */
.prac-list{display:flex;flex-direction:column;gap:var(--space-l-xl)}
.prac-row{
  display:grid;
  grid-template-columns:9.5rem minmax(0,1fr);
  gap:var(--space-s-m);
  align-items:start;
}
.prac-row__photo{
  /* Live: 152x152 with a 15px radius - a rounded square, not a circle. */
  width:9.213rem;height:9.213rem;
  border-radius:15px;object-fit:cover;
  background:var(--accent);
}
.prac-row__name{
  font-family:var(--font-head);font-weight:700;
  font-size:2.0001rem;line-height:1.25;color:var(--heading-brown);
  margin:0 0 .35rem;
}
.prac-row__role{
  font-family:var(--font-head);font-weight:600;
  font-size:1.1875rem;line-height:1.4;color:var(--secondary);
  margin:0 0 .6rem;
}
.prac-row__bio p{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 .8em}
.prac-row__bio p:last-child{margin-bottom:0}
.prac-row__bio ul{font-size:1.1875rem;line-height:1.6;color:var(--text);margin:0 0 .8em;padding-left:1.35em}

/* A heading that introduces a run of practitioner rows. */
.prac-group{
  font-family:var(--font-head);font-weight:700;
  font-size:2.9698rem;line-height:1.2;color:var(--heading-brown);   /* live 49px */
  margin:0 0 1.25rem;
}
.prac-group + .prac-list{margin-top:0}

/* ---- Reflow ---------------------------------------------------- */
@media (max-width:1024px){
  /* NOT 1fr 1fr across the whole tablet range. Every .trio on this site holds
     exactly THREE items, so two columns orphan the third beside a half-width
     void - four times over on one scroll of /booking-info. At 1024 three
     columns are 462px each, which is ample; the squeeze only bites lower
     down. Three columns to 900, two below that (where 3 would be ~230px), one
     on a phone. */
  .trio{grid-template-columns:repeat(3,1fr)}
  .display-title{font-size:2.875rem}
  .display-title--md{font-size:2.5rem}
  .display-title--sm{font-size:2.25rem}
  .phero__card{max-width:min(100%,30rem)}
}

/* Below 900 three columns get too narrow (~230px), so step to two; the third
   item pairs with the row above rather than sitting alone in a wide void. */
@media (max-width:899px) and (min-width:768px){
  .trio{grid-template-columns:repeat(2,1fr)}
}

@media (max-width:767px){
  .split,.split--text-wide,.split--media-wide{grid-template-columns:1fr}
  .split--flip .split__media{order:0}
  .trio{grid-template-columns:1fr}
  .display-title{font-size:2.25rem}
  .display-title--md,.display-title--sm{font-size:2rem}
  .eyebrow{font-size:1.1875rem}
  .phero{padding-block:var(--space-l-xl)}
  .phero__inner{justify-content:stretch}
  .phero__card,.phero--wide .phero__card{max-width:100%}
  .prac-row{grid-template-columns:1fr;justify-items:start}
  .prac-row__name{font-size:1.625rem}
  .prac-group{font-size:1.875rem}
  .split__media--bg{min-height:12rem}
}

/* An emphasised inline link. Weight 700, not <strong> nested in <a>, which
   compounds to 900 - a weight this design reserves for Manrope fee prices. */
.link-strong{font-weight:700;color:var(--primary)}

/* Practitioner qualifications line: live renders it at 19px weight 500,
   between the name and the biography. */
/* Doubled class for the same reason .prac-row__role is doubled below: this is
   a <p> inside .prose, and `.prose p` (0,1,1) beats a bare .prac-row__quals
   (0,1,0). Declared singly, the colour here was silently discarded and every
   doctor's post-nominals rendered --text brown - which is exactly what shipped
   and had to be caught on the live page. */
.prac-row__quals.prac-row__quals{
  font-family:var(--font-body);font-weight:500;
  /* Post-nominals in the lighter green, so the credentials read as secondary
     to the doctor's name rather than competing with it. --secondary-readable
     rather than --secondary: at 19px this is body copy and has to clear
     4.5:1 on cream, which --secondary (3.27:1) does not. */
  font-size:1.1516rem;line-height:1.45;color:var(--secondary-readable);
  margin:0 0 .7rem;
}
/* The role label sits ABOVE the name on the live site, at 21px w700. Its
   colour is --primary, the deeper green: it functions as an eyebrow over the
   doctor's name, and the warm brown it had inherited read as body copy.
   --primary rather than --secondary because at this size the lighter green
   is too low-contrast against the cream. */
.prac-row__role{
  font-family:var(--font-head);font-weight:700;
  font-size:1.2728rem;line-height:1.35;color:var(--primary);   /* live 21px */
  margin:0 0 .2rem;
}

/* A .trio nested inside one column of a .split. The live MEDICARE band keeps
   all three across inside the split column (x=572/813/1054 at 1440), so do NOT
   collapse it to one column - that was a 3-column-to-1 structural regression
   which also cost the page ~400px of height. Tighten the gutter and let the
   headings wrap instead; stack only once the split itself has stacked. */
.split .trio{grid-template-columns:repeat(3,1fr);gap:var(--space-s)}
.split .trio h2:not(.display-title){font-size:1.5152rem;line-height:1.5}   /* live 25px/37.1 */
/* Live stacks at Elementor tablet (<=1024), not 900: measured at 1024 the
   MEDICARE split is stacked and the card grid is 2-across; at 1080 both are
   still in their desktop form. */
@media (max-width:1024px){
  .split .trio{grid-template-columns:1fr}
}

/* Headings INSIDE a .trio column are a step down from a full-width section
   heading - the live service pages render them at 25px (h2) and 21px (h3),
   not the 33px a .prose h2 would give. Scoped so the same markup can be
   reused in a full-width band without shrinking there. */
.trio h2:not(.display-title){
  font-family:var(--font-head);font-weight:700;
  font-size:2.0001rem;line-height:1.3;color:var(--heading-brown);   /* live 33px */
  margin: var(--space-sm) 0 .6rem;
}
.trio > * > h2:first-child,.trio h2:first-child{margin-top:0}
.trio h3{
  font-family:var(--font-head);font-weight:700;
  font-size:1.5152rem;line-height:1.5;color:var(--heading-brown);   /* live 25px/37.1 */
  margin: var(--space-xs) 0 .5rem;
}
.trio ol{font-size:1.1516rem;line-height:1.6;color:var(--text);margin:0 0 .85em;padding-left:1.3em}
/* Not the 30px eyebrow mark: it is an icon, not a column image, and
   width:100% blew it up to the full column width. */
.trio img:not(.eyebrow__mark){width:100%;height:auto;border-radius:var(--radius-md);margin-bottom: var(--space-xs)}
.trio .btn{margin-top: var(--space-2xs)}

/* Quick Links lists only the SEVEN top-level destinations, as the live
   footer does - not the flattened 14 including both submenus' children.
   Flattening them forced a two-column span, which displaced the Contact Us
   column out of the 5-column row and onto a full-width row of its own,
   making the footer ~250px taller than live on every page. */

/* A heading inside a .split COLUMN is a column heading, not a full-width
   section heading: live renders it at 25px, matching .trio. Scoped to a
   direct child so a genuine section heading above the split is unaffected. */
/* A plain heading inside a .split column is a column heading (live 33px),
   but a .display-title in that position is a section title and keeps its
   own larger size - so exclude it rather than let this win on specificity. */
.split > div > h2:not(.display-title){font-size:2.0001rem;line-height:1.3;margin: var(--space-s) 0 .6rem}
.split > div > h2:first-child{margin-top:0}

/* Legal strip: live renders it at roughly 69px; ours was 111. The padding
   is the whole difference. */
.site-footer__legal-inner{padding-block: var(--space-xs)}

/* skin-clinic runs a tighter scale than the other service pages: its column
   headings are 25px with 21px below, where the rest use 33/25. Opt in per
   page rather than making the tighter scale the default. */
.trio--tight h2{font-size:1.5152rem}
.trio--tight h3{font-size:1.2728rem}

/* About lists the same nine doctors as the homepage, but live renders that
   instance as a compact Mulish 16 w600 list rather than the homepage's
   21px heading-face one. */
.name-list--compact li{
  font-family:var(--font-body);font-weight:600;font-size:0.9697rem;
  padding-block: var(--space-3xs);
}

/* The practitioners hero title is the largest on the site at 66px live,
   a step above the 58px the other page heroes use. */
.display-title--xl{font-size:4.0002rem}

/* booking-info deliberately differs from the four service pages: its card
   is cream (Light BG 2) at 645px, not the 873px accent card. Keeping it
   distinct rather than folding it into the service pattern. */
.phero--booking .phero__card{
  background:var(--light-bg-2);
  max-width:min(100%,39.0927rem);   /* 645px on the fluid root */
}

/* ---- Heading colour utilities -------------------------------------------
   The live site picks a heading colour PER HEADING, not by size or level: on
   telehealth alone the 33px headings are both primary green and brown ink,
   and the 21px ones are primary, secondary and dark brown. No size-based rule
   can express that, so colour is opted into at the call site.
   Ink stays the default. */
.t-primary{color:var(--primary)}
/* Headings using this land on both the cream and --accent grounds; --secondary
   measures 2.76:1 on the latter. */
.t-secondary{color:var(--secondary-readable)}
.t-ink{color:var(--heading-brown)}
.t-dark{color:var(--dark-brown)}

/* ---- Outlined group box -------------------------------------------------
   Live wraps the "How It Works" group in a 1px secondary-green outline with a
   20px radius and no fill. */
.outline-box{
  border:1px solid var(--secondary);
  border-radius:20px;
  padding:var(--space-m);
  background:transparent;
}

/* ---- Service-page outlined column ---------------------------------------
   The first column BENEATH the lead card carries a 1px outline with a 15px
   radius and 20px of padding - "Our Approach" on cosmetic-medicine, "Why You
   Might Need Ear Microsuctioning" on ear-microsuction, and so on. The lead
   card itself is the sage-filled block above it and takes no border; an
   earlier pass put the outline on the lead card, which double-treated it.
   1.2122rem = 20 / 16.4992. */
.trio--lead > .outline-box{
  border:1px solid var(--primary);
  border-radius:15px;
  /* 30px (1.8183rem = 30 / 16.4992), not 20: the outlined box sits beside an
     unboxed column, and at 20px its text started closer to the page edge than
     its neighbour's did, so the two columns read as different measures. */
  padding:1.8183rem;
}
/* The two columns of the hero's second row match in height. They are separate
   grid items with different amounts of copy, so without this the outlined box
   ended at its own last line while the column beside it ran on - the ragged
   pair the row is meant to avoid. */
@media (min-width:1025px){
  .trio--lead > .outline-box,
  .trio--lead > .outline-box ~ div:not(.trio__aside){align-self:stretch}
}
/* ---- Nested cards -------------------------------------------------------
   The service pages end with an accent-green outer card holding a cream inner
   card. Rendering that as one flat full-bleed band loses the whole shape. */
.card-outer{
  background:var(--accent);
  border-radius:30px;
  /* 40px inline (2.4244rem = 40 / 16.4992), not --space-s (~20px): the panel
     holds a three-column region and its contents sat hard against the 30px
     rounded corner. */
  padding:var(--space-m-l) 2.4244rem;
}
.card-inner{
  background:var(--light-bg-2);
  border-radius:20px;
  padding: var(--space-l) 1.8183rem 1.8183rem;   /* live 40/30/30 */
}

/* The eyebrow carries a 30x30 brand mark before its label on the live site.
   The label paragraph is a flex row so the mark sits on the text baseline
   rather than forcing its own line. booking-info has no mark on the live
   site and is deliberately left without one. */
.eyebrow{display:flex;align-items:center;gap: var(--space-2xs)}
.eyebrow__mark{width:1.8183rem;height:1.8183rem;flex:none;display:block}

/* The same branded mark the section eyebrows use, beside each submenu item.
   Sized in em so it tracks the link's own fluid type rather than being pinned
   at one viewport, and a little smaller than the eyebrow's 30px because it
   sits against a single line of nav text rather than a heading. Decorative:
   alt="" in the markup, so it is skipped by screen readers - the link text
   already names the destination. */
.site-nav__sub-mark{
  width:1.15em;height:1.15em;flex:none;display:block;
  /* Green, matching the link text. mark.png is solid black on transparency,
     so the image is used as a MASK and the colour comes from
     background-color - no second asset, no markup change, and the icon
     tracks currentColor if the link colour ever changes.

     The <img> still loads and is still the element being masked, so where
     mask is unsupported the visitor gets the original black mark rather
     than an empty box. */
  background-color:currentColor;
  -webkit-mask:url("../images/mark.png") center / contain no-repeat;
          mask:url("../images/mark.png") center / contain no-repeat;
}

/* ---- Service-page lead grid ---------------------------------------------
   Live puts the header card and the hero photo on the SAME row: the card
   spans the two text columns, the photo takes the third and starts at the
   very top of the content (y=248). The text columns then continue beneath
   the card, with the photo column carrying its own content below the image.

   Breakpoint is 1025px, measured on the live site: at 1025 the photo is
   still beside the card (y=248, w=298); at 1024 it jumps to a full-width
   800px and drops below everything (y~1612). That is Elementor's tablet
   breakpoint. */
@media (min-width:1025px){
  .trio--lead{
    grid-template-columns:1fr 1fr 1fr;
    align-items:start;
  }
  .trio--lead > .trio__lead-card{grid-column:1 / span 2;grid-row:1}
  .trio--lead > .trio__aside{grid-column:3;grid-row:1 / span 2}
  .trio--lead > .outline-box{grid-column:1;grid-row:2}
  .trio--lead > .outline-box ~ div:not(.trio__aside){grid-column:2;grid-row:2}
  .trio--lead > .trio__lead-card{margin-inline:0;max-width:none}
}
@media (max-width:1024px){
  .trio--lead{grid-template-columns:1fr}
  .trio--lead > .trio__lead-card{margin-inline:0;max-width:none}
}
/* Phone only: heading, then the bordered box, then everything else, with the
   photography LAST. Stacked, the aside (main image + practitioner bio) sat
   between the heading and the outline box, so a reader had to scroll past two
   photos to reach the section's substance. Ordered rather than re-marked-up,
   so the 1025+ three-column grid above - which places these by explicit
   grid-column/grid-row - is untouched.

   The aside is the only item pushed to the end; the boxes in between keep
   source order because `order` sorts equal values by DOM position. */
@media (max-width:767px){
  .trio--lead > .trio__lead-card{order:1}
  .trio--lead > .outline-box{order:2}
  .trio--lead > *{order:3}
  .trio--lead > .trio__aside{order:4}
}
.band.plain-hero{padding-block:var(--space-xl-2xl)}

/* The aside sizes to its own content. The LEAD CARD used to as well - live
   measures it at ~315px against a 725px photo - but now that it carries a
   visible 1px outline, a box ending at its own last line left the row looking
   ragged. It stretches instead, so every service page's opening row aligns at
   the same height. The aside keeps align-self:start. */
@media (min-width:1025px){
  .trio--lead > .trio__lead-card{align-self:stretch}
  .trio--lead > .trio__aside{align-self:start}
}
/* Stacked below the breakpoint, the photo was overflowing its column because
   the <img> carries explicit 800x1200 attributes. Constrain it like every
   other image on the site. */
.trio__aside img,.trio__aside .split__media img{
  width:100%;height:auto;max-width:100%;display:block;
}
.trio--lead > *{min-width:0}

/* ---- Heading size utilities ---------------------------------------------
   HEADING LEVELS FOLLOW THE SOURCE DOCUMENT'S OUTLINE; SIZES ARE SET BY CLASS.
   The live site uses <h5> for 33px section headings and <h6> for 21px
   practitioner roles, so matching its levels (right for accessibility and for
   the document outline) would otherwise inherit the template's h5/h6 sizes
   and shrink them to 14px. A tag change must never change the rendered size:
   the tag carries semantics, the class carries appearance.

   Sizes are px/16.4992 against this site's fluid root, so they track the
   viewport like everything else. Do not "fix" this by changing the tags back. */
.fs-74{font-size:4.4851rem;line-height:1.12}
.fs-66{font-size:4.0002rem;line-height:1.12}
.fs-58{font-size:3.5153rem;line-height:1.15}
.fs-49{font-size:2.9698rem;line-height:1.2}
.fs-41{font-size:2.4850rem;line-height:1.2}
.fs-33{font-size:2.0001rem;line-height:1.25}
.fs-25{font-size:1.5152rem;line-height:1.3}
.fs-22{font-size:1.3334rem;line-height:1.35}
.fs-21{font-size:1.2728rem;line-height:1.35}
.fs-19{font-size:1.1516rem;line-height:1.45}
/* The size class must beat the element defaults and the .trio/.split
   column-heading rules, which are themselves element-scoped. */
.trio h2.fs-41,.trio h3.fs-41,.trio h4.fs-41,.trio h5.fs-41,.trio h6.fs-41,
.split > div > h2.fs-41{font-size:2.4850rem}
.trio h2.fs-33,.trio h3.fs-33,.trio h4.fs-33,.trio h5.fs-33,.trio h6.fs-33,
.split > div > h2.fs-33{font-size:2.0001rem}
.trio h2.fs-25,.trio h3.fs-25,.trio h4.fs-25,.trio h5.fs-25,.trio h6.fs-25,
.split > div > h2.fs-25{font-size:1.5152rem}
.trio h2.fs-21,.trio h3.fs-21,.trio h4.fs-21,.trio h5.fs-21,.trio h6.fs-21,
.split > div > h2.fs-21{font-size:1.2728rem}
.prose h2.fs-41,.prose h3.fs-41,.prose h4.fs-41,.prose h5.fs-41,.prose h6.fs-41{font-size:2.4850rem}
.prose h2.fs-33,.prose h3.fs-33,.prose h4.fs-33,.prose h5.fs-33,.prose h6.fs-33{font-size:2.0001rem}
.prose h2.fs-25,.prose h3.fs-25,.prose h4.fs-25,.prose h5.fs-25,.prose h6.fs-25{font-size:1.5152rem}
.prose h2.fs-21,.prose h3.fs-21,.prose h4.fs-21,.prose h5.fs-21,.prose h6.fs-21{font-size:1.2728rem}
/* Heading font family/weight must survive a level change too. */
h1,h2,h3,h4,h5,h6{font-family:var(--font-head);font-weight:700}

/* The size utilities must also beat .display-title, .prac-group and the
   .split/.trio column-heading rules, which are more specific than a bare
   class. Doubling the class is the least invasive way to win without
   resorting to !important. */
.fs-49.fs-49{font-size:2.9698rem}
.fs-41.fs-41{font-size:2.4850rem}
.fs-33.fs-33{font-size:2.0001rem}
.fs-25.fs-25{font-size:1.5152rem}
/* The promises row is the one place this size is constrained by its container
   rather than by the page: the four columns stop growing at the 1450px content
   cap while a rem keeps climbing with the fluid root, so above ~1920 the
   longest heading outgrew its column and broke to a third line, and below
   ~1366 the column is too narrow for the longest line at the inherited size.
   Both ends are capped so the explicit <br> break points hold throughout. */
.promise h2.fs-25.fs-25{font-size:clamp(21px, 1.35rem, 25px)}
.fs-21.fs-21{font-size:1.2728rem}
.fs-19.fs-19{font-size:1.1516rem}
.split > div > h2.fs-49.fs-49,.trio h2.fs-49.fs-49,.prose h2.fs-49.fs-49,
.prose h3.fs-49.fs-49,.trio h3.fs-49.fs-49{font-size:2.9698rem}
.prose h5.fs-19.fs-19,.trio h5.fs-19.fs-19,.prose h6.fs-19.fs-19{font-size:1.1516rem}
.prose h2.fs-25.fs-25,.trio h2.fs-25.fs-25,.prose h3.fs-25.fs-25{font-size:1.5152rem}

/* Colour utilities must beat .display-title, .prose h2/h3 and the
   .trio/.split column-heading rules, all of which set colour and are more
   specific than a bare class. Doubling the class wins without !important. */
.t-primary.t-primary{color:var(--primary)}
.t-secondary.t-secondary{color:var(--secondary-readable)}
.t-ink.t-ink{color:var(--heading-brown)}
.t-dark.t-dark{color:var(--dark-brown)}
.prose h1.t-primary.t-primary,.prose h2.t-primary.t-primary,.prose h3.t-primary.t-primary,
.prose h4.t-primary.t-primary,.prose h5.t-primary.t-primary,.prose h6.t-primary.t-primary,
.trio h2.t-primary.t-primary,.trio h3.t-primary.t-primary,.trio h4.t-primary.t-primary,
.trio h5.t-primary.t-primary,.split > div > h2.t-primary.t-primary{color:var(--primary)}
.prose h1.t-secondary.t-secondary,.prose h2.t-secondary.t-secondary,.prose h3.t-secondary.t-secondary,
.prose h4.t-secondary.t-secondary,.prose h5.t-secondary.t-secondary,.prose h6.t-secondary.t-secondary,
.trio h2.t-secondary.t-secondary,.trio h3.t-secondary.t-secondary,.trio h4.t-secondary.t-secondary,
.trio h5.t-secondary.t-secondary,.split > div > h2.t-secondary.t-secondary{color:var(--secondary-readable)}
.prose h2.t-dark.t-dark,.prose h3.t-dark.t-dark,.trio h5.t-dark.t-dark{color:var(--dark-brown)}

/* ---- Practitioner row spacing -------------------------------------------
   The role label and the name read as ONE unit on the live site: a 7px gap,
   not the 59px the .prose h3 margin-top was contributing. The name is a
   heading for the document outline, so the fix is to zero the inherited
   margin rather than to change the tag. */
.prose .prac-row__name,.prose h3.prac-row__name{margin-top:0}

/* ---- Practitioners top section ------------------------------------------
   Live gaps, measured at 1440: 47px after the first BOOK APPOINTMENT, then a
   372px break before the next eyebrow, and only 32px between the second
   block's copy and its button. Matched per block rather than with one global
   value, because the two blocks genuinely differ. */
/* The doctor list opens a new section of the page, directly under the intro
   block's BOOK APPOINTMENT button - so it needs section separation, not the
   paragraph spacing it had. --space-2xl-3xl is the step this site already
   uses between bands (123.7px at 1440), scaling down with the rest. */
.prose__inner > .prac-list{margin-top: var(--space-2xl-3xl)}
/* The break before the second eyebrow block. */
.band + .band > .band__inner > .eyebrow:first-child{margin-top:0}

/* Fine-tuning against the live gaps measured at 1440: role->name 7px (we were
   at 2), and 32px between the second block's copy and its button (we were at
   51). The 372px section break is now 340, close enough that pushing further
   would start to look padded rather than deliberate. */

/* ---- Portrait beside a bio ----------------------------------------------
   Live puts the practitioner portrait to the LEFT of its heading (portrait
   x=953, heading x=1138) at 165x182 with a 20px radius. aspect-ratio rather
   than a fixed height so it reflows with the column. */
.bio-row{
  display:grid;grid-template-columns:10rem minmax(0,1fr);
  gap: var(--space-xs);align-items:start;
}
.bio-row__photo{
  width:100%;height:auto;aspect-ratio:165/182;object-fit:cover;
  border-radius:20px;display:block;margin:0;
}
.bio-row h5{margin-top:0}
@media (max-width:767px){
  .bio-row{grid-template-columns:1fr}
  .bio-row__photo{max-width:12rem}
}

/* Role label and name read as one unit: live measures a 7px gap between
   the role's bottom and the name's top. Stated once, as the role's own
   bottom margin, so nothing later has to fight a margin-top. */
/* Doubled class to beat `.prose p` (0,1,1), which otherwise wins over a bare
   .prac-row__role (0,1,0) and paints this line --text at the prose size -
   which is exactly what it did, silently, to the colour and font-size set on
   the rule above. Anything this element needs that .prose p also sets has to
   be declared HERE, not there. */
.prac-row__role.prac-row__role{
  margin-bottom:7px;
  color:var(--primary);
  font-size:1.2728rem;
  line-height:1.35;
}
.prac-row__name.prac-row__name{margin-top:0;margin-bottom: var(--space-3xs)}

/* ---- BOOK APPOINTMENT spacing -------------------------------------------
   The RATIO is what carries the meaning, not either gap alone. On the live
   site a CTA is tightly coupled to the copy it closes (~35-47px above) and
   strongly separated from whatever follows (~360px below) - roughly 1:10 -
   so it reads as belonging to its own block. An earlier pass had it at 1:5,
   which put the button nearly as close to the next section as to its own
   copy and inverted the hierarchy the owner was reacting to.

   The separation is expressed as the FOLLOWING section's top padding rather
   than a margin under the button, because the button is the last child of
   its block and a margin there would collapse. */
.plain-hero__inner > .btn,
.phero__card > .btn{margin-top:2.12rem}          /* live 35px above */
.prose__inner > .btn.u-stack-top{margin-top:1.94rem}  /* live 32px, 2nd block */

/* ~360px of separation below a block that ends in a CTA. clamp() so it
   scales down on narrow viewports instead of leaving a dead screen. */
.section-after-cta{padding-top:clamp(4rem,1.6rem + 8.4vw,21.8rem)}
/* ---- Section rhythm ceiling ----------------------------------------------
   No two stacked sections should sit more than ~120px apart. A gap is the sum
   of the FACING edges - one section's padding-bottom plus the next one's
   padding-top - so a 123.7px band padding produces a 247px gap, which is what
   /contact and /forms-and-downloads measured. Capping each edge at 60px puts
   the pair at 120px.

   Applied to the padding-block users that exceeded it rather than to every
   band, so the deliberately tighter rhythms set elsewhere in this file (the
   50px service-page gap, the 80px About gap) still win by specificity or by
   coming later. 3.6366rem = 60 / 16.4992. */
main > section.band{padding-block:3.6366rem}
/* The heavier per-page bands - contact's two panels, the forms page's four -
   set 123.7px a side, which is a 247px gap between any two of them. */
main > section.band.contact-split,
main > section.band.contact-access,
.page-forms main > section.band,
main > section.band.panel-band,
main > section.band.fees{
  padding-block:3.6366rem;
}
/* The homepage's fees band adds a 50px margin to the panel below it, which on
   top of two 60px paddings made 170px. Trimmed so the pair lands on ~110px -
   the separation is still deliberate, just not doubled up. */
.fees + .panel-band{margin-top:0}
/* 80px after the SPECIAL SERVICES panel rather than the 120px ceiling: the
   panel is a filled block with its own 40px of internal padding, so the
   section gap reads deeper here than between two plain bands. Split across the
   facing edges. 2.4244rem = 40 / 16.4992. */
main > section.band.panel-band:has(.panel) + section.band.fees{padding-top:2.4244rem}
main > section.band.panel-band:has(.panel):has(+ .fees){padding-bottom:2.4244rem}
/* .section-after-cta's clamp climbs to 21.8rem (~350px). Its whole job is a
   deliberate breath after a call to action, so it keeps a larger value than
   the rest - but 60px a side, not 147. */
.section-after-cta{padding-top:3.6366rem}

/* Homepage hero: a 50px TOP padding, not the 60px section ceiling and not the
   +50px the inner-page heroes got - this band is the first thing on a laptop
   screen, and every pixel above the h1 is content pushed below the fold.
   3.0304rem = 50 / 16.4992. The bottom edge keeps the ceiling value so the
   gap to the band below is unaffected. */
main > section.band.hero{padding-top:1.8183rem}   /* 30px */

/* Service pages: 80px between the hero and the section below it. The band
   after the hero carries .section-after-cta, whose clamp climbs to ~21.8rem
   (~350px) on a wide screen - ear-microsuction measured a 255px gap at 1440.
   Split across the facing edges so it stays symmetrical: 40px each side.

   This WAS 27.65px a side, which measured as 80px only because the band below
   carries .reveal--up and was being measured mid-animation, offset downward by
   the reveal distance. The resting gap was 55px. Raising the reveal distance
   exposed it (the audit read 89px), which is the useful lesson: a layout
   measured while a scroll-driven animation is part-way through is measuring
   the animation, not the layout. Verified here under prefers-reduced-motion,
   where transforms are off and the number is the real one. */
.page-cosmetic .plain-hero + .band,
.page-skin .plain-hero + .band,
.page-ear .plain-hero + .band,
.page-tele .plain-hero + .band{padding-top:2.4244rem}
.page-cosmetic .band.plain-hero,
.page-skin .band.plain-hero,
.page-ear .band.plain-hero,
.page-tele .band.plain-hero{padding-bottom:2.4244rem}

/* About's History section follows the "Caring for our community" band, and
   .section-after-cta's clamp climbs to 21.8rem (~350px) on a wide screen -
   the pair measured a 230px gap at 1440. ~80px of air between the two, split
   across the facing edges so it stays symmetrical. 2.4244rem = 40 / 16.4992,
   x2 = 80. */
.page-about .section-after-cta + .section-after-cta{padding-top:2.4244rem}
.page-about .section-after-cta:has(+ .section-after-cta){padding-bottom:2.4244rem}

/* ---- Fluid nav spacing (single source of truth) --------------------------
   No steps anywhere between 1024 and 1600: monotonic, and continuous through
   what used to be the 1300 and 1150 breakpoints. */
/* ONE fluid value drives both the item spacing and the pipe's position, so
   the separator stays exactly midway between two labels at every width.
   Tuned to the TIGHTER of the two original gaps (~23px at 1440), which is
   what the owner asked for - the link's own .6em padding is part of the
   visible space, so the flex gap is the remainder. */
/* The nav gap is NOT a scale token: it is solved for, not chosen. The pipe is
   positioned from half this value, and the pair (gap, caret pull-back) was
   derived by measuring the rendered gaps either side of the separator until
   they matched at every width. A token's range breaks that solution. */
.site-nav__list{--nav-sep-gap:clamp(0.35rem, 1.9vw - 15.5px, 0.79rem);gap:var(--nav-sep-gap)}
.site-nav__list a{margin-inline:0}

/* ---- Space before the footer ---------------------------------------------
   120px between the last section's content and the footer's top edge. Pages
   were landing at 28-60px, so the footer crowded whatever ended the page. It
   goes on the footer rather than on each last section, because "the last
   section" differs per page and several of them carry their own padding
   rules. Most pages' last band contributes 60px, so 60px here lands the pair
   on 120. The service pages end on a band that contributes less (skin-clinic's
   hero gives only 27.65px), so they get the difference made up explicitly. */
.site-footer{margin-top:3.6366rem}
/* ...unless the last band is COLOURED, in which case the margin shows as a
   strip of page background between the band and the footer. On the homepage
   that read as a 60px cream stripe under the green assurance band. Cream
   bands hide it because the margin matches them; green does not. The space
   moves inside the band, so the colour runs right down to the footer. */
body:has(main > section.band--accent:last-child) .site-footer{margin-top:0}
/* The band absorbs what the footer margin used to provide PLUS its own
   bottom padding, so the cards sit evenly inside the colour: --space-xl-2xl
   matches the padding-block .assurance__inner puts above them, and the
   3.6366rem is the 60px every other page has between its last band and the
   footer. */
main > section.band--accent:last-child{
  padding-bottom:calc(var(--space-xl-2xl) + 3.6366rem);
}
.page-cosmetic .site-footer,
.page-ear .site-footer,
.page-tele .site-footer{margin-top:5.1517rem}     /* +25px: last band gives 60 */
/* skin-clinic ends on a .plain-hero, whose 27.65px bottom padding is ~32px
   short of the 60px every other page's last band contributes. Making that up
   on the footer is what the other overrides do, but here it is wrong: those
   pages' extra sits INSIDE the band (a trailing paragraph margin), so their
   band edge still meets the footer at 60px like everywhere else. This one had
   no internal slack to absorb, so the whole 32px landed outside the band and
   the gap measured 92px against 60 elsewhere - visibly deeper. The padding
   goes on the band instead, so the edge lands on 60 with the rest. */
.page-skin main > .band:last-child{padding-bottom:3.6366rem}

/* ---- Header divider ------------------------------------------------------
   The header shares --light-bg-2 with several of the bands that open a page,
   so on those pages the nav row and the content below it run together with no
   edge. A 1px rule in the secondary green closes the header off - the mirror
   of the footer's top rule below, and the same colour, so the page is bounded
   by a matching pair. It sits on .site-header rather than on the inner
   container so it runs full-bleed, like the footer's. */
.site-header{border-bottom:1px solid var(--rule)}

/* ---- Footer divider ------------------------------------------------------
   The live site separates the footer from the page with a 1px rule in the
   secondary green, full-bleed. It matters because the footer and the content
   above it share a background, so without it they run together. */
.site-footer{border-top:1px solid var(--rule)}
/* The footer spans 1450px rather than the site's 82.5rem (1361px) shell: it
   carries four columns plus a logo lockup, and at shell width Quick Links and
   Contact Us were the two tightest things on the page. 87.8831rem =
   1450 / 16.4992. It stays a max-width, so narrow viewports are unaffected. */
.site-footer__inner{max-width:87.8831rem}
/* ---- Wide bands ----------------------------------------------------------
   SPECIAL SERVICES, WHAT TO KNOW BEFORE YOU VISIT, MORE BOOKING INFORMATION
   and the four-up promises row.

   TWO SEPARATE MEASUREMENTS, and conflating them was a real bug:
     - the PANEL runs full width, inset a fixed 50px each side. No cap - it
       keeps growing with the viewport, which is what "full width with about
       50px margin" means and what the reference screenshots show (their panel
       spans 97.8% of the viewport).
     - the CONTENT INSIDE the panel caps at 1450px and centres within it, so
       text lines stay readable on a very wide display.
   Putting both on one element made the cap win: the panel stopped at 1450px
   and floated in the middle of a wide screen at 57% of the viewport, which is
   the opposite of full-bleed.

   px on purpose. The gutter is a fixed inset and the cap a hard ceiling;
   deriving either from the fluid root makes them drift with the root
   font-size - the shell was landing at 1358px at 1440 and 1485px at 1920. */
/* The gutter goes on the BAND, replacing its own --pad-x rather than adding to
   it: .band already carries padding-inline, so a 50px inset on the inner
   element landed at 95px total. */
.panel-band,
.more,
.promises{padding-inline:50px}
/* Phone: 20px, not 50. The 50px gutter above is right on a desktop but eats a
   quarter of a 390px screen - it left these bands' content 290px wide against
   the 355px every other band gets, so they read as inset from the rest of the
   page. This override has to sit AFTER the rule above rather than up in the
   phone block: both selectors are (0,1,0), so a media query does NOT beat it -
   only source order does. Putting it with the other phone rules looked right
   and silently lost the cascade. */
@media (max-width:767px){
  .panel-band,
  .more,
  .promises{padding-inline:5px}
}
.panel-band > .band__inner{
  max-width:none;
  margin-inline:0;
  padding-inline:0;
}
/* MORE BOOKING INFORMATION caps at 1450 rather than running full-bleed: it
   holds a two-column card, and past that width the photo and the text both
   stretch further than either wants to be. */
.more > .band__inner{
  max-width:1450px;
  margin-inline:auto;
  padding-inline:0;
}
/* The promises band differs from the panel bands: its GREEN is the band
   itself, so the colour must run full-bleed while the four columns inside
   stay within 1450px. The panel bands cap their content one level deeper,
   inside .panel; this band has no such wrapper, so the cap goes here. */
.promises > .band__inner{
  max-width:1500px;
  margin-inline:auto;
  padding-inline:0;
}
/* The content inside each panel is what caps. */
.panel-band .panel > *,
.more__card > *{
  max-width:1450px;
  margin-inline:auto;
  width:100%;
}
/* The promises row alone goes to 1500 - asked for specifically, and only for
   this band. The panel bands above keep 1450: widening them was a side effect
   of sharing this selector, not something anyone asked for. */
.promises__inner > .promise{
  max-width:1500px;
  margin-inline:auto;
  width:100%;
}
/* ...except the slideshow. This rule's `margin-inline:auto` comes LATER in the
   cascade than .more__slideshow's own `margin:40px` shorthand, so it silently
   zeroed the left and right inset while the top and bottom survived - the
   photo kept touching the card's border on one side only. The slideshow is a
   photo, not a text column, so it wants neither the auto margin nor the cap. */
.more__card > .more__slideshow{
  margin:40px;
  max-width:none;
  width:auto;
}
/* On a phone a 40px inset each side takes 80px out of a ~330px card, and with
   the 4:3 ratio that collapsed the photo to 173x130 - smaller than the dots
   sitting on it. The inset steps down with the viewport; the ratio also opens
   up, since a stacked photo has the full card width to work with. */
@media (max-width:767px){
  /* One column: the card is a grid now, so stacking is explicit rather than
     the side effect of flex-wrap it used to be. */
  .more__card{grid-template-columns:1fr}
  /* Copy first, slideshow after it. The photo leads on desktop because it sits
     BESIDE the text; stacked, it pushed the heading and the prescriptions copy
     below the fold, so a reader met a photo before knowing what the section
     was. Reordered with grid `order` rather than by moving the markup, so the
     source order - and the desktop layout that depends on it - is untouched.
     Grid order does not affect reading order for screen readers or keyboard
     focus, which still follow the DOM; that is the right trade here because
     the photo is decorative and the copy is the content. */
  .more__body{order:1}
  .more__slideshow{order:2}
  .more__card > .more__slideshow{margin:0 20px 20px}
  /* Stacked, the photo has no sibling column to match, and the track is out
     of flow so the box has no height of its own - it needs the ratio back to
     have any height at all.

     4/3 rather than the 2/1 it had: at full card width 2/1 is a shallow
     letterbox strip, and these are photographs of rooms and people rather
     than banners. A taller frame gives each slide something to show. */
  .more__slideshow{aspect-ratio:4 / 3}
}
@media (max-width:480px){
  .more__card > .more__slideshow{margin:15px}
}
@media (max-width:767px){
  /* On a phone a 50px gutter would eat a quarter of the screen; fall back to
     the site's normal container padding. */
  .panel-band > .band__inner,
  .more > .band__inner,
  .promises > .band__inner{
    max-width:none;
    padding-inline:var(--pad-x);
  }
}

/* ~40px below the last footer column, before the legal strip. Scoped to the
   desktop breakpoint: the mobile block above deliberately trims the footer's
   block padding, and an unscoped rule here would sit later in the cascade at
   equal specificity and silently undo it. */
@media (min-width:768px){
  .site-footer__inner{padding-bottom:2.4244rem}
}

/* ---- Policy cards --------------------------------------------------------
   practice-policy lays its policies out as accent-green cards in TWO columns,
   each 630px wide at x=75 / x=735, radius 15. The two columns pack
   INDEPENDENTLY - "Test Results" (col 2) starts at y=1560, before
   "Medical Certificates" (col 1) at y=1615 - and the live reading order
   alternates across the columns (1,3,5,7 left / 2,4,6 right) rather than
   filling one column before the next. CSS columns:2 gives the packing but
   the WRONG order (it fills col 1 first), so the two columns are explicit
   flex stacks with the cards assigned to each in the markup. */
/* Two COLUMNS that end level, which is what matters here - not row-by-row
   pairing. An earlier pass unwrapped the columns with display:contents so each
   Nth card matched its opposite number; that made rows align but left the
   columns themselves 370px apart at the bottom, because the left column holds
   four cards and the right holds three.
   The columns are restored as flex columns and stretched to the tallest, then
   the cards inside the shorter one grow to absorb the slack, so both sides
   finish on the same line. */
.policy-cards{
  display:grid;grid-template-columns:repeat(2,1fr);
  gap: var(--space-m);align-items:stretch;
}
.policy-cards__col{
  display:flex;flex-direction:column;
  gap: var(--space-m);min-width:0;
}
/* Cards share the column's spare height between them rather than one taking
   all of it, so the shorter column's blocks stay proportionate. */
.policy-cards__col > .policy-card{flex:1 1 auto}
.policy-card{
  background:var(--accent);
  border-radius:15px;
  padding: var(--space-lg);             /* live: 50px on all four sides */
  margin:0;
}
/* Card typography, measured off live: heading 24.7/37.1 (ratio 1.5) with a
   16px gap to the body, body 18.6/27.8 (also 1.5). The heading is a shade
   smaller than the shared .fs-25 utility, so it is set here rather than by
   changing that utility, which other pages rely on. */
.policy-card h3{
  margin-top:0;margin-bottom:0.9697rem;   /* 16px */
  font-size:1.4970rem;line-height:1.5;    /* 24.7px / 37.1px */
}
.policy-card p{font-size:1.1273rem;line-height:1.5}  /* 18.6px / 27.8px */
.policy-card p:last-child{margin-bottom:0}

/* A full-width accent band, as the Healthcare Complaint Commission section
   is on the live site. */
.band--accent-full{background:var(--accent)}
/* Its contact columns are separated by a rule, so postal / phone / web read as
   distinct routes rather than one run of text. */
@media (min-width:768px){
  .contact-grid--ruled > * + *{
    border-left:1px solid var(--rule);
    padding-left: var(--space-m);
  }
}
/* Stacked on a phone the columns lose their vertical rules, so postal / hours
   / phone / web ran together as one block of text. A rule ABOVE each item
   after the first restores the separation on the axis they now stack in.

   Capped at 600, not 767: .contact-grid uses auto-fit/minmax, so its column
   count follows the available width rather than any breakpoint - at 767 it
   still resolves to THREE columns, which would have worn horizontal rules
   across a horizontal layout. It genuinely stacks below ~600. */
@media (max-width:600px){
  .contact-grid--ruled > * + *{
    border-top:1px solid var(--rule);
    padding-top: var(--space-sm);
    margin-top: var(--space-sm);
  }
}

/* ---- Practice Policies hero ---------------------------------------------
   Centred, with ~120px above it, as reference 3 shows - the page opened
   left-aligned and hard against the header. 7.2731rem = 120 / 16.4992. */
.policy-hero .prose__inner{text-align:center}
.policy-hero .lede{margin-inline:auto}
.policy-hero .btn{margin-inline:auto}
/* .eyebrow is display:flex, so text-align does not centre it - it needs
   justify-content, and the whole row centred on the page. */
.policy-hero .eyebrow{justify-content:center}

/* The three-across intro photos are square-ish with a 20px radius. The middle
   one drops and the third rises, so the row reads as a staggered group rather
   than three equal blocks - the arrangement the reference uses. 3.0304rem =
   50 / 16.4992. */
.photo-trio{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap: var(--space-m);align-items:start;
}
.photo-trio img{
  width:100%;height:auto;aspect-ratio:1/1;object-fit:cover;
  border-radius:20px;display:block;margin:0;
}
@media (min-width:768px){
  .photo-trio > :nth-child(2){margin-top:3.0304rem}
  .photo-trio > :nth-child(3){margin-top:-1.5152rem}
}

/* This page's content sits in a 1290px container (x=75), narrower than the
   site default --shell (1361). Live has no single site-wide container - each
   Elementor section carries its own padding (1290@75, 1350@45, 1380@30 and
   1400@20 all occur) - so this is scoped to the page, NOT applied to --shell,
   which would move every band on every other page. The HCC band is the one
   exception here: it is 1350@45 on live. */
.page-policy .band__inner{max-width:78.1856rem}   /* 1290 / 16.4992 */
.page-policy .band--accent-full .band__inner{max-width:81.8222rem}  /* 1350 */

@media (max-width:1024px){
  /* NOTE: .policy-cards is display:grid, so flex-direction here has never had
     any effect - it stayed two columns all the way down. Left in place only
     because removing it is a separate change; the real control is below. */
  .policy-cards{flex-direction:column}
}
@media (max-width:767px){
  /* One column on a phone. Two 1fr columns left each policy card 167px wide
     at 390 - narrow enough that headings broke across three lines. */
  .policy-cards{grid-template-columns:1fr}
}
@media (max-width:767px){
  .photo-trio{grid-template-columns:1fr}
  .policy-card{padding: var(--space-sm) 1.6rem}
}

/* ---- Contact page --------------------------------------------------------
   Measured off the live page (which WordPress still serves at /sample-page/).
   Its container is 1350@45, not the site default --shell, and the layout
   alternates sides deliberately: the hero card sits RIGHT, the map card LEFT.
   Scoped to .page-contact so no other page moves. */
.page-contact .band__inner,
.page-contact .phero__inner{max-width:81.8222rem}     /* 1350 / 16.4992 */
/* Vertical rhythm for the two contact bands.

   THE TRAP: `section.band:not([class*=" "])` above gives padding-block ONLY to
   a band with a single class. Both of these carry three
   (`band band--cream contact-split`), so they match nothing and computed to
   0 - the sections rendered flush against their neighbours. An earlier
   padding-top:7.2731rem here was a raw-rem patch on the top of ONE band; it
   left the bottom at 0 and never covered .contact-access at all.

   Live is 120px top and bottom on both. --space-2xl-3xl is the Utopia step
   that already carries 120px elsewhere on this site (123.7px at 1440), and it
   scales down with the rest of the scale rather than holding 120 to 390px. */
.page-contact .contact-split,
.page-contact .contact-access{padding-block: var(--space-2xl-3xl)}

/* Hero: card on the right, 651px wide, radius 20, on the accent green. */
/* Live carries 150px top / 300px bottom here, which made an 842px hero around
   a 419px card - half of it empty ground below the card. Halved to ~75px a
   side, which keeps the card clear of the header and the band below without
   the hero running past the fold. 4.5457rem = 75 / 16.4992. */
.page-contact .phero{padding-block:4.5457rem}
.page-contact .phero__card{
  max-width:min(100%,39.4565rem);   /* 651px */
  border-radius:20px;
  padding: var(--space-l) 3.0305rem;      /* 40 / 50 */
}

/* --- THE 50px CUT-OUT CORNER --------------------------------------------
   The composition device of this page. Cream panels sit on the cream page
   with ONE corner cut to a 50px radius, and the cut corners of diagonally
   opposite panels face each other, so the pair reads as two interlocking
   tiles rather than as text on a flat background. Live uses it three times:
   Contact Details (bottom-left) against Our Location (top-right), and
   Disabled Access (bottom-right) against Car Parking (top-left).
   The radius is what does the work - do not "tidy" these to all-corners. */

/* --- Contact details / Our location ------------------------------------
   Live: a 540 / 729 pair. The map card is the accent block; the map itself
   is 460x626 inset 40px inside it. The right panel is a 2x2 of 365px cells
   (photo, Contact Details / Our Location, Continuity of Care). */
.contact-split__inner{
  display:grid;
  /* Live is 540 + 729 with an 80px gutter at 1440. Expressed as fractions so
     the pair scales down cleanly instead of overflowing between 1025 and 1366,
     where fixed px tracks did not fit. */
  grid-template-columns:minmax(0,540fr) minmax(0,729fr);
  gap:var(--space-l-xl);
  align-items:start;
}
.contact-card{
  background:var(--accent);
  border-radius:20px;
  padding: var(--space-l);                        /* 40px, giving the 460px map */
}
.contact-map{
  display:block;width:100%;aspect-ratio:460/626;height:auto;
  border:0;border-radius:20px;
}
/* A caption for the pin, so the card states the address rather than leaving it
   only inside the embed. One line, unlike the footer's stacked form. The map
   sizes by aspect-ratio with height:auto, so this adds height to the card
   without distorting the embed. */
.contact-map__address{
  margin:var(--space-s) 0 0;
  font-size:1.0625rem;line-height:1.4;
  color:var(--heading-brown);text-align:center;
}
/* The CREAM ground. Same construction as the about page's Practice Aim group,
   and the same trap: the interlock is an ILLUSION. Do not give this a green
   background - the green the eye sees between the quadrants comes from the two
   gradient slots below, showing through the notches. Live 729x706 at x=665;
   the rebuild had no ground panel at all, which is why the interlock did not
   read. */
.contact-detail{
  display:grid;
  background:var(--light-bg-2);
  grid-template-columns:repeat(2,minmax(0,1fr));   /* live 2 x 365px */
  /* Live's rows are 289 and 417 (the photo's height, then the remainder of the
     706 panel), and the CARDS sit inside those rows rather than defining them -
     Contact Details is 273 in a 289 row, Our Location 402 in a 417 row. Letting
     the cards set the row heights instead put the second row ~60px too high,
     which is what stopped the cut-out corners meeting. */
  grid-template-rows:17.5161rem 25.2739rem;   /* 289px / 417px */
}
.contact-photo{
  position:relative;
  background-size:cover;background-position:50% 50%;background-repeat:no-repeat;
  /* CORRECTION to the note that stood here, which said live's photo is plain
     20px on all four corners. It is not: live is 20px 20px 0, square where it
     meets the group's interior corner, and it carries a ::before radial
     gradient that reaches full sage at that same corner. That overlay is what
     melts the photo into the green - without it the corner reads as a hard
     edge and the interlock stops at the photo. */
  border-radius:20px 20px 0;
  /* NO aspect-ratio. The grid rows here already have explicit heights
     (289/417 at desktop), so a ratio makes the photo size itself from its
     own WIDTH instead of filling its cell - and as the column narrows it
     shrinks faster than the row does. Between 1025 and 1366 that left it up
     to 60px short of the slot beside it, so the photo visibly detached from
     the interlock. Filling the cell is what keeps the 2x2 square. */
  height:100%;
}
.contact-photo::before{
  content:"";position:absolute;inset:0;
  border-radius:inherit;
  /* 5%, not live's 1%. DELIBERATE DEPARTURE - do not "correct" it back, and
     do NOT raise it. At 1% the overlay reaches full accent at essentially a
     single point, so the photograph shows through the green along the bottom
     and right EDGES of the photo - which is where the artefact actually is.
     Measured along those two edges (dE from accent, >4 = photo visible):

       stop   bottom edge clean to   right edge clean to
        1%            0px                   0px    <- the reported defect
        5%           ~15px                 ~10px   <- CHOSEN
        8%           ~35px                 ~20px
       12%           ~55px                 ~35px
       18%           ~95px                 ~65px

     5% is the lightest value that clears the visible corner edge (bottom-edge
     dE at 20px drops 15 -> 6, right-edge 21 -> 12). Keeping the photograph
     intact is the priority, and 18% dims substantially more of it than the
     defect warrants.

     This DOES mean the fade is not solid across the full 50px notch arc. That
     was the rationale for a higher stop and it has been deliberately traded
     away - do not cite the notch as a reason to raise this later. An earlier
     18% here came from sampling the box DIAGONAL, which runs through the
     middle of the image and overstated how far the green had to reach.

     (The ::before overlay ITSELF is faithful to live - live carries the same
     radial-gradient on the same pseudo-element. Only the stop differs.) */
  background-image:radial-gradient(at 100% 100%, var(--accent) 5%, rgba(2,1,1,0) 100%);
}
.contact-cell{
  padding: var(--space-m);                        /* 30px, as live */
  align-self:stretch;
  /* Centre the copy in the cell, as on the about page's quadrants. The cells
     carry different amounts of text, so a top-aligned block leaves the short
     ones looking like they have slipped upwards. */
  display:flex;flex-direction:column;justify-content:center;
}
/* The interlock. Verified twice, from the live DOM AND from the rendered
   pixels, because two earlier passes read it wrongly in opposite directions:
   one made every bottom cell sage, the other made only Continuity of Care a
   sage card with its notch on the wrong corner.

   What is actually there at 1440:
     TL  665,1002 365x289  the clinic photo, 20px all round
     TR 1030,1002 365x273  CREAM, cut BOTTOM-LEFT 50px
     BL  665,1291 365x402  CREAM, cut TOP-RIGHT 50px
     BR 1030,1291 365x417  SAGE, radius 0/20/20/20

   Sampled pixels: TR and BL are both cream (254,249,239); only BR is sage
   (227,232,211); the far right edge at mid-height is cream, so there is NO
   sage panel running up the right-hand side.

   The two cut corners face each other across the shared corner at
   (1030,1291). That mutual facing is the whole effect - the notches must be
   on the diagonal, not on the same edge. */
/* The two notched slots. The gradient sits on the slot, the notch on the card
   inside it - live layers them at identical coordinates, exactly as the about
   page does.

   The cards FILL their slots. An earlier pass pinned them to live's inner
   heights (273 in a 289 row, 402 in a 417 row) with align-self:start, on the
   reading that live deliberately shows a strip of gradient along each card's
   outer edge. Whatever live's intent, the result here reads as the green
   background leaking out from under the card - so the cards stretch. */
.contact-slot{min-width:0;display:grid;align-items:stretch}
.contact-slot > .contact-cell{grid-area:1/1}
.contact-slot--tr{background-image:linear-gradient(var(--light-bg-2) 0%, var(--accent) 100%)}
.contact-slot--bl{background-image:linear-gradient(var(--accent) 0%, var(--light-bg-2) 65%)}

.contact-cell--details{background:var(--light-bg-2)}
.contact-cell--cut-tr{background:var(--light-bg-2)}
.contact-cell--panel{
  background:var(--accent);
  border-radius:0 20px 20px 20px;
}
/* The two facing notches. These MUST stay on the diagonal: bottom-left on
   the top-right cell, top-right on the bottom-left cell, so the cut corners
   meet at the group's shared interior corner. */
.contact-cell--cut-bl{border-radius:0 0 0 50px}   /* Contact Details, top-right cell */
.contact-cell--cut-tr{border-radius:0 50px 0 0}   /* Our Location, bottom-left cell */
.contact-detail h4{margin-top:0}
/* Trailing margin on the last block skews the optical centring ~18px low. */
.contact-cell > *:last-child{margin-bottom:0}
.contact-access__panel > *:last-child,
.contact-access__cell > *:last-child{margin-bottom:0}

/* --- Access / transport / parking ---------------------------------------
   Live: a 540 / 810 two-column grid inside the same 1350 container.
   Row 1 pairs the accent "Disabled Access" block with "Public Transport";
   row 2 pairs a photo block with the accent "Car Parking" block. Each accent
   block holds a cream panel with one cut-out corner. */
.contact-access__inner{
  display:grid;
  grid-template-columns:minmax(0,540fr) minmax(0,810fr);   /* live 540 / 810 */
  gap:0;
}
/* SAME DEVICE AS THE MAP GROUP AND THE ABOUT PAGE. Re-read off live rather
   than inferred: the green block is a plain rectangle, and the cream panel
   inside it FILLS it completely (live: a 540x390 panel in a 540x390 block,
   and 810x557 in 810x557). The panel simply carries one cut corner; the green
   showing through that single cut is the whole interlock. An earlier pass
   here inset the panel with asymmetric padding to leave an L of green down
   two edges, which is not what live does. */
.contact-access__block{background:var(--accent)}
.contact-access__panel{
  background:var(--light-bg-2);
  height:100%;
  padding: var(--space-l);
  display:flex;flex-direction:column;justify-content:center;
}
.contact-access__panel--cut-br{border-radius:0 0 50px 0}   /* Disabled Access */
.contact-access__panel--cut-tl{border-radius:50px 0 0 0}   /* Car Parking */

/* Row 1 right, "Public Transport". Live is not a bare cell: it is a green
   panel radiused 20/20/20/0 - square only at the corner it shares with the
   Disabled Access block, so the pair reads as one interlocked unit. */
/* The Car Parking photo: the declared 770x300 did not match the file's real
   1540x710, so the browser reserved the wrong box and the photo rendered
   flatter than the reference (2.17 against 1.84). The attributes are corrected
   and the aspect pinned, so the reserved space and the rendered shape agree -
   which also stops the layout shifting when the lazy image arrives. */
.contact-access__photo-inline{
  width:100%;height:auto;aspect-ratio:1.84;object-fit:cover;
  border-radius:15px;display:block;margin-top: var(--space-s);
}
.contact-access__cell{padding: var(--space-l)}
.contact-access__cell--transport{
  background-image:radial-gradient(at 0% 100%, var(--accent) 28%, var(--accent) 100%);
  border-radius:20px 20px 20px 0;
}
/* Live's rows are equal-height pairs: 390 on row 1, 557 on row 2. Left to
   shrink-wrap, the Car Parking block ran 9px taller than the photo beside it
   and the pair stopped lining up at the bottom. Stretch both rows instead. */
.contact-access__inner{align-items:stretch}
.contact-access__inner > *:nth-child(1),
.contact-access__inner > *:nth-child(2){min-height:23.6375rem}   /* 390px */
/* :not(.eyebrow__mark) - the 30px brand mark lives inside this panel too and
   a bare `img{width:100%}` blows it up to the full column width. */
.contact-access__panel img:not(.eyebrow__mark){
  width:100%;height:auto;border-radius:20px;margin-top: var(--space-sm);
}
.contact-access__photo{
  position:relative;
  background-size:cover;background-position:50% 50%;background-repeat:no-repeat;
  border-radius:20px 0 20px 20px;
  /* Height derived FROM width via a ratio spacer, not aspect-ratio. In a
     stretched grid row aspect-ratio resolves the WIDTH from the row height,
     which inflated this from its 540 track to 588 and pushed it over the
     column beside it - the same fault that hit the about page's photo. */
  width:100%;
}
.contact-access__photo::after{content:"";display:block;padding-top:calc(557 / 540 * 100%)}
/* The corner overlay, TOP-RIGHT here rather than bottom-right: this photo's
   square corner is its top-right one, where it meets Car Parking's 50px notch,
   so that is the corner that has to melt into the green. Live carries the same
   device on the same pseudo-element, at 10%. Fades to transparent CREAM rather
   than transparent black so the mid-tones do not muddy. */
.contact-access__photo::before{
  content:"";position:absolute;inset:0;z-index:1;
  border-radius:inherit;
  background-image:radial-gradient(at 100% 0%, var(--accent) 14%, rgba(254,249,239,0) 100%);
}
.contact-access h2,.contact-access h3{margin-top:0}
/* Live puts an Elementor divider under the Car Parking heading: a 1px rule
   the full width of the panel copy, 25px below the heading. --rule already
   resolves to rgb(122,144,113) against live's rgb(122,144,114). */
.contact-access__divider{
  border:0;border-top:1px solid var(--rule);
  margin: var(--space-s) 0 var(--space-s);
}
.contact-access ul{margin: var(--space-2xs) 0 0;padding-left: var(--space-l)}   /* live: bullets indent 40px */
.contact-access li{margin-bottom: var(--space-3xs)}

/* Body copy on this page is 18.6/27.8 like the rest of the site's prose; the
   contact sections are custom grids rather than .prose, so they set it here.
   Section headings are 33/42.9 (ratio 1.3). */
.page-contact .contact-detail p,
.page-contact .contact-access p,
.page-contact .contact-access li{font-size:1.1273rem;line-height:1.4946}
.page-contact .contact-detail h4,
.page-contact .contact-access h2,
.page-contact .contact-access h3{line-height:1.3}

@media (max-width:1024px){
  .contact-split__inner,
  .contact-access__inner{grid-template-columns:1fr;gap: var(--space-l)}
  .contact-detail{grid-template-columns:repeat(2,1fr);grid-template-rows:auto auto}
  /* The rows are content-sized from here down, so height:100% has nothing to
     resolve against and collapses the photo to zero. Give it its ratio back
     as a padding spacer (derives height FROM width, so it cannot inflate
     sideways the way aspect-ratio does in a stretched row). */
  .contact-photo{height:auto}
  .contact-photo::after{content:"";display:block;padding-top:calc(289 / 365 * 100%)}
  /* The cards go back to filling their slots once the rows stop being live's
     fixed 289/417 - a fixed card height in a content-sized row either clips
     the copy or leaves a gap. */
  .contact-slot--tr > .contact-cell,
  .contact-slot--bl > .contact-cell{height:auto}
  /* The photo's height comes from a ::after padding spacer, not aspect-ratio
     (see the base rule). Overriding the RATIO here left the spacer in place
     as well, and the two together blew the element out to ~1760px wide and
     overflowed the page. Restate it as the spacer instead. */
  .contact-access__photo::after{padding-top:56.25%}   /* 16/9 */
  .contact-access__panel--cut-br,
  .contact-access__panel--cut-tl{padding: var(--space-l)}
}
@media (max-width:767px){
  .contact-detail{grid-template-columns:1fr;grid-template-rows:none}
  .contact-slot--tr > .contact-cell,
  .contact-slot--bl > .contact-cell{height:auto}
  .contact-photo{border-radius:20px 20px 0 0}
  .contact-photo::after{padding-top:56.25%}   /* 16/9 once stacked */
  .contact-cell--cut-bl,.contact-cell--cut-tr{border-radius:0}
  .contact-access__cell{padding: var(--space-sm) 1.6rem}
  /* The 50px notch goes with the two-column layout that gives it meaning.
     These cuts are half of the "facing corners" device: two diagonally
     opposite cells whose notches face each other across the group's shared
     interior corner. Stacked into one column there is no facing corner and
     no neighbour, so each cut just reads as one wrongly-clipped corner.
     .contact-cell--cut-* above and .about-aim__cell--cut-* both already
     reset for exactly this reason; these two were missed. */
  .contact-access__panel--cut-br,
  .contact-access__panel--cut-tl{
    padding: var(--space-sm) 1.6rem;
    border-radius:0;
  }
}

/* ---- About: the three-layer story composition ----------------------------
   Live y=2736: accent band (1440) -> cream card inset 30px each side with a
   30px radius (1380x949) -> inside it a three-column grid, and an accent card
   (radius 20) overlaid on the lower part of the middle column's photo.
   The layering IS the design; flattening it to plain columns is what made
   this section read as text on a slab. */
.band--accent-full{background:var(--accent)}          /* shared with practice-policy */

/* practice-policy's last band is a full-width accent panel that runs straight
   into the footer, so its own bottom padding just stacked empty green on top
   of the footer's 40px. Measured before: 60px of band padding PLUS a 60px
   footer margin-top = 120px of dead space between the last policy card and
   the footer. Scoped to the LAST section so the same class earlier in a page
   keeps its padding, and to .page-policy's practice-policy rather than the
   privacy/terms pages, which do not use this band. */
/* Bottom padding matching the top, NOT zero. Zeroing it earlier removed the
   band's internal breathing room as well as the gap to the footer, so the
   last line ("Website / hcc.vic.gov.au") sat flush on the green band's edge.
   What had to go was the GAP between band and footer - the footer's own
   margin-top below - not the padding inside the band. */
.page-policy main > section.band--accent-full:last-child{
  /* 3.6366rem = 60/16.4992, the SAME value `main > section.band` gives this
     band's top. Two earlier guesses missed because the top is not what it
     looks like: .prose sets padding-block to --space-xl-2xl (82.5px), but the
     later `main > section.band` rule overrides it to 60. So --space-l-xl gave
     62/30 (right at 1440, half the top on a phone) and --space-xl-2xl gave
     82 against a 60px top. Matching the rule that actually wins is what makes
     the band symmetric at every width. */
  padding-bottom:3.6366rem;
}
body.page-policy:has(main > section.band--accent-full:last-child) .site-footer{margin-top:0}
/* Same treatment as the homepage's wide bands: the CARD runs full width to a
   fixed 50px gutter and keeps growing, while the CONTENT inside it caps at
   1450px. The 1380px max-width this had was a cap on the card itself, so it
   stopped growing and floated - 95.7% of the viewport at 1440 but only 65.6%
   at 2542. Reference 1 shows it near edge-to-edge. */
.about-story{padding-block: var(--space-2xl);padding-inline:50px}
/* Phone: 20px, matching the homepage's wide bands. Same reasoning and the
   same cascade constraint - it must follow the rule above, not sit in the
   phone block higher up the file. */
@media (max-width:767px){
  .about-story{padding-inline:5px}
}
.about-story__card{
  background:var(--light-bg-2);
  border-radius:30px;
  padding: var(--space-2xl) 2.4244rem;                /* 80 / 40 */
  max-width:none;margin-inline:0;
}
.about-story__cols{
  display:grid;
  /* Live is 469 / 481 / 273 with an 80px gutter at 1440. As fixed tracks those
     sum past the container below 1440 and the right column ran off the page
     (1100-1366). Proportional tracks hold live's ratio and scale down. */
  grid-template-columns:minmax(0,469fr) minmax(0,481fr) minmax(0,273fr);
  gap:0 var(--space-l-xl);
  align-items:start;
  /* px, not rem: 81.216rem is on the site's FLUID root, so it grew with the
     viewport (1462px at 1920, 1620px at 2542) instead of holding a ceiling -
     the same drift the homepage bands had. This is the content cap that sits
     inside the full-width card. */
  max-width:1450px;margin-inline:auto;
}
/* Middle column: photo as background, accent card overlaid at its foot. */
.about-story__photo{
  background-size:cover;background-position:50% 50%;background-repeat:no-repeat;
  border-radius:20px;
  min-height:42.9718rem;                              /* 709px, as live */
  display:flex;align-items:flex-end;justify-content:center;
  padding: var(--space-s);                                  /* 20px */
}
.about-story__overlay{
  background:var(--accent);
  border-radius:20px;
  padding: var(--space-s) 1.8183rem;                        /* 20 / 30 */
  width:100%;
}
.about-story__overlay h3{margin-top:0}
.about-story__overlay p:last-child{margin-bottom:0}
.about-story__aside img{
  width:100%;height:auto;border-radius:20px;display:block;
}
.about-story__aside h3{margin-top: var(--space-m)}
.about-story__badge{border-radius:0 !important;margin-top: var(--space-m)}
.about-story__text h3:first-child{margin-top:0}

/* ---- About: the Practice Aim interlock -----------------------------------
   THE INTERLOCK IS AN ILLUSION. There is a GREEN GROUND with PAGE-COLOURED
   HOLES punched in it; the green showing through the two facing 50px notches
   is what reads as interlinked cards. There are no cards that touch.

   Three earlier passes painted the quadrants green and the group cream, which
   is the inversion the owner keeps rejecting: "Focus on Prevention and
   Practice Aim have the light green background and should not." Those two
   cells are CREAM. Only the left column and the bottom-right quadrant carry
   green, and both do it through a gradient, not a flat fill.

   Live's own structure, measured from the computed styles (do not "simplify"
   this into flat backgrounds - the gradients are visible):
     left col   20   560x774  radial-gradient(at 0% 0%, sage 0%, sage 80%)  r20
     ground     660  756x774  CREAM, r0                     <- the panel
     TL         660  378x398  photo, r20 20 0, plus a ::before
                              radial-gradient(at 100% 100%, sage 1%,
                              transparent 100%) - the photo melting into green.
                              We ship this at 5% rather than live's 1%; see
                              the rule itself for why.
     TR-under   1038 378x398  linear-gradient(cream 0%, sage 100%)  r0
     TR-over    1038 378x398  CREAM, r 0 0 0 50px                   <- notch
     BL-under   660  378x376  linear-gradient(sage 0%, cream 65%)   r0
     BL-over    660  378x376  CREAM, r 0 50px 0 0                   <- notch
     BR         1038 378x376  radial-gradient(at 0% 0%, sage 0%, sage 100%)
                              r 0 20px 20px

   Each notched cell is therefore TWO stacked elements (.about-aim__slot holds
   the gradient, .about-aim__cell carries the notch and the copy).

   The 20px and 50px radii are deliberate fixed values transcribed from live,
   not scale steps, so they stay literal. */

/* Live's band inner is 1400 wide (a 1500 cap inside a 20px page gutter), not
   the 1361px site shell: 560 + 80 gap + 756 = 1396 does not fit the shell.
   Scoped to this band so nothing else widens. */
.about-team{
  padding-inline:1.2121rem;               /* live 20px page gutter */
  padding-block:0;                        /* the 120px lives on the inner */
}
.about-team .band__inner{
  max-width:84.8526rem;                   /* live 1400px */
  padding-block:7.2731rem;                /* live 120px */
}

.about-team__grid{
  display:grid;
  grid-template-columns:33.9414rem 45.8206rem;          /* live 560 / 756 */
  gap:4.8487rem;                                        /* live 80px */
  align-items:start;
  justify-content:start;
}

/* Left column: a GREEN GRADIENT, not a flat fill. Live is a radial-gradient
   that holds one colour to 80%, so it reads flat but is not. */
.about-team__card{
  background-image:radial-gradient(at 0% 0%, var(--accent) 0%, var(--accent) 80%);
  background-color:transparent;
  border-radius:20px;
  padding: var(--space-l);                              /* 40px */
  min-height:46.9114rem;                                /* live 774px */
  display:flex;flex-direction:column;
  align-self:start;
}
.about-team__card > *:first-child{margin-top:0}
/* Centred like the quadrants, rather than top-aligned with the button pushed
   to the bottom by margin-top:auto - which is what left the old build with a
   block of dead space in the middle of the column. */
.about-team__card{justify-content:center}
.about-team__card .btn{align-self:flex-start;margin-top: var(--space-s)}

/* .prose h2/h3/h4 win on specificity over .display-title, so inside this band
   they would recolour the headings green and loosen the rhythm - which is what
   they did the moment the old .split wrapper (whose own h2 rules shadowed
   them) was replaced. Restore the display-title treatment explicitly. */
.about-team .display-title{color:var(--heading-brown);margin:0 0 1rem}
.about-team__card .eyebrow{margin:0 0 var(--space-s)}
.about-team__card .name-list{margin: var(--space-s) 0 var(--space-l)}
.about-aim__cell h3.display-title{margin:0 0 var(--space-xs)}
.about-aim__cell h4{margin:0 0 var(--space-xs)}

/* The GROUND. Cream, the same colour as the band - it is what shows through
   the notches as a hole, and it is what the earlier builds were missing
   entirely. */
.about-aim{
  display:grid;
  grid-template-columns:repeat(2,22.9103rem);           /* live 2 x 378px */
  /* DELIBERATE DEPARTURE FROM LIVE, at the owner's request: live's rows are
     unequal (398 top / 376 bottom), which lands the two 50px notches slightly
     off the group's vertical midpoint. Splitting a fixed-height group 1fr/1fr
     makes both rows 387 and the notches meet exactly at the midpoint, which is
     what makes the interlock read cleanly. Do NOT "correct" this back to
     398/376 on a later fidelity pass - the total 774 still matches live. */
  grid-template-rows:1fr 1fr;
  height:46.9114rem;                                    /* live group 774px */
  background:var(--light-bg-2);
  align-items:stretch;
  max-width:100%;
}

/* Top-left: the photograph, with a radial gradient overlay that reaches full
   green at its bottom-right corner. That overlay is what merges the photo
   into the green ground - without it the corner reads as a hard edge. */
.about-aim__photo{
  position:relative;
  background-size:cover;background-position:50% 50%;background-repeat:no-repeat;
  border-radius:20px 20px 0;                            /* live: 20 20 0 */
  min-width:0;
}
.about-aim__photo::before{
  content:"";position:absolute;inset:0;
  border-radius:inherit;
  /* 5%, not live's 1%. DELIBERATE DEPARTURE - do not "correct" it back, and
     do NOT raise it. At 1% the overlay reaches full accent at essentially a
     single point, so the photograph shows through the green along the bottom
     and right EDGES of the photo - which is where the artefact actually is.
     Measured along those two edges (dE from accent, >4 = photo visible):

       stop   bottom edge clean to   right edge clean to
        1%            0px                   0px    <- the reported defect
        5%           ~15px                 ~10px   <- CHOSEN
        8%           ~35px                 ~20px
       12%           ~55px                 ~35px
       18%           ~95px                 ~65px

     5% is the lightest value that clears the visible corner edge (bottom-edge
     dE at 20px drops 15 -> 6, right-edge 21 -> 12). Keeping the photograph
     intact is the priority, and 18% dims substantially more of it than the
     defect warrants.

     This DOES mean the fade is not solid across the full 50px notch arc. That
     was the rationale for a higher stop and it has been deliberately traded
     away - do not cite the notch as a reason to raise this later. An earlier
     18% here came from sampling the box DIAGONAL, which runs through the
     middle of the image and overstated how far the green had to reach.

     (The ::before overlay ITSELF is faithful to live - live carries the same
     radial-gradient on the same pseudo-element. Only the stop differs.) */
  background-image:radial-gradient(at 100% 100%, var(--accent) 5%, rgba(2,1,1,0) 100%);
}

/* The two notched slots: the gradient sits here, the notch sits on the cell
   inside. Both layers are full-bleed to the grid cell, exactly as live
   stacks them at identical coordinates. */
.about-aim__slot{min-width:0;display:grid}
.about-aim__slot > .about-aim__cell{grid-area:1/1}
.about-aim__slot--tr{background-image:linear-gradient(var(--light-bg-2) 0%, var(--accent) 100%)}
.about-aim__slot--bl{background-image:linear-gradient(var(--accent) 0%, var(--light-bg-2) 65%)}

/* Each quadrant centres its own copy vertically. The three cells carry
   different amounts of text (a heading plus one or two paragraphs), so a
   fixed top offset cannot centre them all - and it fails hardest at the
   narrow widths where the wrapping changes. Horizontal padding is unchanged. */
.about-aim__cell{
  padding: var(--space-m);min-width:0;                  /* 30px */
  display:flex;flex-direction:column;justify-content:center;
}
/* CREAM holes - NOT green. This is the whole point of the composition. */
.about-aim__cell--cut-bl{background:var(--light-bg-2);border-radius:0 0 0 50px}  /* Practice Aim, top-right */
.about-aim__cell--cut-tr{background:var(--light-bg-2);border-radius:0 50px 0 0}  /* Focus on Prevention, bottom-left */
/* The one green quadrant: bottom-right, rounded on its three OUTER corners. */
.about-aim__cell--fill{
  background-image:radial-gradient(at 0% 0%, var(--accent) 0%, var(--accent) 100%);
  border-radius:0 20px 20px;
}
.about-aim__cell h3,.about-aim__cell h4{margin-top:0}
.about-aim__cell p:last-child{margin-bottom:0}

/* ---- Responsive ---------------------------------------------------------
   Below the full 1440 layout the fixed tracks stop fitting, so the whole
   composition goes fluid at anything narrower than 1440, then single-column. The notches are dropped once
   the cells no longer sit in a 2x2 - a notch with nothing diagonally
   opposite it just looks like a mistake. */
@media (max-width:1439px){
  .about-team__grid{grid-template-columns:minmax(0,560fr) minmax(0,756fr);gap: var(--space-l-xl)}
  .about-team__card{min-height:0}
  .about-aim{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:1fr 1fr;height:auto}
  /* NOT aspect-ratio. In this block the grid rows are content-driven
     (1fr/1fr on height:auto), and the photo has no content of its own, so
     align-items:stretch gives it whatever height the text cells set.
     aspect-ratio then resolves the photo's WIDTH from that height - the
     dependency runs backwards - and between about 1101 and 1180, where the
     text wraps an extra line and the row grows, the photo inflated up to 35px
     past its own column and over the top-right quadrant. A percentage
     padding-top spacer derives height FROM width, which is the correct
     direction. Same 378/398 - it is live's measured ratio. */
  .about-aim__photo{aspect-ratio:auto;width:100%;height:auto}
  .about-aim__photo::after{content:"";display:block;padding-top:calc(398 / 378 * 100%)}
}
@media (max-width:1100px){
  .about-team__grid{grid-template-columns:minmax(0,1fr);gap: var(--space-l)}
  .about-aim{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:767px){
  .about-team{padding-inline: var(--space-xs)}
  .about-team__card{padding: var(--space-l) 1rem;border-radius:20px}
  .about-aim{grid-template-columns:minmax(0,1fr);grid-template-rows:none;height:auto}
  .about-aim__cell--cut-bl,.about-aim__cell--cut-tr{border-radius:0}
  .about-aim__cell--fill{border-radius:0 0 20px 20px}
  .about-aim__photo{aspect-ratio:16/9;border-radius:20px 20px 0 0}
}
@media (max-width:1024px){
  .about-story__cols{grid-template-columns:1fr;gap: var(--space-l)}
  .about-story__photo{min-height:26rem}
}
@media (max-width:767px){
  /* A 50px gutter would take a quarter of a phone screen. */
  .about-story{padding-inline: var(--space-xs)}
  .about-story__card{padding: var(--space-l) 1rem;border-radius:20px}
}

/* Live's two about banners are 560x500 (1.12:1), not letterboxes; the source
   files are now cropped to that ratio, so cap the rendered width to 560 and
   let the ratio hold rather than stretching to the column. */
.page-about .split__media img{max-width:33.9411rem;margin-inline:auto}   /* 560px */
/* The MEET THE MEDICAL TEAM band, transcribed from the live geometry at

/* About hero card: live is 512 wide at x=818 with a 20px radius (not the
   shared --radius-lg), and taller than the site default because it carries
   the eyebrow, a 57.7px title, body copy and the CTA. */
.page-about .phero__card{
  max-width:min(100%,31.0318rem);   /* 512px */
  border-radius:20px;
  padding: var(--space-l) 3.0305rem;
}
/* Modern Systems card: live 421x270 at x=616, i.e. inset 30px from the 481
   photo column rather than the 20px default. */
.about-story__photo{padding: var(--space-m)}                /* 30px */
.about-story__overlay{padding: var(--space-m)}              /* 30px */

/* Live's lead trio is not three equal thirds: the right-hand aside is 437px
   (x=963) and the two text columns share the rest. Equal thirds gave 424. */
@media (min-width:1025px){
  .trio--lead{grid-template-columns:1fr 1fr 26.4861rem}   /* 437px */
}

/* Contact hero rhythm, measured against live: the gaps inside the card are a
   little wider than the site default, and the band below the CTA is much
   deeper - live leaves 933px between the button and the map band against the
   763 the shared padding gave. */
.page-contact .phero__card .eyebrow{margin-bottom: var(--space-s)}      /* live 20px */
.page-contact .phero__card .display-title{margin-bottom: var(--space-s)}
/* NOT live's 933px gap below the CTA. That produced an 842px hero around a
   419px card - more empty ground than content, and past the fold on a laptop.
   The hero's padding is now the single value set earlier in this file
   (~75px a side); this rule is deliberately left out rather than reinstated
   with a smaller number, so there is one place that sets it. */

/* Service-page lead images: live is consistently 437px wide with a per-page
   height (cosmetic 382, ear 491, skin/telehealth 655), so the ratio is set
   per page rather than squared off or forced to one value. */
.page-cosmetic .trio__aside .split__media img{aspect-ratio:437/382;object-fit:cover}
.page-ear      .trio__aside .split__media img{aspect-ratio:437/491;object-fit:cover}
.page-skin     .trio__aside .split__media img{aspect-ratio:437/655;object-fit:cover}
.page-tele     .trio__aside .split__media img{aspect-ratio:437/655;object-fit:cover}

/* Live lists the doctors in TWO columns (names at x=60 and x=310), not one.

   GRID, not CSS multi-column. `columns:2` balances by HEIGHT and fills the
   first column first, which with nine names gave 4 in the left column and 5
   in the right - the owner wants the SHORT column second. It also left the
   two columns 5-6px out of vertical step with each other on every row,
   because the per-item bottom borders are distributed differently in each
   balanced column. An explicit 5-row grid flowing down-then-across fixes
   both: 5/4, and every shared row at an identical y. */
.about-team .name-list{
  columns:auto;
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  grid-template-rows:repeat(5,auto);
  grid-auto-flow:column;
  column-gap: var(--space-l);
}
.about-team .name-list li{break-inside:avoid}
@media (max-width:600px){
  .about-team .name-list{
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:none;
    grid-auto-flow:row;
  }
}

/* ---- forms-and-downloads: document cards ------------------------
   Live groups ALL SIX cards into ONE sage band (y=942 h=827) as two rows
   of three, each card carrying a 50px circular icon badge above a 25px
   heading. An earlier build split them across two bands, dropped every
   badge, and replaced the ACCESS FORM buttons with static text - the
   documents were assumed lost because they are absent from the static
   export, but all ten are live on the WordPress origin and are now
   carried in assets/docs/. */
.doc-grid{row-gap:var(--space-xl)}
.doc-card__icon{
  display:flex;align-items:center;justify-content:center;
  width:3.0304rem;height:3.0304rem;   /* live 50px */
  padding:0.7576rem;                   /* live 12.5px */
  margin-bottom:var(--space-s);
  background:var(--primary);border-radius:50%;
}
.doc-card__icon svg{width:100%;height:100%;fill:var(--light-bg-1);display:block}
/* A two-card row inside a split column: live renders MyHealthRecord as two
   across (x=175/438), not three. */
.split .trio.trio--pair{grid-template-columns:repeat(2,1fr)}
@media (max-width:1024px){
  .split .trio.trio--pair{grid-template-columns:1fr}
}

/* forms-and-downloads bands run a taller rhythm than the site default: live
   uses 120px block padding on the three lower bands (vs the 80px elsewhere)
   and a 614-624px media card, against the ~395px the shared min-height gives.
   Scoped to the page so the other splits keep the default. */
.page-forms .band.prose:has(.split){padding-block:3.6366rem}   /* was --space-2xl-3xl (123.7px), a 247px gap between two of them */
.page-forms .split__media--bg{min-height:clamp(18rem,12rem + 22vw,38.5rem)}
.page-forms .hero-media img{width:100%;height:auto;border-radius:var(--radius-md);display:block}

/* forms-and-downloads hero: live gives the text column 556px and the photo
   800x534 running to the right viewport edge (x=623..1423), in a 774px band.
   The shared 1.35fr/1fr split put the photo at 560x373 and left the band
   236px short. */
.page-forms .band:not(.prose){padding-block:3.6366rem;padding-right:0}
.page-forms .band:not(.prose) .split--text-wide{
  grid-template-columns:minmax(0,556fr) minmax(0,800fr);
  gap:var(--space-l);align-items:center;
}
.page-forms .band:not(.prose) .split__media img{
  border-radius:var(--radius-md) 0 0 var(--radius-md);
}
/* The HERO split stays side-by-side at tablet - live measures 773px at 1024,
   the same as at 1440, so it does not stack until phone. Only the two lower
   splits stack at 1024. */
@media (max-width:767px){
  .page-forms .band:not(.prose){padding-right:var(--pad-x)}
  .page-forms .band:not(.prose) .split--text-wide{grid-template-columns:1fr}
  .page-forms .band:not(.prose) .split__media img{border-radius:var(--radius-md)}
}
/* Live drops the six document cards from 3-across to 2-across at tablet. */
@media (max-width:1024px){
  .doc-grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:600px){
  .doc-grid{grid-template-columns:1fr}
}

/* ---- Legal documents (privacy-policy, terms-and-conditions) -------------
   Live sets these two pages on a DIFFERENT type scale to the rest of the
   site: body is 16.4992px (1rem at this fluid root) with 24.75 leading and
   14.85 paragraph spacing, not the 19/30.4/19 the general .prose default
   gives. Section headings are 41.248px (2.5rem) and subheadings 32.9984px
   (2rem), against the 33/25 .prose otherwise applies.

   Inheriting the general scale made privacy-policy 35% taller than live and
   terms-and-conditions 30% taller - the pages are long single-column
   documents, so every per-paragraph excess compounds down the page.

   Levels are deliberately NOT changed to match live. Live uses h3/h4 under
   an h2 with no h1 on the page; the rebuild uses h1/h2/h3, which is the
   correct document outline. Per the migration README, the tag carries
   semantics and the class carries appearance - so the SIZE moves here and
   the level stays. Do not \"fix\" these back to h3/h4.
   Scoped to .prose__body, which only these two pages use. */
.prose__body{font-size:1rem}
.prose__body p{font-size:1rem;line-height:1.5;margin:0 0 0.9rem}
.prose__body ul,.prose__body ol{font-size:1rem;line-height:1.5;margin:0 0 0.9rem}
.prose__body li{margin-bottom:0}
/* The page title: live 49.4977px (3rem at this root). It is an h2 on live
   (that page has no h1) and an h1 here, which is the correct outline - so
   the size is set by rule rather than by matching the tag. */
.prose__body h1{font-size:3rem;line-height:1.2;margin:0 0 1rem}
.prose__body h2{font-size:2.5rem;line-height:1.2;margin: var(--space-2xs) 0 1rem}
.prose__body h3{font-size:2rem;line-height:1.3;margin: var(--space-2xs) 0 1rem}
.prose__body h4{font-size:1.5rem;line-height:1.5;margin: var(--space-2xs) 0 0.5rem}

/* ---- Legal-document card composition ------------------------------------
   Both legal pages share one composition on the live site, and neither the
   accent hero band nor the plain prose band the rebuild used resembles it:

     grey ground  #F5F5F5  full width, behind everything
     hero         1000px tall - a cover photo on terms, plain ground on
                  privacy (privacy has no hero image on live)
     sage card    x=75 w=1290 (i.e. 1290/1440 of the viewport), 100px pad,
                  20px radius on privacy / 15px on terms, pulled UP 300px so
                  it overlaps the hero, carrying the title and the whole
                  document

   The card is the page: everything from the title to the last paragraph
   sits inside it. */
/* .legal carries .prose for its typography, but must NOT inherit .prose's
   block padding: live starts the hero flush against the header. */
.legal{background:var(--legal-ground);padding-block:0 var(--space-2xl)}
.legal__hero{
  height:60.61rem;   /* live 1000px at 1440 (root 16.4992px) */
  background-size:cover;background-position:50% 50%;
}
.legal__card{
  position:relative;z-index:var(--layer-raised);
  width:min(89.6%,calc(var(--shell) + 6.5rem));   /* live 1290 of 1440 */
  margin:-18.18rem auto 0;                         /* live: 300px up into the hero */
  padding: var(--space-2lg);                                 /* live 100px */
  background:var(--accent);
  border-radius:var(--radius-md);
}
/* Live steps DOWN at tablet rather than scaling: the hero becomes a fixed
   500px and the card padding 70px, with the card inset 40px. Measured at
   1024 and 768, both identical. */
@media (max-width:1024px){
  .legal__hero{height:500px}
  .legal__card{
    width:calc(100% - 80px);   /* live 40px each side */
    margin-top:-100px;         /* live: card top 528 under a hero ending 628 */
    padding:70px;              /* live 70px */
  }
}
.legal__card > .prose__body{max-width:none}
/* Live steps again at phone: card inset 20px each side with 50px/30px
   padding. Measured at 390 and 320, identical. */
@media (max-width:600px){
  .legal__hero{height:260px}
  .legal__card{
    width:calc(100% - 40px);
    margin-top:-60px;
    padding:50px 30px;
  }
}




/* ---- BOOK APPOINTMENT: the whitespace BELOW it, matched per page ---------
   The gap ABOVE is 35px sitewide on live and is already set by
   .phero__card > .btn. The gap BELOW is not uniform - live varies it from
   21px to 430px - so each hero is matched to its own live counterpart rather
   than normalised to one value, per the brief.

   Measured at 1440 as the distance from the button's bottom edge to the top
   of the nearest painted content below it:

     page              live   before   how it is closed
     practitioners      360     271     +89 on the hero
     about              430     270    +160 on the hero
     fees               305     395     -90 on the band below
     contact            310     459    -149 on the band below
     booking-info        21     271    -250 on the band below
     practice-policy     50     165    -115 on the band below

   The gap is the hero's padding-bottom PLUS the following band's
   padding-top, so where live wants a smaller gap than the shared rhythm
   gives, the reduction has to come off the band below - a negative padding
   on the hero is not a thing. Desktop only; a phone keeps the normal
   rhythm, where these long hero gaps would just be dead space. */
@media (min-width:768px){
  /* Live carries 360px and 430px of hero padding-bottom here. That padding,
     not the min-height cap above, is what actually sets these heroes' height:
     the cap only binds when the CONTENT is shorter than it, and at 430px of
     padding About's content never was. Both are cut to a single shared value
     (~120px) so the hero ends a reasonable distance below its card instead of
     holding a third of a screen of empty ground. 7.2731rem = 120 / 16.4992. */
  /* +50px, as with the other heroes. These two are CONTENT-driven - their
     cards exceed the min-height above - so the extra has to come from padding
     or nothing changes. 7.2731 + 3.0304 = 10.3035rem. */
  .page-practitioners .phero{padding-bottom:10.3035rem}
  .page-about .phero{padding-bottom:10.3035rem}

  /* fees and contact have TALL hero cards (645px on fees), so their height is
     set by content, not by the min-height cap - the default 82.5px of hero
     padding on each side then pushes the card's last line past an 800px fold.
     Cut to 60px: at 40px the card came within a hair of the photo band's top
     and bottom edges, so the hero read as a card with a photo squeezed behind
     it rather than a photographic hero. 50px, not more: this card carries the
     most copy of any hero here (two ledes plus a tick list), and the CTA
     crosses an 800px fold at 55px. Measured, band -> CTA bottom at 1440x800:
     40->786, 45->791, 50->796, 55->801, 60->806. 50 is the ceiling that both
     lets the photo breathe and keeps the booking button on a laptop screen.
     3.0304rem = 50 / 16.4992. */
  .page-fees .phero{padding-block:3.0304rem}
  /* 35px, down from the 40px asked for. Once the BAND went to 60px above,
     40px inside the card put the CTA at 811px - below an 800px fold, which is
     the exact problem the original 30px existed to avoid. 35px holds the
     button on screen while staying close to the 41px its neighbours use.
     2.1213rem = 35 / 16.4992. */
  .page-fees .phero__card{padding:2.1213rem}

  /* Tighter than the default: take it off the band that follows */
  .page-fees .phero + .band{padding-top:3.4737rem}        /* live 305 */
  /* NOT zeroed. Measured on live at 1440, hero-bottom -> map-card-top is
     120px - the same rhythm as every other gap on this page. The "live 310"
     figure in the table above measured the hero SECTION's bottom, which
     includes the hero's own padding, not the visible gap. Zeroing this
     butted the map card straight against the hero. The band's own
     padding-block (--space-2xl-3xl) is what live actually has. */
  .page-booking-info .phero + .band{padding-top:0}        /* live 21 */
  .page-booking-info .phero{padding-bottom:0}
  /* Practice Policies opens with ~120px of air above its eyebrow, per
     reference 3. The other .page-policy pages (privacy, terms) keep the live
     site's 0. 7.2731rem = 120 / 16.4992. */
  .page-policy main > .band.prose:first-of-type{padding-top:0}  /* live 50 */
  .page-policy main > .band.policy-hero:first-of-type{padding-top:7.2731rem}
  .page-policy .plain-hero,.page-policy main > .band:first-of-type{padding-bottom:0}
}

/* Live gives its 25px column headings 37.1232px of leading (1.5), not the
   1.3 the rebuild had applied - 34 runs across 10 pages measured wrong on
   that one number alone.

   A bare .fs-25{line-height:1.5} does NOT do it: .trio h2:not(.display-title)
   and .prose h2 both out-specify a single class, so they kept winning at 1.3.
   The leading has to be set on a selector that carries the size class AND the
   context, which is what these do. */
.prose h2.fs-25,.prose h3.fs-25,.prose h4.fs-25,.prose h5.fs-25,.prose h6.fs-25,
.trio h2.fs-25,.trio h3.fs-25,.trio h4.fs-25,.trio h5.fs-25,.trio h6.fs-25,
.split h2.fs-25,.split h3.fs-25,.split > div > h2.fs-25,
.split .trio h2.fs-25,
.fs-25{line-height:1.5}


/* Live drops to 16.4992px/24.7488 for body copy INSIDE a card - the team band's
   cards, the fee and policy cards, the service-page lists - while keeping
   19px/30.4 for band-level prose. The rebuild used 19px throughout, which is
   16 measured runs across five pages. Scoped to the carded contexts. */
.about-team__card p,
.about-aim__cell p,
.about-aim__cell li{font-size:1rem;line-height:1.5}


/* The Cookie Policy half of the terms page is a SECOND document title on live,
   rendered at the same 49.4977px as "Website Terms" rather than as a section
   within it - so everything under it sits one size larger than its nesting
   depth implies. Levels stay correct for the outline (h2 under h1, h3 under
   h2); the size is carried by these classes, per the tag-carries-semantics /
   class-carries-appearance rule. */
.prose__body h2.legal-title{font-size:3rem;line-height:1.2}
.prose__body h3.legal-h2{font-size:2.5rem;line-height:1.2;margin: var(--space-2xs) 0 1rem}
.prose__body h4.legal-h3{font-size:2rem;line-height:1.3;font-weight:700;margin: var(--space-2xs) 0 1rem}


/* Live runs THREE button treatments, not one, and they are chosen per page
   rather than by role:

     18.1491 / 600 / 27.2237   the header and footer BOOK APPOINTMENT
     19.7991 / 600 / 29.6986   most in-content buttons (the homepage's
                               BOOK APPOINTMENT, LEARN MORE, MEET THE
                               DOCTORS & NURSES)
     18.1491 / 700 / 25.4088   the document buttons on forms-and-downloads
                               (ACCESS FORM, VIEW FILE, VISIT MEDICARE)

   The shared default stays on the 19.8/600 middle treatment, which is what
   most pages use. Do NOT make the forms-page treatment the global default:
   doing that regressed all four homepage buttons at once. */
.page-forms .btn{font-size:1.0999rem;font-weight:700;line-height:1.4}   /* 18.1491/700/25.4 */
.site-header__cta,.site-footer__cta{font-size:1.0999rem;font-weight:600;line-height:1.5}


/* The interlock cards' headings are 700 on live, not the 600 the shared
   .fs-33 utility gives (Focus on Prevention, Empowering Patients). */
.about-aim__cell h3,.about-aim__cell h4{font-weight:700}


/* ---- Hero depth, matched per page --------------------------------------
   Live gives each photo hero its own height, and every rebuild hero was short
   of it - about by 378px, booking-info by 439px. That is what the visual band
   comparison was picking up: the content matches, but everything below a short
   hero sits high, so the whole page misaligns proportionally.

     page            live hero   was
     about              1248      870
     practitioners      1021      762
     fees                926      810
     booking-info        931      492

   Setting the hero HEIGHT rather than its padding also delivers the CTA gap
   below the button that the per-page padding above was buying: the card stays
   centred as live has it, and the surplus falls below it.

   Desktop only - a phone sizes the hero to its content. */
/* CAPPED AT 72vh. The transcribed heights are taken from a live site that
   does not care whether its hero clears the fold: at 1248px, About's hero card
   and its button were still below the bottom of a 1440x800 laptop viewport
   once the 168px header is subtracted, so the page opened on a photo with no
   visible call to action. min() keeps each page's own transcribed height on a
   tall display and yields to 72vh on a short one, which leaves the next band
   just peeking - enough to invite the scroll. The per-page differences the
   live site has are preserved wherever the viewport can afford them. */
@media (min-width:768px){
  /* +50px on every hero, at the owner's request. Expressed as a floor added to
     the capped value rather than by raising the cap, so the 72vh ceiling still
     protects short viewports. 3.0304rem = 50 / 16.4992. */
  .page-about .phero{min-height:calc(min(75.64rem, 72vh) + 3.0304rem)}
  .page-practitioners .phero{min-height:calc(min(61.88rem, 72vh) + 3.0304rem)}
  /* NOT given a min-height. Every other hero here is capped so a short card
     still fills a reasonable band, but fees has the tallest card on the site
     and its height is content-driven. A min-height of 698px against a 564px
     card left 67px of ground above and below - measured - which read as the
     40px padding-block below not applying at all. It was applying; the
     min-height was simply taller than padding + content, and the surplus
     split evenly. Dropping the floor lets the padding actually set the gap. */
  .page-booking-info .phero{min-height:calc(min(56.43rem, 72vh) + 3.0304rem)}
  .page-contact .phero{min-height:calc(34.5rem + 3.0304rem)}

}


/* The About history card's body copy is 18.5616px/27.8424 on live - the site's
   normal band-level body size - but it was inheriting 16.4992 here, which is
   the smaller CARD size. Both sizes are real on this site; this block is the
   larger one. Six measured runs. */
.about-story__text p,
.about-story__text li,
.about-story__overlay p,
.about-story__aside p{font-size:1.1250rem;line-height:1.5}   /* 18.5616 / 27.84 */

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

/* .eyebrow is display:flex, so the text-align:center on .notfound does not
   centre it - a flex container aligns its children with justify-content.
   Same trap, and the same fix, as .policy-hero .eyebrow above. */
.notfound .eyebrow { justify-content: center; }

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