/* ==========================================================================
   Melbourne Echo Clinic - Components
   --------------------------------------------------------------------------
   Depends on tokens.css and base.css. Load third.
   All colours, fonts and spacing reference tokens - no hard-coded values.
   ========================================================================== */

/* ==========================================================================
   Display typography - ONE TIER, no override needed
   --------------------------------------------------------------------------
   There was a "serif tier" block here for part of 2026-08-13. It set
   .display--hero, .section-title, .page-hero__title, .sec-head__title and
   .cta-panel__title in Instrument Serif at weight 400. It was withdrawn the
   same day on request: headings are to use the same face as the rest of the
   site, in bold.

   Deleting the block IS the revert - there is nothing to replace it with.
   Every one of those five selectors already resolves to Instrument Sans 700
   without any help from this stylesheet:

     - .display--hero, .page-hero__title, .sec-head__title and .cta-panel__title
       are all authored with the `display` class alongside, and base.css sets
       `.display { font-family: var(--font-display); font-weight: 700;
       letter-spacing: -0.02em }`.
     - .section-title is used bare, on an <h2> - base.css's `h1, h2, h3, h4, h5,
       h6` rule sets the identical three properties, so it lands in the same
       place by a different route.

   700 is the ceiling, not a choice. partials/head.html requests Instrument
   Sans as `wght@400..700`, so there is no heavier weight available; asking for
   800 would get a synthesised faux-bold. If headings need to shout louder than
   this, the lever is size or colour, not weight.

   The tracking that went with the serif (+0.01em, to open up a narrow face)
   goes with it. These are back on base.css's -0.02em, which is the value tuned
   for this sans at 700.

   A companion rule setting `font-style: normal` on <em> inside three of those
   titles went too. It existed only because a real serif italic was loading and
   could render mid-headline. base.css's `.display em` already covers all of
   them, so it was redundant the moment the serif left.
   ========================================================================== */

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--space-xs) var(--space-md);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

/* White on --mec-blue is 4.15:1 - below AA. --mec-blue-deep gives 7.31:1.
   This is the Book Now button, the most important control on the site. */
.btn--primary {
  background-color: var(--mec-blue-deep);
  color: var(--white);
}

.btn--primary:hover {
  background-color: var(--mec-navy);
  color: var(--white);
}

.btn--secondary {
  background-color: transparent;
  color: var(--mec-blue-deep);
  border-color: var(--rule);
}

.btn--secondary:hover {
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
  border-color: var(--mec-blue);
}

/* ==========================================================================
   Site header
   ========================================================================== */

.site-header {
  background-color: var(--paper);
  border-bottom: 1px solid var(--rule);
  position: sticky;
  top: 0;
  z-index: 50;
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: 72px;
}

.site-header__brand {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* height drives the size and width follows the intrinsic ratio, so the markup's
   width/height attributes only ever reserve space - they never fight this.

   52px -> 64px on request, 2026-08-15. THE HEADER IS STILL 73px TALL and that
   is the constraint that matters: .site-header__inner has min-height 72px, and
   four scroll-margin-top values elsewhere are calculated from 73px (72 + the
   1px bottom border) - .team-member, .info-block[id] and .faq-section, twice.
   Grow the box past 72 and every one of those silently drops its anchor target
   under the sticky header.

   64 fits because the artwork is padded. mec-logo-full-400.png is 400x127 with
   an alpha bbox of (2, 11, 394, 113), i.e. ~20% of its height is transparent -
   so a 64px box renders about 51px of visible mark with ~5.5px of its own air
   on top and ~7px underneath. Added to the 4px the flex box leaves either side,
   the mark clears the header edges by roughly 10px optically even though the
   raw numbers look tight. MEASURED FROM THE FILE, not estimated; if the logo
   asset is ever recut with less padding this number has to be rechecked. */
.site-header__logo {
  width: auto;
  height: 64px;
}

/* Below this breakpoint the nav collapses to the toggle and the logo competes
   with it for WIDTH, not height - so mobile stays a step down. 44px -> 52px,
   the same proportional increase as the desktop size. At 52px the mark is 164px
   wide, which leaves the toggle and its gap comfortable inside a 375px
   viewport. */
@media (max-width: 40em) {
  .site-header__logo {
    height: 52px;
  }
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__list a {
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
}

.site-nav__list a:hover {
  color: var(--mec-blue-deep);
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

.site-nav__list a.btn--primary {
  color: var(--white);
  text-decoration: none;
}

/* Current page indicator. Matches the attribute regardless of value, because
   build.js emits two of them: "page" on the page itself, and "true" on the
   section ancestor (a location sub-page marks Locations, which is where it
   lives but is not the page you are on). Visually they mean the same thing -
   "this nav item is the one you're under" - so they share a style. The
   distinction is for screen readers, which announce "current page" for one and
   "current" for the other. */
.site-nav__list a[aria-current] {
  color: var(--mec-blue-deep);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.35em;
}

.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
}

.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 20px;
  height: 2px;
  background-color: var(--ink);
  content: '';
  transition: transform 0.15s ease;
}

.nav-toggle__bars {
  position: relative;
}

.nav-toggle__bars::before { position: absolute; top: -6px; }
.nav-toggle__bars::after  { position: absolute; top: 6px; }

@media (max-width: 60em) {
  .nav-toggle { display: inline-flex; }

  .site-nav {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    background-color: var(--paper);
    border-bottom: 1px solid var(--rule);
    padding: var(--space-md);
  }

  .site-nav.is-open { display: block; }

  .site-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-sm);
  }

  .site-nav__list a { display: block; padding: var(--space-2xs) 0; }
  .site-nav__list a.btn { justify-content: center; padding: var(--space-xs) var(--space-md); }
}

/* ==========================================================================
   Page hero
   ========================================================================== */

.page-hero {
  padding-block: var(--space-2xl) var(--space-xl);
  background-color: var(--paper);
}

/* "Bands own the page's vertical rhythm" - see the .band block. This is the one
   place that was breaking it: the hero paid --space-xl and the band below paid
   --space-section on top, so /for-practices, /for-practices/refer and the three
   /for-patients sub-pages opened with 144px of blank paper between the intro
   paragraph and the first heading. Reduced on request 2026-08-15 by giving the
   gap back to the band entirely - 96px desktop, and 56px on mobile where
   --space-section drops, which the old stacked pair could not do.

   THE :not()s ARE THE POINT, do not simplify to `.page-hero + .band`. On a
   tinted or navy band the hero's bottom padding is not dead space, it is the
   run of paper before the colour changes; collapsing it there pulls the band
   edge up under the paragraph. /about and /for-patients are the two pages that
   depend on this and both were deliberately left alone.

   :has() rather than a rule on the band, so the gap keeps scaling with
   --space-section instead of being pinned to a fixed value. If a browser
   without :has() ever matters the whole rule drops and the old 144px comes
   back, which is the pre-2026-08-15 layout - loose, but not broken. */
.page-hero:has(+ .band:not(.band--tint):not(.band--navy)) {
  padding-bottom: 0;
}

/* Second pass, same day: 96px still read as a void, so the band's own top
   padding comes down too. 144 -> 96 -> 64.

   min() rather than a flat --space-2xl so this can never be LARGER than the
   band's normal rhythm. --space-section drops to 3.5rem under 40em (base.css)
   while --space-2xl is a fixed 4rem, so a flat value would have quietly grown
   the mobile gap from 56px to 64px while shrinking the desktop one. This
   resolves to 64px on desktop and stays at 56px on mobile.

   Selector repeated rather than folded into the rule above: that one is on the
   hero and this one is on the band, so they cannot be one declaration block.
   Keep the two :not()s identical in both - if a third band modifier is ever
   added it has to be excluded from both or the pair will disagree. */
.page-hero + .band:not(.band--tint):not(.band--navy) {
  padding-top: min(var(--space-2xl), var(--space-section));
}

.page-hero__title {
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  line-height: 1.05;
  margin-bottom: var(--space-md);
}

/* 58ch, not 65ch. `ch` is the advance width of "0", and in Inter that is
   noticeably wider than the average glyph, so a 65ch cap was actually setting
   ~80 characters to the line - measured 2026-08-15 by walking a Range through
   the text node, not estimated. That is past the 45-75 the cap exists to hold.
   58ch measures ~72. Site-wide on purpose: every hub and sub-page hero was
   over, and a per-page override would fork the token to fix one of them. */
.page-hero__intro {
  font-size: 1.125rem;
  color: var(--ink-soft);
  max-width: 58ch;
}

/* A hero can carry more than one intro paragraph - see `lead: true` in
   build.js. base.css zeroes every <p> margin, so without this the second one
   would sit hard against the first. */
.page-hero__intro + .page-hero__intro { margin-top: var(--space-md); }

/* THE HUB HERO IS SINGLE COLUMN. It carried a second slot for one day on
   2026-08-15 - first a bordered content panel (.page-hero--split), then an
   illustration (.page-hero--art) - and both were reversed; see the note in
   buildHub. The rules for both are gone, and this stays a single column: what
   sits beside the intro on /for-practices is .page-hero__row below, which
   splits one row rather than the whole hero. */
.page-hero__inner { display: grid; gap: var(--space-xl); }

/* Intro paragraph and the section's routing card, side by side. /for-practices
   only - `heroCard` in build.js is the sole trigger.

   THE ROW HOLDS THE INTRO, NOT THE WHOLE LEDE. The h1 above it stays full
   width because it needs to: measured at 1440px it runs to within 81px of the
   container edge, so putting it in a column would wrap "across Victoria." onto
   a third line. Only the intro has width to spare, being capped at 58ch.

   Single column below 60rem, so on a phone the card stacks under the intro in
   the order it is written - the same order it had when it sat at the foot of
   the page, just higher up.

   ALIGNMENT IS CENTRE, ON REQUEST 2026-09-04. A first pass pinned the controls
   to the top of the row, which hung them off the end of "Victoria." rather than
   placing them in the space. The card is taller than the four-line intro, so it
   sets the row height and the intro centres against it; that is what makes the
   two columns read as a balanced pair instead of one block with something stuck
   beside it. */
.page-hero__row {
  display: grid;
  gap: var(--space-lg) var(--space-xl);
  align-items: center;
}

/* 27rem (432px), not `auto` and not a fraction. Measured at 1440px the space
   beside the 58ch intro is 460px, so this fills it with the gutter intact, and
   minmax lets it give width back rather than overflow as the viewport narrows
   toward the 60rem breakpoint. A 1fr second track would instead have taken half
   the row and squeezed the intro well under its cap. */
@media (min-width: 60rem) {
  .page-hero__row { grid-template-columns: minmax(0, 1fr) minmax(0, 27rem); }
}

/* The card's wrapper is a list of one, so it needs the list reset that
   .nav-grid would normally provide. It deliberately does NOT get .nav-grid
   itself - see the note in buildHub for the two rules there that would break
   the card at this width. */
.page-hero__card {
  display: grid;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Nudged down 20px so the card reads as centred in the blank space rather than
   sitting high in it. Geometric centring is not optical centring here:
   `.page-hero:has(+ .band:not(.band--tint):not(.band--navy))` zeroes the hero's
   bottom padding, so the card ended flush with the hero edge while the band
   below still contributed its own 64px padding-top. Measured at 1512px that
   left 24px of clear space above the card and 64px below; moving it down by
   half the 40px difference splits the space evenly, 44px each side.

   THE NEGATIVE BOTTOM MARGIN IS THE POINT, not a hack to undo. Margin-top
   alone grows the row, which grows the hero, which pushes the band down with
   it - so the gap below stayed at 64px and the centred intro drifted 10px down
   with the taller row. Cancelling the growth keeps the row, the hero, the h1
   and the intro exactly where they were and moves only the card. It overhangs
   the hero by 20px, which is safe here: the band below has a transparent
   background and neither element clips or creates a stacking context.

   THE SELECTOR NEEDS THE TYPE AND THE ATTRIBUTE. A bare `.page-hero__card` is
   (0,1,0) and loses outright to base.css `ul[role="list"] { margin: 0 }` at
   (0,2,1), which also matches this element - so a first attempt parsed fine,
   matched fine, and silently did nothing. `ul[role="list"].page-hero__card` is
   (0,2,1) too, and styles.css loads after base.css, so source order settles the
   tie. That is the same mechanism described in the cascade note further down
   this file, and the reason no !important is needed.

   Two-column widths only, so the stacked mobile order is untouched. */
@media (min-width: 60rem) {
  ul[role="list"].page-hero__card {
    margin-top: 20px;
    margin-bottom: -20px;
  }
}

.eyebrow {
  margin-bottom: var(--space-sm);
}

/* An eyebrow is a <p>, so inside a content block `.info-block p` (0,1,1) beat
   `.eyebrow` (0,1,0) and painted it --ink-soft. It looked like the eyebrow had
   simply not been given a colour; it had, and was being overridden. This wins
   at (0,2,0) and puts it back on the site-wide --mec-blue-deep, 6.57:1 on
   --band-tint.

   Deliberately NOT written as `.info-block p:not(.eyebrow)` on the existing
   rule: that would raise its specificity to (0,2,1) and change which rules it
   beats everywhere else it applies.

   Must stay ABOVE `.band--navy .eyebrow`, which is also (0,2,0) and therefore
   only wins on source order. An info-block inside a navy band needs the sky
   colour, not this one. */
.info-block .eyebrow {
  color: var(--mec-blue-deep);
}

/* ==========================================================================
   Map
   ========================================================================== */

.map-section {
  padding-block: 0 var(--space-section);
}

/* Blue hairline + soft blue glow, the same treatment .team-member__media carries.
   That one was lifted verbatim from the live site's .echo-box-prominent, so this
   keeps the two framed-media elements on the site consistent rather than giving
   the map a bespoke edge. It replaced a 1px var(--rule) border for the reason
   documented on .team-member__media: warm grey against blue reads as a rendering
   fault, and the map's tiles are predominantly blue-grey.

   THE FIRST LAYER IS NOT A SPREAD RING. A 6px spread ring plus a diffuse layer
   was tried on the team frames and rejected as too heavy - see the note above
   .team-member__media. This is 24px of blur at zero offset and zero spread,
   which is diffuse and has no edge of its own. The map is a full-width 480px
   rectangle, so the 0 12px 30px layer alone (which is all the team circles need,
   being small enough that a 12px offset still reads as surrounding them) would
   pool under the bottom edge and leave the top and sides bare. The request was
   for a glow AROUND the border, hence the unoffset layer.

   Second layer is 1:1 with .echo-box-prominent. Both layers are a color-mix of
   --signal-blue, so no new hex enters the palette. If color-mix is unsupported
   the border and shadow are both dropped and the frame renders flat against
   --mec-blue-soft, which is a degradation and not a break. */
.map-frame {
  position: relative;
  border: 1px solid var(--glow-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-color: var(--mec-blue-soft);
  box-shadow: var(--glow-shadow);
}

#locations-map {
  width: 100%;
  height: 480px;
}

@media (max-width: 40em) {
  #locations-map { height: 340px; }

  /* At 325×340 an open popup covers most of the frame, so the zoom buttons -
     which Leaflet always paints above popups - land on top of the popup's own
     "View details" and "Get directions" links and swallow the taps. No corner
     is safe at this size; the map is simply too small to hold both.

     Removing the control does not remove zooming: pinch and double-tap work
     on touch, and Leaflet's keyboard handler still zooms with +/- when the
     map has focus. display:none also takes the buttons out of the tab order,
     so no invisible focus stop is left behind. */
  .map-frame .leaflet-control-zoom { display: none; }
}

/* Leaflet paints its own controls and panes above the tiles at z-index 400–800
   and the map is inside an `overflow: hidden` rounded frame, so nothing here
   needs to fight it. The one thing worth pinning is the stacking context: the
   sticky location aside sits at the top of its own context, and without this
   the map's zoom control can render over a sticky header on scroll. */
.map-frame .leaflet-container {
  font-family: var(--font-body);
  background-color: var(--mec-blue-soft);
}

/* Leaflet's default focus ring is a faint outline that disappears against the
   pale Positron basemap. Markers are keyboard-reachable, so the focused pin
   has to be unmistakable - this is the only way a keyboard user can tell which
   of five pins they're about to open. */
.map-frame .leaflet-marker-icon:focus-visible {
  outline: 3px solid var(--signal-blue);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ATTRIBUTION IS A LICENCE CONDITION for both OpenStreetMap (ODbL) and CARTO.
   It is styled for legibility, never for concealment. Do not set display:none,
   shrink it further, or drop the contrast - deleting it makes the site
   non-compliant, not tidier. Text is --ink-soft on white: 8.2:1. */
.map-frame .leaflet-control-attribution {
  background-color: rgba(255, 255, 255, 0.9);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--ink-soft);
  padding: 2px var(--space-3xs);
}

.map-frame .leaflet-control-attribution a {
  color: var(--mec-blue-deep);
  text-decoration: underline;
}

/* Leaflet's zoom buttons default to a system-serif "+"/"−" on a plain white
   square. Squaring them off to the design system's radius and blue keeps them
   from reading as browser chrome dropped onto the page. */
.map-frame .leaflet-bar a {
  color: var(--mec-blue-deep);
  border-bottom-color: var(--rule);
}

.map-frame .leaflet-bar a:hover {
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

/* Shown when Leaflet fails to load or no location has coordinates.
   Keeps the page useful rather than leaving an empty grey box.

   The [hidden] rule below is load-bearing, not defensive boilerplate. The
   browser's own `[hidden] { display: none }` is a bare attribute selector, so
   the `display: flex` on the class beats it and the panel renders even when
   the map worked - telling every visitor the map failed while the map sits
   right above it. Any element that sets `display` and is toggled with the
   `hidden` attribute needs this. */
.map-fallback[hidden] { display: none; }

.map-fallback {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  min-height: 240px;
  padding: var(--space-xl) var(--space-md);
  text-align: center;
  color: var(--ink-soft);
}

.map-fallback svg { color: var(--mec-blue); }

/* Leaflet's popup chrome is outside our control; scope tightly to .map-frame
   so these never leak onto anything else. */
/* NOTE FOR ANYONE TEMPTED TO FIX A POPUP/CONTROL OVERLAP WITH z-index HERE:
   it cannot work. .leaflet-map-pane carries z-index:400 and so opens a
   stacking context; .leaflet-popup-pane lives inside it, meaning no value on
   the popup pane can lift it above .leaflet-top (z-index:1000), which is a
   sibling of the map pane. The overlap is solved in maps.js instead, by
   moving the zoom control and padding the popup auto-pan away from it. */

.map-frame .leaflet-popup-content-wrapper {
  border-radius: var(--radius);
  box-shadow: 0 6px 24px rgb(31 58 82 / 0.18);
}

.map-frame .leaflet-popup-content {
  margin: var(--space-sm);
  line-height: inherit;
}

.map-frame .leaflet-popup-close-button {
  color: var(--ink-mute);
  padding: var(--space-3xs) var(--space-2xs) 0 0;
}

.map-frame .leaflet-popup-close-button:hover { color: var(--ink); }

.map-info {
  font-family: var(--font-body);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink);
  max-width: 240px;
  padding-right: var(--space-2xs);
}

.map-info__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.01em;
  margin-bottom: var(--space-3xs);
}

.map-info__address { color: var(--ink-soft); margin-bottom: var(--space-2xs); }

.map-info__days {
  font-weight: 500;
  color: var(--mec-blue-deep);
  margin-bottom: var(--space-2xs);
}

/* --ink-soft on white: 8.37:1. NOT --ink-mute, which is 3.26:1 - at 11px this
   is normal-size text under WCAG (large starts at 24px, or 18.66px bold), so
   the 4.5:1 threshold applies and --ink-mute fails it outright. Being a small
   caption is the reason it needs the contrast, not an excuse to skip it. */
.map-info__label {
  display: block;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Two links, allowed to wrap on a narrow popup rather than being squeezed
   onto one line. */
.map-info__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-2xs);
  margin-bottom: 0;
}

.map-info__link { font-weight: 600; }

/* --mec-blue-deep on white: 7.31:1 */
.map-info__soon {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--mec-blue-deep);
  margin-bottom: var(--space-3xs);
}

/* ==========================================================================
   Location card grid
   ========================================================================== */

/* .nav-grid / .nav-card are the text-only variant used by hub and sub-pages
   (For Patients now, For Practices later). They are grouped into the existing
   .location-card rules rather than given their own copy of the same
   declarations, so the card treatment has ONE definition. If you restyle a
   card, both follow. The only thing .nav-card drops is the media box. */
.location-grid,
/* auto-FIT, not auto-fill. auto-fill keeps the tracks it creates even when
   nothing is placed in them: at 1180px this grid makes three 300px tracks, so
   the two-card For Practices hub sat in two of them and left a third of the row
   empty, with both cards sized for a three-up layout. auto-fit collapses the
   unused track and the two cards share the full width.
   The three-card For Patients hub is unaffected - three items fill all three
   tracks, so there is nothing to collapse and its layout is byte-identical.

   min(300px, 100%) AND NOT A BARE 300px. minmax()'s minimum is a floor the
   track is not allowed to fall below, so a bare 300px stayed 300px even inside
   a grid narrower than that: at a 320px viewport the container is 272px and
   every card on /locations, both hubs and all four sub-pages sat 28px wider
   than the grid, pushing the page sideways. min() keeps the 300px floor
   wherever it fits - which is every width above ~368px, so nothing else on the
   site moves - and clamps it to the grid only when it cannot. Fixed
   2026-08-16 alongside the lone-card cap below; the two are independent bugs
   that both showed up on the same phone-width pass. */
.nav-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: var(--space-lg);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* THE ONE CASE auto-fit GETS WRONG. The For Practices sub-page's Related row
   holds a single card - and auto-fit collapses every empty track, which
   stretched that card across the full 1132px into a banner. (auto-fill did not
   have this problem, but it had a worse one; see above.)

   The track is sized to what a card in a two-up row gets, so a lone Related card
   and a card in a two-card row are the same width. Every For Patients sub-page
   has two siblings and never matches this.

   :not(.nav-grid--feature) because a lone card is not always a mistake. The
   For Practices hub deliberately has exactly one and wants it wide - see
   .nav-grid--feature below. This cap is for the Related row, where a
   full-bleed banner at the foot of a page would outweigh the page above it.

   THE MEDIA QUERY IS THE WHOLE POINT AND IS NOT OPTIONAL. "Half of a two-up
   row" is a meaningless width when the grid is ONE column, and until
   2026-08-16 this rule carried no media query, so it applied at every width.
   On a 375px phone that left the lone "Host our service" card 147.5px wide in
   a 327px grid - a 399px-tall ribbon of wrapped text down the left edge, with
   half the row empty beside it. Below the breakpoint the base .nav-grid rule
   governs and the card fills the grid, which is what every other card on the
   site does on a phone.

   42.5em IS DERIVED, NOT CHOSEN, which is why it is not one of the site's
   usual breakpoints. The grid goes two-up when it can hold two 300px tracks
   plus a 32px --space-lg gap = 632px, and the grid is the viewport less the
   container's two 24px --space-md gutters, so 632 + 48 = 680px = 42.5em.
   Change minmax()'s 300px, --space-lg or the container padding and this number
   is wrong - recompute it, do not round it to 44em. */
@media (min-width: 42.5em) {
  .nav-grid:has(> :only-child):not(.nav-grid--feature) {
    grid-template-columns: minmax(0, calc(50% - var(--space-lg) / 2));
  }
}

/* ONE CARD, GIVEN WEIGHT. /for-practices dissolved its second sub-page on
   2026-08-14 and the single "Refer a patient" card left behind is the only
   route off that page - it has to read as the entry point it is, not as the
   survivor of a two-up row. Full container width, roomier padding, and the
   title stepped up one size from 1.375rem.

   NOT A NEW COMPONENT: still .nav-card, still the stretched link, still the
   shared hover lift and the __extra bar. Four declarations on a modifier,
   because the card's behaviour is identical and only its weight changes. */
.nav-grid--feature { grid-template-columns: minmax(0, 1fr); }

.nav-grid--feature .nav-card__body {
  gap: var(--space-xs);
  padding: var(--space-lg);
}

.nav-grid--feature .nav-card__title { font-size: 1.75rem; }

/* 60ch, not the container. The card is 1132px wide on desktop and its
   description is prose - measured across the full width it runs to ~150
   characters a line, roughly double the comfortable measure. The cap is what
   keeps this a card rather than a paragraph with a border. */
.nav-grid--feature .nav-card__desc {
  font-size: 1rem;
  max-width: 60ch;
}

.nav-grid--feature .nav-card__extra { padding: var(--space-sm) var(--space-lg); }

/* Below the container's own gutters the extra padding is more than the content
   needs, so the feature card returns to standard card metrics. Same breakpoint
   .steps collapses at - one breakpoint for "narrow phone", not two that nearly
   agree. */
@media (max-width: 34em) {
  .nav-grid--feature .nav-card__body { padding: var(--space-md); }
  .nav-grid--feature .nav-card__title { font-size: 1.375rem; }
  .nav-grid--feature .nav-card__extra { padding: var(--space-sm) var(--space-md); }
}

/* Elevation added 2026-08-13. A white card on --paper is only 1.06:1 - the
   border was doing 100% of the work of separating card from page, which is why
   the grids read as a wireframe. The shadow is what makes them read as objects.
   It is navy-tinted (see --shadow-* in tokens.css); a neutral-black shadow on
   these blue-tinted surfaces greys the page under each card. */
.location-card,
.nav-card {
  display: flex;
  flex-direction: column;
  background-color: var(--white);
  border: 1px solid var(--glow-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--glow-shadow);
  overflow: hidden;
}

/* The :hover that used to live here (2px lift, --shadow-lg) was folded into the
   shared link-card lift further down this file on 2026-08-14, which also gave
   these cards a top bar and a keyboard-focus state they did not have. Do not
   add a local :hover back - it would fight the shared rule by source order. */

/* Fixed aspect ratio so the varied source photo dimensions (landscape 900×464
   through portrait 495×619) don't produce a ragged grid. object-fit keeps them
   undistorted.

   The image is absolutely positioned because `aspect-ratio` is not a hard
   constraint - an in-flow child at its intrinsic size overrides it, which let
   the portrait Dromana photo stretch its card to 974px against 607px for the
   others. Taking the image out of flow makes the ratio authoritative. */
.location-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background-color: var(--mec-blue-soft);
}

.location-card__media img,
.location-card__media .photo-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.location-card__body,
.nav-card__body {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  gap: var(--space-2xs);
  padding: var(--space-md);
}

.location-card__name,
.nav-card__title {
  font-size: 1.375rem;
  line-height: 1.2;
  margin: 0;
}

/* Stretched link: whole card is clickable, but only one focusable element,
   so keyboard and screen-reader users get one clean stop rather than three. */
.location-card__name a,
.nav-card__title a {
  color: var(--ink);
  text-decoration: none;
}

.location-card__name a::after,
.nav-card__title a::after {
  content: '';
  position: absolute;
  inset: 0;
}

.location-card,
.nav-card { position: relative; }

.location-card__name a:hover,
.nav-card__title a:hover { color: var(--mec-blue-deep); }

.location-card__suburb,
.nav-card__desc {
  font-size: 0.9375rem;
  color: var(--ink-soft);
  margin: 0;
}

.location-card__schedule {
  margin-top: auto;
  padding-top: var(--space-sm);
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--mec-blue-deep);
}

/* ==========================================================================
   Photo placeholder - locations without a photo yet
   ========================================================================== */

.photo-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  width: 100%;
  height: 100%;
  min-height: 180px;
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue);
  text-align: center;
  padding: var(--space-md);
}

.photo-placeholder svg {
  width: 32px;
  height: 32px;
  stroke-width: 1.5;
}

.photo-placeholder__label {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mec-blue-deep);
}

/* ==========================================================================
   Coming soon badge
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-3xs) var(--space-2xs);
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  border-radius: 999px;
  align-self: flex-start;
}

.badge--soon {
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

.badge__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--accent-red);
}

/* In the page hero the badge sits between the h1 and the intro paragraph,
   neither of which carries a margin (base.css resets them). */
.page-hero .badge {
  margin-top: var(--space-sm);
  margin-bottom: var(--space-sm);
}

/* ==========================================================================
   Location sub-page
   ========================================================================== */

/* Gains the glow as well as the border: it is the location pages' equivalent of
   .about-hero__media, and those two are the site's only two hero photographs. */
.location-hero__media {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--glow-border);
  box-shadow: var(--glow-shadow);
  margin-bottom: var(--space-xl);
}

/* Same reasoning as the card media above - the ratio must win over the
   image's intrinsic size, so the image is taken out of flow. */
.location-hero__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background-color: var(--mec-blue-soft);
}

.location-hero__media img,
.location-hero__media .photo-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.location-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: start;

  /* Trailing white space, added on request 2026-08-15. THIS IS NOT DECORATION -
     without it .arrival-note sits flush against the navy contact band, its
     rounded bottom corners cut off by the gradient with no paper between them.

     The cause is that this grid's parent is a bare <div class="container">, not
     a .band. "Bands own the page's vertical rhythm" holds everywhere else on the
     site, which is why almost nothing carries an outer margin - but the location
     sub-pages put their whole body in a plain container, so this run has no band
     to inherit rhythm from and has to carry its own bottom edge. It is the last
     thing before contactSection() on all five pages.

     --space-2xl deliberately mirrors .arrival-note's own margin-top, so the box
     sits in an even 64px pocket of white rather than being crammed to one side
     of it. NOT --space-section: the navy band already contributes its own 96px
     of top padding, and a second 96px here reads as a hole in the page.

     On the grid rather than on the container because the container also wraps
     .location-hero__media above it, and this space belongs after the columns. */
  margin-bottom: var(--space-2xl);
}

@media (max-width: 56em) {
  .location-layout { grid-template-columns: 1fr; gap: var(--space-xl); }
}

/* The three-selector form exists because a sub-page body is no longer a flat
   list of blocks. buildSubPage() groups consecutive blocks into runs by their
   `wide` flag and wraps each narrow run in a .content-column, so a page that
   mixes the two - /for-practices/refer has five wide blocks then two narrow ones
   - has a wrapper boundary in the middle of the stack. `.info-block +
   .info-block` cannot see across that boundary, and the last wide block sat flush
   against the first narrow one at a 0px gap.
   .content-column is only ever a run wrapper or a band's sole child here, so the
   two extra selectors cannot fire anywhere a gap is unwanted. */
.info-block + .info-block,
.info-block + .content-column,
.content-column + .info-block { margin-top: var(--space-xl); }

.info-block__title {
  font-size: 1.5rem;
  line-height: 1.2;
  margin-bottom: var(--space-sm);
}

/* PATTERN B, restated for this heading. base.css carries the same declarations
   on `.display em`, and every other section heading on the site is
   `class="display sec-head__title"` and inherits them from there - but
   .info-block__title is not a .display, and until 2026-08-14 no block heading had
   ever contained an <em>, so nothing revealed the gap. The For Practices sub-page
   headings all do ("A <em>simple</em> referral process."), and without this they
   rendered as browser-default italic: the exact treatment that was withdrawn from
   this site on 2026-08-13.

   RESTATED, NOT MERGED into base.css's selector: `.display em` sets the size tier
   as well, and .info-block__title is deliberately a smaller heading. This is
   Pattern B only - the colour swap. It is never Pattern A (--signal-blue), which
   belongs to the homepage hero alone. */
.info-block__title em {
  font-style: normal;
  color: var(--mec-blue);
}

.info-block p { max-width: 70ch; color: var(--ink-soft); }

.detail-list {
  display: grid;
  gap: var(--space-sm);
  margin: 0;
}

/* --ink-mute (#8A8F96) is the natural token here per design-system.md, but at
   12px it measures 3.11:1 against the card background - below the 4.5:1 AA
   floor for text this size. These are real content labels, not decoration, so
   they use --ink-soft (7.9:1) instead. */
.detail-list dt {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: var(--space-3xs);
}

.detail-list dd {
  margin: 0;
  color: var(--ink);
}

/* Sidebar.
   --cream is a warm beige; against the blue palette it read as an unrelated
   colour rather than a brand one. --mec-blue-soft is the brand tint already
   used for the card media backdrop and the photo placeholder, so the booking
   card now belongs to the same family. The border is dropped - the tint alone
   separates it from --paper, and a warm --rule hairline was part of the clash. */
.location-aside {
  background-color: var(--mec-blue-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  position: sticky;
  top: 96px;
}

@media (max-width: 56em) {
  .location-aside { position: static; }
}

.location-aside__title {
  font-size: 1.25rem;
  margin-bottom: var(--space-sm);
}

.location-aside .btn { width: 100%; }
.location-aside .btn + .btn { margin-top: var(--space-2xs); }

/* The global .btn--secondary is a transparent button with a warm --rule border
   and a --mec-blue-soft hover. On the tinted aside that border was the same
   warm grey that clashed with the blue, and the hover state was the card's own
   background - so hovering did nothing visible. Scoped override only. */
.location-aside .btn--secondary {
  background-color: var(--white);
  border-color: var(--mec-blue);
}

.location-aside .btn--secondary:hover {
  background-color: var(--white);
  border-color: var(--mec-blue-deep);
}

.location-aside__note {
  font-size: 0.875rem;
  color: var(--ink-soft);
  margin-top: var(--space-sm);
}

/* Coming-soon locations only. Sits above the booking buttons so the reader
   knows this location isn't running before they act on the CTA. --ink is used
   rather than --ink-soft because this is a correction to an expectation the
   page has just set, not a footnote. */
.location-aside__notice {
  font-size: 0.9375rem;
  color: var(--ink);
  margin-bottom: var(--space-sm);
}

/* Shared arrival / results block - rendered once per page, never per card */
.arrival-note {
  background-color: var(--mec-blue-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  margin-top: var(--space-2xl);
}

.arrival-note__title {
  font-size: 1.25rem;
  margin-bottom: var(--space-2xs);
}

.arrival-note p { color: var(--ink-soft); max-width: 70ch; }
.arrival-note p + p { margin-top: var(--space-2xs); }

/* Breadcrumb */
.breadcrumb { padding-block: var(--space-md) 0; }

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.875rem;
  /* Carries the current page name, so it's content text and must clear AA at
     14px - --ink-mute measures ~3:1 here. */
  color: var(--ink-soft);
}

/* Purely decorative separator - --ink-mute is fine. */
.breadcrumb li:not(:last-child)::after {
  content: '/';
  margin-left: var(--space-2xs);
  color: var(--ink-mute);
}

.breadcrumb a { font-weight: 500; }

/* ==========================================================================
   Team page
   --------------------------------------------------------------------------
   One team, not tiered: the cardiologist and the two sonographers use the
   IDENTICAL block. The only thing that distinguishes them is the role line -
   no larger photo, no different background, no ordering cue beyond sequence.
   If a future change makes one member's block visually heavier than another's,
   that is a content decision, not a styling one - check content/team.md first.
   ========================================================================== */

.team-list {
  padding-block: var(--space-2xl);
}

.team-member {
  display: grid;
  /* Narrower than the 380px the rectangular frame used. A circle of a given
     width reads considerably larger than a portrait rectangle of the same
     width, because nothing is cropped off the sides of the eye's impression
     of it - 300px here sits at about the same visual weight 380px did. */
  grid-template-columns: 300px minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: start;
  /* Each member has an id, so /team#robert-crea is a real link. .site-header
     is position: sticky at 72px tall, and without this the anchor target lands
     underneath it. */
  scroll-margin-top: 96px;
}

/* Hairline between members rather than cards. Cards would each need a
   background, and against --paper that starts to read as a directory listing
   of separate people rather than one team. */
.team-member + .team-member {
  margin-top: var(--space-2xl);
  padding-top: var(--space-2xl);
  border-top: 1px solid var(--rule);
}

/* Circular frame. Same reasoning as .location-card__media - the ratio has to
   win over the image's intrinsic size, so the image is taken out of flow and
   cropped. The ratio is fixed here specifically so Nigel's .photo-placeholder
   occupies a box identical to the two real photos and the row can't go ragged.

   BORDER + SHADOW, lifted verbatim from the live site's .echo-box-prominent -
   the six cards on /for-practices. Those read:

     border:     1px solid rgba(0, 110, 223, 0.25);
     box-shadow: 0px 12px 30px rgba(0, 110, 223, 0.12);

   rgb(0, 110, 223) is #006EDF, i.e. --signal-blue exactly, so this reproduces
   as a color-mix of the existing token and no new hex enters the palette (see
   the "ask before adding" rule in design-system.md). Read out of the live
   page's own CSS rather than eyedropped from the screenshot, for the same
   reason --signal-blue itself was - no compression or colour-profile drift.

   This replaces an earlier --mec-blue glow (a 6px spread ring plus a 32px
   diffuse layer) that was rejected as not matching those cards. Two things
   were wrong with it: the colour was the dusty --mec-blue rather than the
   vivid signal blue, and the frame was a thick spread ring rather than a
   hairline. A 1px var(--rule) border was tried before that and read as a
   rendering fault against blue - the same warm-grey-on-blue clash documented
   on .location-aside. A blue hairline has no such problem.

   The cards' border-radius: 22px is deliberately NOT carried over; the 50%
   below supersedes it. Everything else is 1:1.

   JUDGEMENT CALL: design-system.md caps --signal-blue at one "signal moment"
   per page, and this rule paints three headshots. That cap is written about
   Pattern A headline emphasis - what it protects is the impact of a coloured
   phrase in a display heading, and team.html has no Pattern A heading for
   these to compete with. The live site itself repeats this exact colour
   across six cards as structural chrome. At 25%/12% alpha it is a tint, not
   a signal. Flagged rather than assumed.

   If color-mix is unsupported both declarations are dropped and the circle
   renders flat, which is a degradation and not a break. */
.team-member__media {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: 50%;
  background-color: var(--mec-blue-soft);
  /* These two declarations ARE the origin of --glow-border/--glow-shadow: they
     were the first place .echo-box-prominent was reproduced, and the site-wide
     tokens were extracted from here on 2026-08-15. Now consuming the tokens so
     the circles cannot drift from the cards they seeded. The one change that
     made: the 24px zero-offset layer, which this rule never had. On a 1:1
     circle it is barely perceptible - it was added for wide rectangles like
     .map-frame - but sharing one value beats keeping a near-duplicate. */
  border: 1px solid var(--glow-border);
  box-shadow: var(--glow-shadow);
}

.team-member__media img,
.team-member__media .photo-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A 1/1 crop of a 3/4 portrait discards a quarter of the frame vertically, and
   a centred crop takes it evenly off the top and bottom - which cuts the top of
   the head, because that is where the subject sits. Biased upward so the crop
   comes off the torso instead. Verified against both photos: 18% keeps Robert's
   hairline and Alicia's whole head inside the circle. Re-check this number if
   the masters are replaced in the planned reshoot. */
.team-member__photo {
  object-position: 50% 18%;
}

.team-member__name {
  font-size: 1.75rem;
  line-height: 1.15;
  margin-bottom: var(--space-3xs);
}

.team-member__body p {
  color: var(--ink-soft);
  max-width: 70ch;
}

.team-member__body p + p {
  margin-top: var(--space-sm);
}

/* Specificity has to beat .team-member__body p above, which would otherwise
   recolour the role line to --ink-soft. Same treatment as .detail-list dt:
   --mec-blue is the natural brand choice. It measured 3.97:1 when this was
   written and outright failed the 4.5:1 AA floor; since 2026-08-11 it is
   #006EDF at 4.67:1 and technically passes. --mec-blue-deep (6.83:1) stays,
   because this is 12px uppercase at weight 500 - the size and weight where a
   bare pass reads thin. Passing is not the same as being legible. */
.team-member__body .team-member__role {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mec-blue-deep);
  margin-bottom: var(--space-md);
}

@media (max-width: 56em) {
  .team-member {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
  }

  /* Capped rather than full-bleed: a circle at 100% of a phone viewport is a
     ~340px disc that pushes the whole bio below the fold, and the drop shadow
     needs room to fall below the circle without touching the container edge.
     Kept at the desktop column width so the srcset `sizes` hint stays honest
     at both breakpoints. */
  .team-member__media {
    max-width: 300px;
  }
}

/* Closing CTA. Same --mec-blue-soft panel as .location-aside and
   .arrival-note, so the three shared blocks on the site read as one family. */
/* Closing booking CTA. Renamed from .team-cta on 2026-08-10 when the For
   Patients section needed the identical panel on four more pages - the old
   name would have meant either a second copy of these rules or a For Patients
   page carrying "team" classes. It is section-neutral now; For Practices and
   the remaining pages should use it as-is rather than adding another variant.
   Only build.js emits these classes, so the rename needs no HTML migration -
   the generated files are rewritten by `node build.js`.

   .cta-panel__text is optional: the team page sets a supporting line, the
   For Patients pages have no approved copy for one and omit it. */
.cta-panel {
  background-color: var(--mec-blue-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-3xl);
  text-align: center;
}

/* Top margin only when something precedes the panel in the same flow. On
   team.html the panel is the sole child of its own .container and the section
   above supplies the gap, so an unconditional margin-top would double it. The
   For Patients pages put the panel straight after .related inside one
   container, where it otherwise butts against the last card with no gap.
   "* + " matches the second case and not the first. */
* + .cta-panel { margin-top: var(--space-2xl); }

.cta-panel__title {
  font-size: 2rem;
  line-height: 1.15;
  margin-bottom: var(--space-2xs);
}

.cta-panel__text {
  color: var(--ink-soft);
  max-width: 48ch;
  margin-inline: auto;
  margin-bottom: var(--space-md);
}

.cta-panel__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  justify-content: center;
}

/* Same scoped override as .location-aside - the global .btn--secondary has a
   warm --rule border and a --mec-blue-soft hover, which is this panel's own
   background, so hovering would do nothing visible. */
.cta-panel .btn--secondary {
  background-color: var(--white);
  border-color: var(--mec-blue);
}

.cta-panel .btn--secondary:hover {
  background-color: var(--white);
  border-color: var(--mec-blue-deep);
}

@media (max-width: 40em) {
  .cta-panel { padding: var(--space-lg); }

  /* 1.75rem, not the 1.625rem this was before 2026-08-13. The serif tier's
     floor is ~1.75rem and it has to hold at EVERY breakpoint, not just desktop
     - this was the one clamp on the site that dropped under it on mobile, so
     the rule was true in the stylesheet and false on a phone. Raising the size
     by 2px was the cheaper fix than carving out an exception. */
  .cta-panel__title { font-size: 1.75rem; }
}

/* ==========================================================================
   Hub + sub-page sections (For Patients now, For Practices later)
   ========================================================================== */

/* --------------------------------------------------------------------------
   Background bands
   --------------------------------------------------------------------------
   Spec: design-system.md → "Alternating background bands".

   A band is full-bleed; its .container keeps the content on the same 1180px
   grid as everything else. This is why the hub is a stack of bands rather than
   one container wrapping everything - a tint that stops at 1180px reads as a
   very wide box, not as a section.

   Bands own the page's vertical rhythm. Sections inside them must NOT add
   their own block margins or the gap doubles at every colour change.

   ONE HARD RULE: no --mec-blue-soft panel inside a tinted band. They measure
   1.05:1 against each other, so the panel's edge disappears and what is left
   looks like a rendering fault. Blocks that need a tint panel go on a paper
   band; blocks on a tinted band let the band do the emphasising.
   -------------------------------------------------------------------------- */
.band { padding-block: var(--space-section); }

/* Was .band--cream against --cream until 2026-08-12; the warm neutral was
   rejected for breaking the all-blue palette. See --band-tint in tokens.css. */
.band--tint { background-color: var(--band-tint); }

/* --------------------------------------------------------------------------
   .band--navy - the signature
   --------------------------------------------------------------------------
   Added 2026-08-13. This is the answer to "the site looks plain", and it exists
   because the alternative was measured and found impossible: every light blue
   between #FFFFFF and --mec-blue-soft sits within 1.10:1 of every other one, so
   light-on-light banding can only ever whisper. Contrast at this scale has to
   come from a dark section. Don't respond to a flat-looking page by reaching
   for a stronger light tint - there isn't one.

   IT IS A SIGNATURE, NOT THE HOUSE STYLE. Two or three per page, maximum. The
   prototype runs 2 dark sections out of 7 and that ratio is the target. A navy
   band works because the page around it is light; make the page mostly navy and
   you have spent the effect and gained a heavy site.

   Three layers, bottom to top:
     1. a 135deg ramp navy -> lighter blue -> deeper navy, so the section has a
        direction rather than being a flat fill
     2. two radial glows, offset from each other and from the ramp's midpoint,
        which is what stops the ramp reading as a mechanical gradient
     3. a faint grid mesh, masked to fade out at the edges (::before)

   CONTRAST IS MEASURED AGAINST THE WORST POINT, not the average. The lightest
   place text can land is the ramp's #003A7A midpoint under the blue glow. White
   is 8.77:1 there and --mec-blue-soft is 7.67:1, both comfortably AA. If you
   ever raise a glow's alpha or lighten the ramp, re-measure that point rather
   than assuming the margin absorbs it.

   And see --sky in tokens.css before using the second accent here: it is
   2.69:1 on this background, which fails even the 3.0 non-text floor. It may
   glow and draw lines. It may not be a link, a label or a focus ring.
   -------------------------------------------------------------------------- */
.band--navy {
  position: relative;

  /* Contains the mesh and any decorative overflow. */
  overflow: hidden;

  /* Creates a stacking context so the ::before mesh's negative z-index stays
     trapped inside this band. Without it, z-index: -1 escapes and the mesh
     paints behind the page background - i.e. invisible. A negative-z child
     still paints ABOVE its own parent's background, which is what makes the
     mesh sit on the gradient and under the text. */
  isolation: isolate;

  /* Fallback for anything that can't do the gradients, and the base the glows
     composite onto. */
  background-color: var(--mec-navy);

  background-image:
    radial-gradient(ellipse 80% 60% at 70% 50%, rgba(0, 110, 223, 0.28) 0%, transparent 60%),
    radial-gradient(ellipse 60% 80% at 20% 80%, rgba(0, 153, 204, 0.16) 0%, transparent 50%),
    linear-gradient(135deg, #002244 0%, #003A7A 50%, #001A52 100%);

  color: var(--mec-blue-soft);
}

/* The mesh. 52px cells at 3.5% white - deliberately near the threshold of
   visibility. It should register as texture, not as a visible grid; if you can
   read it as squares from a normal viewing distance it is too strong.

   The mask fades it to nothing at the edges so it doesn't collide with the
   band's boundary. -webkit-mask-image is still required for Safari. */
.band--navy::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px);
  background-size: 52px 52px;
  -webkit-mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 100%);
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 100%);
}

/* Headings go to full white; body copy stays on --mec-blue-soft inherited from
   the band. That split is the same one the footer already makes, which is why
   no new token was needed for it. */
.band--navy .display,
.band--navy .sec-head__title,
.band--navy h2,
.band--navy h3 {
  color: var(--white);
}

.band--navy .sec-head__sub,
.band--navy p {
  color: var(--mec-blue-soft);
}

/* --mec-blue is 1.79:1 on this background - invisible. Any Pattern B <em> in a
   navy heading has to drop its colour swap and let the surrounding white carry
   the sentence, otherwise the emphasised phrase becomes the one part nobody can
   read. --sky is no better here (2.69:1); this is a background that simply does
   not take a coloured emphasis, so the phrase stays white. */
.band--navy .display em,
.band--navy .sec-head__title em,
.band--navy .info-block__title em {
  color: inherit;
  font-style: normal;
}

/* --sky-on-navy, NOT --sky. The eyebrow is text and the raw accent is 2.69:1
   here - it fails AA and it fails the non-text floor too. See tokens.css. */
.band--navy .eyebrow { color: var(--sky-on-navy); }

/* Buttons on navy. The primary blue button loses almost all its separation from
   this background, so the roles invert: the solid button becomes white with ink
   on it (16.91:1), and the secondary becomes a white outline. */
.band--navy .btn--primary {
  background-color: var(--white);
  color: var(--ink);
  border-color: var(--white);
}

.band--navy .btn--primary:hover {
  background-color: var(--mec-blue-soft);
  border-color: var(--mec-blue-soft);
  color: var(--ink);
}

.band--navy .btn--secondary {
  background-color: transparent;
  color: var(--white);
  border-color: rgba(255, 255, 255, 0.45);
}

.band--navy .btn--secondary:hover {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: var(--white);
  color: var(--white);
}

/* The default focus ring is --mec-blue, which is 1.79:1 here and would leave
   keyboard users with no visible focus at all on a navy section. */
.band--navy :focus-visible { outline-color: var(--white); }

/* Enforcement of "bands own the vertical rhythm", for the one element that
   legitimately carries an outer margin elsewhere. .cta-panel sets
   margin-bottom: var(--space-3xl) so it clears the footer on team.html, where
   it is not in a band. Inside a band that margin lands on top of the band's own
   padding and the gap before the footer nearly doubles. */
.band .cta-panel { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Section header formula
   --------------------------------------------------------------------------
   Spec: design-system.md → "Section header formula".

   Eyebrow → title → one-line sub, centred, with a fixed gap to whatever
   follows. It is the label for a full-width device (the step row, the card
   grid), which is why it is centred: those devices span the container, so a
   left-aligned header would hang off one corner of a symmetrical graphic.

   NOT for prose. .info-block headings stay left-aligned on the reading column
   - centring a heading over a 70ch paragraph puts the two on different axes
   and the ragged result is worse than no header treatment at all.

   All three parts are optional. The card grid takes a title only; the steps
   take all three. Don't invent an eyebrow to fill the slot - the eyebrow is a
   named section label, not decoration.
   -------------------------------------------------------------------------- */
.sec-head {
  text-align: center;
  margin-bottom: var(--space-2xl);
}

/* For a section header that does NOT sit over a symmetrical full-width
   device: a two-column split, or anything where the content beneath starts at
   the container's left edge. Centring a heading over left-aligned body puts
   the two on different axes, which is the fault this fixes.

   The margin drops from --space-2xl to --space-md at the same time, and that
   is not incidental. Centred, the 64px reads as a section break before a
   full-width device begins. Left-aligned above a paragraph in the same column
   it reads as a hole - reported 2026-08-15 as "roughly 150px of dead space".
   24px is heading-to-body spacing, which is what this now is. */
.sec-head--left {
  text-align: left;
  margin-bottom: var(--space-md);
}

.sec-head__title { font-size: clamp(1.75rem, 3vw, 2.375rem); }

.sec-head__sub {
  max-width: 52ch;
  margin: var(--space-sm) auto 0;
  color: var(--ink-soft);
}

.sec-head--left .sec-head__sub { margin-inline: 0; }

/* Line length is the whole job here. .info-block p is already capped at 70ch,
   but the headings and lists were free to run the full container width, which
   left h2s floating a long way right of the text they belong to. */
.content-column { max-width: 70ch; }

.section-body { padding-block: var(--space-2xl); }

/* Flow spacing inside a block. base.css zeroes the margin on every p, and
   until now every .info-block on the site held exactly one paragraph, so
   nothing ever revealed that there was no rule for the second one. The For
   Patients blocks run to four paragraphs each and rendered as one solid slab.
   Written as "> * + *" rather than "p + p" so lists and any element added to
   a block later are spaced too, without another rule being needed.
   .info-block__title already carries margin-bottom: var(--space-sm); adjacent
   sibling margins collapse, so the heading gap is unchanged, not doubled. */
.info-block > * + * { margin-top: var(--space-sm); }

/* Deep-link target. The homepage links to /for-patients#bulk-billing, and
   .site-header is position: sticky at 72px, so without this the heading lands
   underneath it and the block looks like it starts at its second paragraph.
   Applies to every id'd block, not just bulk billing - all of them are
   linkable and any of them could be linked to later. */
.info-block[id] { scroll-margin-top: 96px; }

/* The tinted panel exists so the #bulk-billing jump visibly lands on
   something. Same treatment as .arrival-note and .location-aside, so the
   tinted blocks across the site stay one family.
   Full-bleed of .content-column rather than inset, so the tint reads as a
   section rather than as a pull-quote. */
.info-block--tint {
  background-color: var(--mec-blue-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
}

.info-block--tint p { max-width: 64ch; }

/* Centres the BLOCK inside a full-width band and holds its prose to a reading
   measure. The paragraphs stay left-aligned: the centred .sec-head above them
   is two short lines and reads as a heading, where three centred paragraphs
   would have a ragged left edge for the eye to hunt on every line.

   max-width goes on the section, not on its <p>s, so the .sec-head is centred
   over the same measure the text occupies rather than over the container. */
.info-block--measure {
  max-width: 68ch;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   Split block - copy left, illustration right
   --------------------------------------------------------------------------
   `image` on a block descriptor. Used by Bulk billing on /for-patients and
   nowhere else yet.
   -------------------------------------------------------------------------- */

/* The margin is zeroed because the grid `gap` does this job once the block is
   two columns; leaving both would add 16px to the top of the art column and
   quietly break the vertical centring. Inside .info-block__body the sibling
   rule still applies, so the paragraphs are spaced as they always were. */
.info-block--split {
  display: grid;
  gap: var(--space-xl);
}

.info-block--split > * + * { margin-top: 0; }

/* .info-block > * + * only reaches direct children, and a split block's copy
   is one level deeper inside .info-block__body. Without this the paragraphs
   collapse into a single slab, which is the exact fault that rule was written
   to fix in the first place. */
.info-block__body > * + * { margin-top: var(--space-sm); }

/* 1.4fr : 1fr, which lands at roughly 55/40 once the 64px gap is taken out -
   at the 1180px container that is 651px of copy against 465px of column. The
   art is capped at 440px below, so the cap binds and the column never
   stretches it. Widen this and the cap stops doing anything.

   60em matches every other two-column device on the site. align-items: center
   vertically centres the illustration against the text block. */
@media (min-width: 60em) {
  .info-block--split {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    gap: var(--space-2xl);
    align-items: center;
  }
}

/* The illustration sits directly on the page: no card, no border, no fill, no
   shadow. It is line art with a knocked-out background, so any of those would
   box a shape that has no edges of its own.

   440px is half the intrinsic 880px - the asset is exported at 2x so it stays
   sharp on a retina screen. `height: auto` overrides the intrinsic height
   attribute, which is there only to give the <img> an aspect-ratio and hold
   CLS at zero. Do not remove the attribute to "fix" a stretched image; fix it
   here. */
.split-art {
  display: block;
  width: 100%;
  max-width: 440px;
  height: auto;
}

/* Stacked. Capped harder and centred, or a 440px illustration on a 375px
   phone outweighs everything above it. width: 100% keeps it inside the
   container below 300px, so there is no horizontal overflow. */
@media (max-width: 59.99em) {
  .split-art {
    max-width: 300px;
    margin-inline: auto;
  }
}

/* --------------------------------------------------------------------------
   Fact row - three short requirements above the body copy
   --------------------------------------------------------------------------
   Restrained on purpose: a thin rule above each label and nothing else. No
   card, no border box, no pill, no fill. The rule is the same --rule the site
   uses for every other hairline, so the row reads as part of the page rather
   than as a widget dropped into it.

   --ink, not --ink-soft: these are the section's load-bearing facts and the
   body copy beneath them is already soft. 15.57:1 on --band-tint.
   -------------------------------------------------------------------------- */
.fact-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md);
  margin: 0 0 var(--space-lg);
  padding: 0;
  list-style: none;
}

.fact-row__item {
  border-top: 2px solid var(--mec-blue);
  padding-top: var(--space-2xs);
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
}

/* One column below 34em, not two. The three labels are different lengths, so
   a 2-up grid leaves one item alone on the second row looking like an
   afterthought; stacked, the rules read as a short list. */
@media (max-width: 34em) {
  .fact-row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-sm);
  }
}

/* The button is in a <p> so it inherits the block's flow spacing; --space-md
   on top of that gives it a little more room than a paragraph gets, so it
   reads as the end of the section rather than as another line of copy. */
.info-block__cta { margin-top: var(--space-md); }

.bullet-list {
  list-style: none;
  margin: 0 0 0 var(--space-xs);
  padding: 0;
  max-width: 70ch;
  color: var(--ink-soft);
}

.bullet-list li {
  position: relative;
  padding-left: var(--space-md);
}

.bullet-list li + li { margin-top: var(--space-2xs); }

/* Dot bullets per design-system.md "Structural motifs". Sized and positioned
   off the text's own line-height so it stays aligned with the first line when
   an item wraps to two lines. */
.bullet-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--mec-blue);
}

.bullet-list strong { color: var(--ink); }

/* The separator used to be a hairline rule plus a top margin, because Related
   sat in the same container as the prose above it with nothing else to mark the
   change of purpose. It now has a tinted band of its own, and a border inside a
   colour change is one separator too many - the band edge already reads as the
   line. There is deliberately no .related rule left: the band supplies the
   spacing and the separation, and the heading comes from the shared .sec-head
   formula. The class stays in the markup as a hook, not as a style. */

/* ==========================================================================
   Site footer
   ========================================================================== */

/* LIGHT SINCE 2026-08-13. It was --mec-navy until then.

   The reason it changed: every page closes on a navy CTA band, so a navy
   footer meant the bottom third of the site was one continuous dark mass with
   two slightly different dark blues in it - the CTA's gradient ends at #001A52,
   the footer was flat #002244. Close enough to look like the same colour
   failing to render consistently, far enough apart to read as a clash. Making
   the footer light turns that ambiguous join into a deliberate step: navy CTA,
   then a clean edge, then the pale foundation. #001A52 against this soft blue
   is 14.46:1, so the boundary is unmistakable.

   EVERY rule below this point was written for white-on-navy and has been
   inverted. If you ever take the footer dark again, they all flip back
   together - the colour is not a one-line change. */
.site-footer {
  /* #F8FBFF, matched to the LIVE site on 2026-08-13 - melbourneechoclinic.com.au
     serves `.echo-footer-v3 { background: #F8FBFF }`. Read from its stylesheet,
     not sampled off a screenshot, so this is the actual value and not an
     approximation of one. Was --mec-blue-soft #E6F1FC for a few hours before.

     Yes, this is the value --paper held until earlier the same day. That is a
     coincidence of the brand's own light-blue ramp having very few usable steps,
     not a mistake - but it does mean the footer fill is only 1.04:1 against the
     now-white page. THE FILL IS NOT WHAT SEPARATES THE FOOTER. The border below
     is. Delete the border and the footer stops existing as a region.

     Every text contrast improved by moving here from #E6F1FC:
       ink 14.78 -> 16.30   ink-soft 7.31 -> 8.06   link 6.24 -> 6.88
       focus ring 4.27 -> 4.71   logo artwork 3.89 -> 4.28
     and the navy CTA band above it is 15.94:1, so that join is still emphatic. */
  background-color: #F8FBFF;

  /* The live site uses rgba(0, 110, 223, 0.1) here. Deliberately not copied:
     10% blue over white measures 1.14:1, under the ~1.2 this codebase treats as
     the floor for a 1px edge to be perceptible (see --rule in tokens.css), and
     with a 1.04:1 fill there is nothing else drawing the line. 22% gives 1.36:1
     and is the same mix already used by .site-footer__legal below, so the
     footer's outer edge and its internal divider match. */
  border-top: 1px solid color-mix(in srgb, var(--mec-blue) 22%, transparent);

  color: var(--ink);

  /* NO margin-top, and --space-section rather than --space-2xl on top.

     There used to be margin-top: var(--space-section) here, plus a :has() rule
     cancelling it whenever the last band was navy. Both existed only because a
     dark footer needed daylight around it. A light footer is simply the last
     band in the stack, and the house rule is that bands own their vertical
     rhythm through padding - so the 96px moves from outside to inside and the
     :has() special case disappears with it.

     Moving it inward matters on the two page types that do NOT end on a band
     (locations index, individual location pages). They ended with the old 96px
     margin doing the separating; an external margin would still work for them
     and an internal one works for every page, which is why it went in here. */
  padding-block: var(--space-section) var(--space-lg);
}

/* Sized EXPLICITLY, not with auto-fit, and the reason is one string of text.
   "admin@melbourneechoclinic.com.au" measures 280px at the footer's 16px body
   size. The old `repeat(auto-fit, minmax(200px, 1fr))` produced four equal
   247px columns at 1280px and a three-up state of roughly 215px on the way
   down, so the address could not fit at ANY width and always broke in two.

   So the columns are no longer equal: Contact gets the widest track, and the
   three-up state is skipped altogether (four columns straight down to two).
   The alternative was setting the address smaller than the phone number
   directly above it, which fixes the symptom by making one line of contact
   detail quietly less legible than its neighbours.

   Track sizes are chosen against the narrow end of each range, not the wide
   end - that is where a layout breaks:
     1024px viewport -> 976 inner - 96 gaps = 880 across 4.1fr -> Contact 290px
     1180px (capped) -> 1132 inner - 96 gaps = 1036          -> Contact 341px
      704px viewport -> 656 inner - 32 gap   = 624 across 2   -> 312px
   All comfortably over 280. If you change the track ratios, the gap, or
   --container-max, re-run those three numbers. */
.site-footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  /* Columns are tighter than rows on purpose: the four-column layout needs the
     horizontal room back to clear 280px, and 32px between columns still reads
     as separate columns. Rows keep the original 48px. */
  row-gap: var(--space-xl);
  column-gap: var(--space-lg);
}

@media (min-width: 44em) {
  .site-footer__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64em) {
  .site-footer__grid { grid-template-columns: 1fr 0.8fr 1.35fr 0.95fr; }
}

/* --mec-blue-deep, not --mec-blue. On this background --mec-blue is 4.27:1 and
   these are 16px links, so it misses the 4.5 floor; --mec-blue-deep is 6.24:1.
   This is the same constraint that applies everywhere else on the site - see
   design-system.md - it just bites harder here because the ground is tinted
   rather than white. */
.site-footer a {
  color: var(--mec-blue-deep);
  font-weight: 400;
  text-decoration: none;
}

.site-footer a:hover { color: var(--mec-navy); text-decoration: underline; }

.site-footer__heading {
  font-family: var(--font-body);
  font-size: 0.75rem;
  /* 700, raised from 500 on request - the column labels were not holding their
     own against the link lists under them. Inter is loaded at 400;500;600;700
     (see partials/head.html), so 700 is a real weight here and not a synthesised
     one. It is also the ceiling: asking for 800 would get faux-bold. */
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* --ink, 15.86:1, on request. These were --ink-soft (8.06:1) on the reasoning
     that column labels should sit back from the content they label; the ask was
     for black, and at 12px uppercase with 0.18em tracking there is no legibility
     argument against it - wide-tracked small caps are the one place where the
     darkest ink is easiest to defend. --ink rather than pure #000 so the footer
     labels match every other piece of body text on the site. */
  color: var(--ink);
  margin-bottom: var(--space-sm);
}

/* The address is 280px wide and, since the grid was re-sized above, the Contact
   column is 290px at its narrowest. It now sits on ONE line everywhere the site
   is normally read, which is the point - a wrapped email address reads as a
   layout accident.

   This rule and the <wbr> in the markup are the safety net for what is left:
   viewports under ~320px, and browser text-only zoom, where 280px of text
   cannot fit no matter how the columns are cut. Without them an unbroken string
   would push the footer wider than the screen; with them it breaks after the
   "@" instead of mid-domain ("...melbourneechoclini / c.com.au"). Keep both -
   they cost nothing when the address fits, which is now the normal case. */
.site-footer__email {
  overflow-wrap: anywhere;
}

.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2xs); }

/* The footer uses the SAME blue-on-transparent PNG as the header, and now uses
   it AS DRAWN. It carried filter: brightness(0) invert(1) until 2026-08-13 to
   render it white, because the artwork's own blue - #377DB0, sampled from the
   PNG's pixels rather than assumed from the token - measured 2.86:1 on navy and
   read as a dimmed or half-loaded image.

   That filter had to come off the moment the footer went light: white artwork
   on the current #F8FBFF is 1.04:1, i.e. an invisible logo, not a subtle one.
   As drawn it measures 4.28:1 on that background, which is a real mark. Logos
   are exempt from the WCAG minimums either way; the point is that it is legible.
   (Both figures re-measured 2026-08-13 against #F8FBFF. They previously read
   1.15 / 3.89, which were correct for the #E6F1FC the footer used for about an
   hour before it was matched to the live site.)

   So: if the footer background ever changes again, check this rule. It is the
   one footer property where getting it wrong removes an element rather than
   dimming it. */
.site-footer__logo {
  width: auto;
  height: 46px;
  margin-bottom: var(--space-sm);
}

/* Accreditation marks. Added 2026-08-13 to mirror the live site's fourth column.

   Sized by WIDTH, not height, even though the two marks have quite different
   aspect ratios (ASAR 2.74:1, NATA 2.03:1). Matching heights would make ASAR
   physically wider than NATA and the pair would read as one big mark and one
   small one; matching widths gives a tidy left edge and an even column, which is
   what the live site does. NATA ends up taller. That is correct, not a bug.

   NOTE THE SELECTOR. `.site-footer ul` above sets gap: var(--space-2xs) and is
   specificity (0,1,1); a bare `.site-footer__accred-list` is (0,1,0) and loses,
   which silently left these marks 8px apart - close enough to read as one
   two-part logo. Scoping to .site-footer makes it (0,2,0) and it wins. Do not
   "simplify" this back to the single class. */
.site-footer .site-footer__accred-list {
  gap: var(--space-md);
  justify-items: start;
}

.site-footer__accred-logo {
  width: 100%;
  max-width: 160px;
  height: auto;
  /* Served at 320px for 160px of layout, i.e. 2x for retina. Do not point this
     at the full-size originals (asar.png 832px/78KB, nata.png 491px/25KB) -
     they are the archive copies, not the web ones. */
}

/* No colour rule here. "Bulk-billed." is wrapped in .display__signal in
   partials/footer.html and picks up --signal-blue from base.css, which is the
   point: the footer's blue word is the same declaration as every heading's blue
   word and cannot drift from it. --mec-blue on #F8FBFF is 4.71:1, and this is
   18px so the 4.5 normal-text floor applies - it clears, but not by much. If the
   footer fill is ever darkened, re-measure this before anything else.

   KNOWN TENSION, added on request 2026-08-13: base.css says "one signal moment
   per page maximum, or it stops signalling", and every page's h1 already spends
   it. This is a second. The live site does the same thing in its own footer
   (<span class="blue-text">delivered locally.</span>), so it is at least
   consistent with the brand as shipped - but if the blue word ever starts
   feeling cheap, this is the one to drop, not the h1. */
.site-footer__tagline {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-size: 1.125rem;
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
  padding-top: var(--space-md);
  /* Not --rule: that token is 1.09:1 on this background, i.e. a hairline you
     cannot see. A hairline is decorative and exempt from 1.4.11, but an
     invisible one is just a missing separator. Mixing the brand blue into the
     footer's own tint keeps it in the 210° family and actually shows. */
  border-top: 1px solid color-mix(in srgb, var(--mec-blue) 22%, transparent);
  color: var(--ink-soft);
}

/* CURRENTLY UNUSED, and kept on purpose. The Privacy and Accessibility links
   this styles were pulled out of partials/footer.html on 2026-08-15 because the
   pages behind them were never built; Robert is adding them later, so the markup
   is coming back and deleting this rule would only mean retyping it. If those
   pages are ever formally abandoned, delete this too - but the privacy policy is
   legally required, so that is unlikely.

   .site-footer__legal is justify-content: space-between and now has a single
   child, which is why the copyright line still sits left with the list gone. */
.site-footer__legal-links { display: flex; gap: var(--space-md); list-style: none; margin: 0; padding: 0; }

/* Focus ring: no override. The global :focus-visible is --signal-blue, which is
   4.27:1 here - above the 3.0 non-text floor of WCAG 1.4.11. The white override
   that used to live here was for navy and would now be 1.15:1, i.e. no visible
   focus indicator at all for keyboard users. Deleted deliberately; do not
   reinstate a footer-specific outline colour without measuring it. */

/* ==========================================================================
   About page
   ========================================================================== */

/* Hero: prose beside a portrait photograph.

   THE PICTURE USED TO BE FULL WIDTH AND 16:9, BELOW THE TEXT. It was changed on
   2026-08-13 because it was "way too big and takes up too much space": at the
   1180px cap that figure stood 637px tall on its own and pushed Our Mission
   clean off the first screen. Set beside the text at 3:4 it costs no vertical
   space the prose was not already spending. Do not put it back under the
   headline "so the image is bigger" - being bigger was the complaint. */
.about-hero { display: grid; gap: var(--space-xl); }

@media (min-width: 60em) {
  .about-hero {
    /* The text column gets the fraction and the picture a bounded track, not
       the other way round, because the headline is what must not go narrow -
       at 1fr 1fr the h1 wrapped to five lines. The ceiling here is also what
       build.js promises the browser in the img's `sizes` attribute. Change one
       and the other stops being true.

       600px, raised from 560px on 2026-08-16 on a request for the picture to
       be "a bit bigger". THAT IS THE CEILING, and it was measured, not chosen:
       at the 1180px container the text column is then 468px and the h1 still
       sets in four lines. 610px takes it to five.

       `min(600px, 53%)` AND NOT `minmax(300px, 600px)`, which is what this was.
       A fixed maximum is greedy - it takes its full 600px at every width above
       60em, so the text column absorbs the whole shortfall. At a 1024 viewport
       that left the text 312px and the h1 setting in SEVEN lines. (It was
       already poor at the old 560px: 352px and six lines. This is a fix for
       both, not just for the regression I would otherwise have introduced.)

       At 53% the picture yields proportionally instead: 600px at the container
       cap, ~517px at 1024, and the headline holds its shape on the way down.
       53 is the smallest percentage that still reaches the full 600px at the
       cap - 52% stops at 589px. The old 300px floor is gone because it could
       never bind; 53% of the narrowest two-column container is already 483px. */
    grid-template-columns: minmax(0, 1fr) min(600px, 53%);
    gap: var(--space-2xl);
    /* WAS `center`. Changed 2026-08-16, asked for directly: the top of the
       picture should line up with the top of the heading. Centring cannot do
       that - it aligns the two columns' midpoints, so the offset drifts with
       every copy edit. `start` aligns the tops, and the figure then buys back
       the eyebrow's height below. */
    align-items: start;
  }
}

/* NO aspect-ratio AND NO object-fit HERE, DELIBERATELY.

   Until 2026-08-14 this figure forced a 3/4 window with `object-fit: cover`
   over what is really a 3:2 landscape master, waiting on a portrait crop that
   was supplied but never reached the repo. A 3:4 window over a 3:2 frame keeps
   exactly half the width, so both sides of the photograph were being thrown
   away. That was reported as the sides being cropped, and it was.

   The figure now takes the picture's own proportions and the column was widened
   from 420px to 560px to pay for the height the wider frame no longer needs.
   Do not reintroduce a fixed ratio to "tidy up" the box - any ratio that is not
   the file's own is a crop. */
.about-hero__media {
  margin: 0;
  border: 1px solid var(--glow-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-color: var(--mec-blue-soft);
  /* REPLACED a bespoke pairing this figure carried alone: a --mec-navy 12% lift
     plus --mec-blue 38%, both with negative spread and a y-offset, which tucked
     the glow under the frame instead of haloing it. That was the intent at the
     time; the request was to halo it. See --glow-shadow in tokens.css. */
  box-shadow: var(--glow-shadow);
}

.about-hero__media img {
  display: block;
  width: 100%;
  height: auto;
}

/* Lands the figure's top edge on the h1's top edge rather than the eyebrow's.
   `align-items: start` above aligns the two columns' tops, but the text column
   starts with the eyebrow, so without this the picture lines up with — ABOUT US
   — instead of with the headline. 0.75rem x 1.4 is the eyebrow's own font-size
   and line-height from base.css; --space-sm is its margin-bottom from this file.

   WRITTEN AS THE CALC AND NOT AS `33px` ON PURPOSE. Restyle the eyebrow and the
   calc follows. A hard number silently stops being correct and the alignment
   this was added for quietly goes away.

   MUST STAY BELOW `.about-hero__media`, which sets `margin: 0`. Both are
   (0,1,0) and a media query adds no specificity, so this only wins on source
   order. Written inside the query rather than as `margin: X 0 0` on the base
   rule because below 60em the hero is stacked and the offset would be a gap
   under the prose for no reason. */
@media (min-width: 60em) {
  .about-hero__media { margin-top: calc(0.75rem * 1.4 + var(--space-sm)); }
}

/* The three mission pillars.
   auto-fit rather than a fixed repeat(3, ...): at 300px minimum this gives 3
   up on desktop, 2 up on a tablet and 1 up on a phone with no breakpoints of
   its own. It also means a fourth pillar could be added without touching CSS. */
.pillar-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-lg);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Card on the tint band. --white on --band-tint is 1.09:1, so - exactly as
   with .page-hero__aside and .location-card - the fill is not what makes this
   a card. The border is. Do not strip it on the grounds that the card "already
   has a background". The --glow-border is more visible against the tint than
   the --rule hairline it replaced, so that constraint is better served now,
   not worse.

   The glow is the DEFAULT here but not universal - see .pillar-grid--plain
   below, which takes a grid back to the neutral hairline. */
.pillar {
  background-color: var(--white);
  border: 1px solid var(--glow-border);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--glow-shadow);
}

.pillar__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-md);
  border-radius: var(--radius);
  background-color: var(--mec-blue-soft);
  /* The icon inherits currentColor. --mec-blue-deep, not --mec-blue: at a
     1.5px stroke this is effectively hairline non-text content, and the deep
     blue clears the 3:1 floor of WCAG 1.4.11 against the soft-blue chip where
     the primary would be marginal. */
  color: var(--mec-blue-deep);
}

.pillar__icon svg { width: 22px; height: 22px; }

/* Centred variant. Three of the site's four pillar grids now use it: the About
   mission grid, the six benefit cards on /for-practices, and "At a glance" on
   /for-practices/refer. Only "How to send referrals" on refer is still left.

   STILL OPT-IN, not the default, even at three out of four. Each was centred by
   a separate explicit request; the fourth has not been asked for. If it ever is,
   that is the moment to invert this into a --left modifier - not before.

   The icon needs its own rule: it is a fixed 44px block-level grid box, so
   text-align on the card does nothing to it and it stays hard left. Only
   margin-inline is set, so the base rule's margin-bottom survives - do not
   collapse these into a `margin:` shorthand. */
.pillar-grid--center .pillar {
  text-align: center;
}

.pillar-grid--center .pillar__icon {
  margin-inline: auto;
}

/* Opt OUT of the blue glow, back to the neutral card edge every .pillar had
   before 2026-08-15. Added on request for the six benefit cards on
   /for-practices and then "At a glance" on /for-practices/refer: both are
   multi-row grids, where the glows meet in the gutters and the block reads as
   one tinted slab rather than as separate cards. Same problem that took the
   glow off .check-list. The About mission grid keeps it - single row of three.

   A .pillar-grid--glow modifier briefly existed instead, scoping the glow TO the
   About grid. It was deleted when the glow moved into the .pillar base rule.
   This is its inverse and it is the right direction: the glow is the default and
   grids opt out, so a new grid inherits the house style rather than missing it.

   (0,2,0) against .pillar's (0,1,0), so it wins on specificity and does not care
   where it sits in the file. */
.pillar-grid--plain .pillar {
  border-color: var(--rule);
  box-shadow: var(--shadow-sm);
}

.pillar__title {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.125rem;
  margin-bottom: var(--space-2xs);
}

.pillar__text {
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* Mobile-echo section: prose left, benefits right. */
.about-split { display: grid; gap: var(--space-xl); }

@media (min-width: 60em) {
  .about-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
    gap: var(--space-2xl);
    align-items: start;
  }
}

.about-split__lede p {
  color: var(--ink-soft);
  max-width: 60ch;
  margin-top: var(--space-md);
}

/* SECOND TIME THIS EXACT BUG HAS BEEN REPORTED. An eyebrow is a <p>, so the
   rule above (0,1,1) beat `.eyebrow` (0,1,0) and painted "— The model —"
   --ink-soft. It read as an eyebrow that had never been given a colour; it had
   been, and was being overridden. Identical to the .info-block .eyebrow fix
   further up this file - see that comment for why this is written as an
   additive rule rather than as `:not(.eyebrow)` on the rule above.

   If a THIRD `<container> p { color: ... }` rule ever captures an eyebrow,
   stop patching case by case and reconsider the base .eyebrow specificity -
   but note that raising it would put it above `.band--navy .eyebrow`, which
   currently wins on source order alone.

   NOTE the rule above also still applies max-width and margin-top to this
   eyebrow. Both are harmless here (the eyebrow is far shorter than 60ch, and
   it is the section's first child so the top margin collapses against the
   band's padding) and are left alone deliberately - only the colour was
   wrong. */
.about-split__lede .eyebrow {
  color: var(--mec-blue-deep);
}

/* Ticked benefits. A <ul> already announces "list, 4 items", and the tick is a
   decorative restatement of membership, which is why the mark is aria-hidden
   in the markup - otherwise every item is announced with a stray graphic. */
.check-list {
  display: grid;
  gap: var(--space-sm);
  list-style: none;
  margin: 0;
  padding: 0;
}

.check-list li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  /* NOT --glow-border/--glow-shadow. This component briefly took the site-wide
     blue glow on 2026-08-15 and was reverted the same day on request. A
     check-list is a dense stack of short rows at --space-sm gaps, so a 24px
     diffuse halo on each row overlaps its neighbours and the run reads as one
     blue smear rather than as separate items. The glow is for cards that stand
     alone with room around them. Keep this pairing. */
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background-color: var(--white);
  box-shadow: var(--shadow-sm);
}

.check-list__mark {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background-color: var(--mec-blue-deep);
  color: var(--white);
}

.check-list__mark svg { width: 14px; height: 14px; }

/* ICON VARIANT - one meaningful icon per item instead of four identical ticks.
   Opt-in via .check-list--icons on the <ul>, so the About page's standards list
   keeps its ticks. Deliberately the same chip as .pillar__icon (soft-blue
   rounded square, deep-blue line icon) rather than a third icon treatment.

   --mec-blue-deep, not --mec-blue: at a 1.5px stroke these are hairline
   non-text content, and the deep blue clears WCAG 1.4.11's 3:1 floor against
   the soft-blue chip where the primary would be marginal. Same reasoning as
   .pillar__icon - if you change one, change both. */
.check-list--icons .check-list__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius);
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

/* LUCIDE_MAP_PIN is authored with width/height="32" presentation attributes for
   the map popups it was written for. Those win over the SVG's intrinsic sizing,
   so without this the pin renders half again as large as its three neighbours.
   Sized here rather than stripped from the constant, which the map still uses. */
.check-list--icons .check-list__icon svg { width: 20px; height: 20px; }

/* "This model means:" - the sentence the list completes. Deliberately not a
   heading: it is a fragment, and promoting it to an h3 would put "This model
   means:" into the document outline, where it says nothing on its own. */
.list-lede {
  font-weight: 600;
  margin-bottom: var(--space-sm);
}

/* The clinical-standards list only. Its four items are full clauses of 12–18
   words, so one column leaves a very long, very thin stack. The model list next
   door stays single-column on purpose - its items are four words each, and two
   columns of that reads as scattered text rather than a list.

   align-items: start because these items wrap to two and three lines and a
   vertically centred tick beside a three-line item looks unattached. */
@media (min-width: 52em) {
  .check-list--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .check-list--two li { align-items: start; }
}

/* .band--navy sets color on the band itself and every descendant inherits it,
   including these cards - which have their own white fill, so the text would be
   white on white. The cards are correct on every other background, so the fix
   belongs here rather than on .check-list. (0,2,1) beats the band's own rule. */
.band--navy .check-list li { color: var(--ink); }

/* Accreditation marks, on a light card INSIDE the navy band. Both logos are
   dark ink on transparent: dropped straight onto the gradient they read as
   smudges. This is the same problem the footer solved by going light, solved
   the same way at component scale. Do not "simplify" by removing the card. */
/* STACKED AND CENTRED, and width-capped, on request 2026-08-14: as a full-width
   row with the label beside the logos it left a lake of white on the right.
   The card is sized to its contents now and centred in the band, rather than
   stretched across it. fit-content lets it shrink below the cap on narrow
   viewports without a second media query. */
.accred-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  width: fit-content;
  max-width: min(100%, 620px);
  margin-top: var(--space-xl);
  margin-inline: auto;
  padding: var(--space-lg) var(--space-2xl);
  border-radius: var(--radius-lg);
  background-color: var(--white);
  text-align: center;
}

/* The label's own styling, band-independent. This USED TO BE SCOPED TO
   .band--navy, which was fine while About was the only page with an accred
   bubble - until For Patients grew one on a tint band (2026-08-14) and the
   label rendered as ordinary sentence-case body copy, because none of this
   applied. Anything here that is genuinely about the navy context belongs in
   the override below, not in this rule. */
.accred-card__label {
  color: var(--ink-soft);
  font-size: 0.875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin: 0;
}

/* Colour only. `.band--navy p` is (0,1,1) and would paint this label
   --mec-blue-soft, i.e. near-invisible on the white card. (0,2,0) wins. */
.band--navy .accred-card__label { color: var(--ink-soft); }

.accred-card__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xl);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* 215px of layout from a 320px file. Enlarged from 150px on request
   2026-08-14 (~43%). THIS IS AT THE CEILING: 215px is 1.49x density on the
   -320 derivatives, and going further would start resampling upward on
   ordinary screens. If these need to be bigger again, generate larger
   derivatives first - do not just raise this number.

   Same derivatives the footer uses; the full-size originals in
   assets/img/logos are archive copies. */
.accred-card__list img {
  display: block;
  width: 100%;
  max-width: 215px;
  height: auto;
}

/* Locations teaser. Generated from content/locations.json in build.js, so the
   count and the names cannot drift from /locations. */
/* Flex, not the auto-fit grid every other card set on this site uses, and the
   reason is the number five. An auto-fit grid puts four chips on the first row
   and leaves the fifth hard against the left edge under a centred heading,
   which reads as a layout that ran out. Flex with a centred wrap puts the
   remainder in the middle of its row, where it reads as deliberate.

   1 1 200px lets all five sit on one row above ~1100px of container and fall to
   4 + 1 below it; max-width stops a lone chip on the last row from stretching
   to the full container. Revisit the basis if a sixth location is added. */
.loc-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

.loc-chips > li {
  flex: 1 1 200px;
  max-width: 260px;
}

/* Icon ABOVE the text, not beside it. Five chips across a 1132px container is
   about 207px each, and a 36px icon plus its gap took 52px of that before the
   host practice name got a character - "Williamstown Specialist Centre" came
   out as three ragged lines. Stacking gives the text the full column. */
/* Centred: align-items does the icon, text-align does the two text lines. The
   icon is a fixed 36px box and the chip is a flex COLUMN, so align-items is the
   cross axis and centres it - it needs no margin of its own (unlike .pillar__icon,
   whose card is not a flex container).

   Border and glow come from the shared tokens - see --glow-border in tokens.css. */
.loc-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-sm);
  height: 100%;
  padding: var(--space-md);
  border: 1px solid var(--glow-border);
  border-radius: var(--radius);
  background-color: var(--white);
  box-shadow: var(--glow-shadow);
  color: inherit;
  text-decoration: none;
}

/* :hover lives in the shared lift-card block below, not here. It sets
   border-color to full-strength --mec-blue and its own heavier shadow, so it
   still reads as a lift against this resting glow rather than being cancelled
   by it. Both properties are already in that block's transition list. */

.loc-chip__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius);
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

/* The shared LUCIDE_MAP_PIN carries width/height="32" for the map popups it was
   written for. Presentation attributes lose to any CSS rule, so this resizes it
   without needing a second copy of the icon. */
.loc-chip__icon svg { width: 20px; height: 20px; }

.loc-chip__body { display: grid; gap: var(--space-3xs); }
.loc-chip__name { font-weight: 600; }

.loc-chip__host {
  color: var(--ink-soft);
  font-size: 0.875rem;
  line-height: 1.4;
}

/* Inline "read on" link, used for "See all locations". Centred to sit under the
   centred .sec-head above it, same reasoning as .band__actions. */
.band__footer-link {
  margin-top: var(--space-lg);
  text-align: center;
}

.arrow-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-weight: 600;
  color: var(--mec-blue-deep);
}

.arrow-inline svg {
  width: 18px;
  height: 18px;
  transition: transform 160ms ease;
}

.arrow-inline:hover svg { transform: translateX(3px); }

/* Two link cards to the For Practices sub-pages. Whole card is the link, so the
   label, the description and the arrow are all one target rather than three. */
.arrow-links { display: grid; gap: var(--space-md); }

@media (min-width: 52em) {
  .arrow-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.arrow-link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-md);
  row-gap: var(--space-3xs);
  align-items: center;
  padding: var(--space-lg);
  /* NOT --glow-border/--glow-shadow - took it on 2026-08-15, reverted the same
     day on request. This card's hover state already swaps in a blue border and
     a blue shadow (see the shared lift block below); a blue resting state left
     hover with nothing to announce. .location-card and .nav-card share that
     hover block and DID keep the glow - they sit in grids of five and six where
     the glow groups them, whereas this appears once or twice inside body copy. */
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  background-color: var(--white);
  box-shadow: var(--shadow-sm);
  color: inherit;
  text-decoration: none;
}

/* SHARED LINK-CARD LIFT. Added 2026-08-14; extended to .location-card and
   .nav-card on 2026-08-14 so every card on the site that links somewhere
   behaves the same way. THIS IS THE ONE DEFINITION - see the .location-card
   comment above, which already made that promise for the resting state.

   .lift-card is the opt-in hook for future components: add the class in the
   markup rather than adding another selector to these lists.

   THIS REPLACED the bespoke :hover rules these cards used to carry (.arrow-link
   and .loc-chip stepped their shadow up; .location-card/.nav-card lifted 2px).
   Do not reinstate any of them alongside this block - same specificity, so they
   would fight over box-shadow by source order.

   Three things happen together: the card lifts 4px, a blue glow appears beneath
   it, and a bar draws in along the top edge.

   TWO DIFFERENT KEYBOARD SHAPES ARE HANDLED HERE, deliberately:
     - .arrow-link and .loc-chip ARE the <a>, so :focus-visible matches the card.
     - .location-card and .nav-card are <li>s containing a stretched link
       (.nav-card__title a::after covers the card). The <li> is not focusable,
       so :focus-visible would never fire on it - :has(a:focus-visible) is what
       makes those two keyboard-reachable. Removing it silently drops the focus
       affordance for every hub and locations card on the site.

   THE BAR IS CLIPPED BY overflow: hidden ON THE CARD, which rounds its ends to
   follow the corner radius. All four carry overflow already and none has
   content that needs to escape. An element's own focus outline is not clipped
   by its own overflow, so the focus ring survives. */
:is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card) {
  position: relative;
  overflow: hidden;
  transition:
    transform 220ms cubic-bezier(0.2, 0, 0.2, 1),
    box-shadow 220ms cubic-bezier(0.2, 0, 0.2, 1),
    border-color 220ms cubic-bezier(0.2, 0, 0.2, 1);
}

:is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card)::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background-color: var(--mec-blue);
  transform: scaleX(0);
  transform-origin: left center;
  /* Above the stretched link's ::after overlay, which is transparent but paints
     later and would otherwise sit over the bar. */
  z-index: 1;
  transition: transform 220ms cubic-bezier(0.2, 0, 0.2, 1);
}

/* :focus-visible carries the identical treatment, not a reduced version - a
   keyboard user gets the same affordance a mouse user does. Grouped rather than
   duplicated so the two cannot drift apart. */
:is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card):hover,
:is(.arrow-link, .loc-chip, .lift-card):focus-visible,
:is(.location-card, .nav-card, .lift-card):has(a:focus-visible) {
  transform: translateY(-4px);
  border-color: var(--mec-blue);
  box-shadow:
    0 2px 6px -2px color-mix(in srgb, var(--mec-navy) 10%, transparent),
    0 14px 32px -10px color-mix(in srgb, var(--mec-blue) 45%, transparent);
}

:is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card):hover::before,
:is(.arrow-link, .loc-chip, .lift-card):focus-visible::before,
:is(.location-card, .nav-card, .lift-card):has(a:focus-visible)::before {
  transform: scaleX(1);
}

/* Explicit grid areas rather than source order: the arrow spans both rows in
   the second column, which auto-placement cannot express. */
.arrow-link__label {
  grid-area: 1 / 1;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.arrow-link__text {
  grid-area: 2 / 1;
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.arrow-link__icon {
  grid-area: 1 / 2 / 3 / 3;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

.arrow-link__icon svg {
  width: 18px;
  height: 18px;
  transition: transform 160ms ease;
}

.arrow-link:hover .arrow-link__icon svg,
.arrow-link:focus-visible .arrow-link__icon svg { transform: translateX(2px); }

/* Centred because the section header above it is centred; a left-aligned button
   under a centred heading reads as a stray. */
.band__actions {
  margin-top: var(--space-xl);
  text-align: center;
}

/* Contact details in the closing navy panel. A <dl>, not a <table>: there is
   one dimension here - label, value - and a table would announce phantom
   columns. Each row is a <div> wrapper, which is valid inside <dl> and is what
   lets a row be a grid. */
.contact-rows {
  display: grid;
  gap: var(--space-2xs);
  max-width: 30rem;
  margin: var(--space-lg) auto 0;
  text-align: left;
}

/* A FIXED term column, not `auto`. With auto, each row sizes its own label and
   the values start at different x positions down the list, which on two rows
   reads as a misalignment rather than as a shape. 6.5rem holds "Phone" and
   "Email" with their icons at the body size used here. */
.contact-row {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  /* Glass, not a solid fill: a white card here would fight the accreditation
     card two sections up for attention, and the gradient behind it is the point
     of the band. 20% white on this gradient measures ~1.5:1 against the band -
     a visible edge, not a contrast-carrying one, which is all a container
     boundary has to be. The TEXT is what has to clear AA, and it does. */
  border: 1px solid color-mix(in srgb, var(--white) 20%, transparent);
  background-color: color-mix(in srgb, var(--white) 8%, transparent);
}

/* justify-content: start is not optional. Two `auto` tracks in a grid that is
   wider than its content stretch to fill it, so the icon ended up centred in
   its own half and the label pushed to the middle - which looked like centred
   text inside a left-aligned list. Packing the tracks to the start puts the
   icon and the word next to each other, which is what they are. */
.contact-row__term {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  gap: var(--space-2xs);
  align-items: center;
  font-weight: 600;
  color: var(--sky-on-navy);
}

.contact-row__icon { display: grid; place-items: center; }
.contact-row__icon svg { width: 18px; height: 18px; }

/* overflow-wrap is the last resort, not the plan. The email address is 32
   characters and cannot hold one line on a 375px phone; build.js puts a <wbr>
   after its "@" so the line breaker takes that opportunity first and the
   address splits at the "@" instead of mid-domain. This declaration only comes
   into play below about 320px or under heavy text-only zoom. Same arrangement
   as .site-footer__email - see the note there. */
.contact-row__value {
  margin: 0;
  overflow-wrap: anywhere;
}

.contact-row__value a {
  color: var(--white);
  text-decoration-color: color-mix(in srgb, var(--white) 45%, transparent);
}

.contact-row__value a:hover { text-decoration-color: var(--white); }

/* .cta-panel__actions carries a small top margin sized for sitting directly
   under a paragraph. Following a bordered list it needs the gap of a separate
   block, or the button looks like a fourth row that has escaped its box. */
.contact-rows + .cta-panel__actions { margin-top: var(--space-lg); }

/* Below ~30rem of viewport the two-column row leaves about 150px for a 32
   character email address, which breaks it across three lines beside a label
   sitting on one. Stacking gives the address the full width of the card. */
@media (max-width: 30em) {
  .contact-row { grid-template-columns: 1fr; gap: var(--space-3xs); }
}

@media (prefers-reduced-motion: reduce) {
  .arrow-inline svg,
  .arrow-link__icon svg { transition: none; }

  .arrow-inline:hover svg,
  .arrow-link:hover .arrow-link__icon svg,
  .arrow-link:focus-visible .arrow-link__icon svg { transform: none; }

  /* The card keeps its colour and shadow response and loses only the movement.
     The top bar still appears - it is snapped to full width with the transition
     off rather than hidden, because it is an affordance, not decoration, and a
     reduced-motion user needs the same hover feedback everyone else gets. */
  :is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card) {
    transition: box-shadow 220ms ease, border-color 220ms ease;
  }

  :is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card)::before {
    transition: none;
  }

  :is(.arrow-link, .loc-chip, .location-card, .nav-card, .lift-card):hover,
  :is(.arrow-link, .loc-chip, .lift-card):focus-visible,
  :is(.location-card, .nav-card, .lift-card):has(a:focus-visible) {
    transform: none;
  }
}

/* ==========================================================================
   Numbered step process
   --------------------------------------------------------------------------
   Spec: design-system.md → "Numbered step process".
   Used on the For Patients hub; For Practices inherits it by adding a `steps`
   array to its descriptor in build.js, with no CSS change.
   ========================================================================== */

/* No outer margin here on purpose. This section used to carry
   `margin-block: var(--space-2xl)`, because it sat between two .info-blocks and
   broke their `.info-block + .info-block` adjacency, leaving the heading below
   at a 0px gap. It now occupies a band of its own and the band's padding
   supplies the space - see the band rule for why a section inside a band must
   not add block margins of its own.

   Its header comes from the shared .sec-head formula, which lives above with
   the other section-level primitives rather than here. It was defined in this
   block when the steps were the only thing using it. */

/* COLUMN COUNT IS NOT HARD-CODED. This was `repeat(4, 1fr)` until 2026-08-14,
   which was correct only because For Patients happened to have four steps. The
   two For Practices processes have three, and a fixed four-track grid left them
   with a dead column and a row that stopped two-thirds of the way across.
   `grid-auto-flow: column` gives one equal track per <li> for any count, so a
   step can be added or removed in build.js without touching CSS.
   The wrapping breakpoints below must restore `grid-auto-flow: row`, or the
   grid-template-columns they set would be ignored. */
.steps {
  list-style: none;                 /* the <ol> is for semantics, not markers */
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--space-md);
}

.step {
  position: relative;
  text-align: center;
}

/* Connecting hairline.
   Drawn on each LI rather than as one line behind the whole row: a single
   background line has to be masked at both ends and still overshoots when the
   step count changes. Per-item it just stops at the last one.
   The 34px insets clear the 54px circle (27px radius) plus a little air, so
   the line meets the ring instead of touching it. */
.step:not(:last-child)::before {
  content: '';
  position: absolute;
  top: 27px;
  left: calc(50% + 34px);
  right: calc(-50% + 34px);
  height: 1px;
  /* Was a flat --rule hairline until 2026-08-13. The gradient makes the row
     read as a journey with a direction rather than as four unrelated circles
     joined by a wire, and it is the one legitimate use of --sky in this
     component: a connector conveys nothing that isn't also carried by the
     numbers, so it is decorative and the 3.0 non-text floor doesn't bind.
     Don't promote this colour to anything a reader has to see. */
  background: linear-gradient(90deg, var(--sky), color-mix(in srgb, var(--mec-blue) 35%, transparent));
}

.step__num {
  position: relative;
  z-index: 1;                        /* sits over the connector, not under it */
  width: 54px;
  height: 54px;
  margin: 0 auto var(--space-sm);
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--white);
  border: 1px solid color-mix(in srgb, var(--mec-blue) 40%, transparent);
  color: var(--mec-blue-deep);
  font: 600 1.125rem/1 var(--font-body);
  /* Lining tabular figures so 1 and 4 occupy the same width and every circle
     looks optically centred. */
  font-variant-numeric: lining-nums tabular-nums;
  transition: background-color 160ms ease, border-color 160ms ease,
              color 160ms ease, transform 160ms ease;
}

/* EVERY NODE IS IDENTICAL. Do not single one out again.

   The last step used to be filled with a glow - the prototype's "glowing
   endpoint", marking the payoff of the report reaching the doctor. Removed
   2026-08-15 on request: read as a row of numbers where the last one had been
   left in a different state, rather than as deliberate emphasis. A sequence of
   equal steps is a sequence; highlighting the end of it invites the reader to
   ask what is special about step 4, and the answer was only "it is last".

   If a step ever does need emphasis, it should be driven by something in the
   markup that says WHY, not by :last-child, which just means "the end". */

/* Hover - DECORATIVE ONLY.
   Nothing is hidden behind this, so no keyboard equivalent is owed and the
   steps must not be given tabindex (that would put non-interactive content in
   the tab order). Triggered from .step, not .step__num, so the whole column is
   the target rather than a 54px circle.
   White on --mec-blue is 4.89:1, above the 4.5 floor. */
.step:hover .step__num {
  background: var(--mec-blue);
  border-color: var(--mec-blue);
  color: var(--white);
  transform: translateY(-3px);
}

.step__tag {
  display: inline-block;
  font: 600 0.6875rem/1 var(--font-body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* --mec-blue-deep, NOT --mec-blue: the primary is 4.27:1 on this tint and
     fails at pill size. See the trap noted in tokens.css. */
  color: var(--mec-blue-deep);
  background: var(--mec-blue-soft);
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: 999px;
  margin-bottom: var(--space-2xs);
}

.step__title {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.0625rem;
  margin-bottom: var(--space-2xs);
}

.step__desc {
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.6;
  max-width: 26ch;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   Steps on a navy band
   --------------------------------------------------------------------------
   Every colour in this component was chosen against a light background and all
   of them invert on navy. Listed together rather than scattered so it stays
   obvious that this is one coherent restatement, not a pile of patches.

   This band previously carried its own inverted version of the last-node
   highlight - 1-3 translucent, 4 solid white with a white halo - because
   filling 4 with a dark colour on an already-dark band would have made the
   emphasised step recede. Both the light and navy highlights were removed
   2026-08-15; all four nodes are identical everywhere now. The navy rules that
   remain are a straight restatement of the light ones, with no per-step cases.
   -------------------------------------------------------------------------- */

.band--navy .step__num {
  background: rgb(255 255 255 / 0.08);
  border-color: rgb(255 255 255 / 0.35);
  color: var(--white);
}

.band--navy .step:hover .step__num {
  background: rgb(255 255 255 / 0.2);
  border-color: var(--white);
  color: var(--white);
}

/* The tag pill is --mec-blue-deep on --mec-blue-soft, which is a light-surface
   recipe; both halves have to change. 0.14 white behind --white gives a legible
   pill without introducing a second bright rectangle per column. */
.band--navy .step__tag {
  color: var(--white);
  background: rgb(255 255 255 / 0.14);
}

.band--navy .step__title { color: var(--white); }
.band--navy .step__desc { color: var(--mec-blue-soft); }

/* The connector's --sky end is legitimate on navy - decorative, and it is the
   accent's intended home - but its --mec-blue end drops to 1.79:1 and the line
   appears to stop halfway. Fading to white keeps the taper visible. */
.band--navy .step:not(:last-child)::before {
  background: linear-gradient(90deg, var(--sky), rgb(255 255 255 / 0.25));
}

/* --------------------------------------------------------------------------
   CTA panel on a navy band
   --------------------------------------------------------------------------
   The panel's whole job on a light page is to be a distinct tinted card. On
   navy the band already IS that emphasis, so a second container inside it is a
   box within a box. The panel dissolves and lets the band do the work.
   -------------------------------------------------------------------------- */
.band--navy .cta-panel {
  background-color: transparent;
  padding: 0;
}

/* The connector is hidden wherever the row wraps - a line pointing off the end
   of a row reads as a broken graphic, which is worse than no line at all. */
@media (max-width: 60em) {
  .steps {
    grid-auto-flow: row;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-lg);
  }
  .step:nth-child(2)::before { display: none; }
}

@media (max-width: 34em) {
  .steps { grid-auto-flow: row; grid-template-columns: 1fr; }
  .step::before { display: none; }
}

/* Colour change is kept - it is the part that communicates. Only the movement
   is dropped, for readers who have asked for less of it. */
@media (prefers-reduced-motion: reduce) {
  .step__num { transition: none; }
  .step:hover .step__num { transform: none; }
}

/* ==========================================================================
   For Practices components
   ==========================================================================
   Added 2026-08-14 for the /for-practices section. Every one of these is a
   generic block part emitted by contentBlock() in build.js, not a page-specific
   style - any section can use them by adding the matching key to a descriptor.
   Named for what they are (two-lists, data-table, dl-card), never for the page
   that happened to need them first.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Secondary link on a hub card  (.nav-card__extra)
   --------------------------------------------------------------------------
   "Download referral template →" under the Refer card on the For Practices hub.

   TWO THINGS MAKE THIS WORK AND BOTH ARE LOAD-BEARING:

   1. z-index. .nav-card__title a::after is a transparent overlay covering the
      entire card. It paints after this paragraph in source order, so without a
      stacking context this link is visually present and completely unclickable
      - every click lands on the stretched link and goes to the wrong page.
   2. It sits outside .nav-card__body, which is the flex child carrying
      flex-grow: 1. Inside it, the extra link would float up under the
      description on a card with short copy; outside, it pins to the bottom edge
      and the two cards' links line up regardless of description length.

   The top border is a divider, not decoration: it is what says "this is a
   second, different destination" rather than a third line of the description. */
.nav-card__extra {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-top: 1px solid var(--rule);
  font-size: 0.9375rem;
  font-weight: 600;
}

.nav-card__extra a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  /* --mec-blue-deep, not --mec-blue: this is 15px text and the primary blue is
     4.27:1 on white - under the 4.5 floor. See the note in tokens.css. */
  color: var(--mec-blue-deep);
  text-decoration: none;
}

.nav-card__extra a:hover { text-decoration: underline; }

/* --------------------------------------------------------------------------
   Two headed lists side by side  (.two-lists)
   --------------------------------------------------------------------------
   "We bring" / "You provide" on /for-practices/partner.

   The columns are NOT cards. Each .check-list li is already a white bordered
   card, so wrapping four of them in a fifth card gives a box inside a box inside
   a box. The <h3> and the column gap are the only grouping needed.

   Single column below 52em rather than at some narrower width, because that is
   where .check-list--two also collapses - one breakpoint for list layout across
   the site instead of two that nearly agree. */
.two-lists {
  display: grid;
  gap: var(--space-lg);
}

@media (min-width: 52em) {
  .two-lists { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.two-lists__title {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.125rem;
  margin-bottom: var(--space-sm);
}

/* --------------------------------------------------------------------------
   Data table  (.table-scroll / .data-table)
   --------------------------------------------------------------------------
   The MBS item summary on /for-practices/refer. Content brief: legally vetted,
   not to be simplified or reworded - which is also why it is not reflowed into
   cards on mobile. It scrolls instead, so every row keeps all four of its
   columns and the reader never sees a partial statement of an MBS restriction.

   The wrapper is the scroller AND the focus target. It carries tabindex="0" and
   role="region" from build.js; a scrollable area that can only be reached with a
   pointer is a WCAG 2.1.1 (Keyboard) failure. The focus ring below is therefore
   a real affordance, not a default worth suppressing.

   BORDER LIVES ON THE WRAPPER, not the table: the wrapper is what clips, so a
   border on the table would scroll out of view at the right-hand edge. */
.table-scroll {
  overflow-x: auto;
  /* NOT --glow-border/--glow-shadow - took it on 2026-08-15, reverted the same
     day on request. A data table is reference material the reader scans, not a
     card being offered; the blue edge gave the MBS item list a promotional
     weight it should not carry. */
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background-color: var(--white);
  box-shadow: var(--shadow-sm);
}

.table-scroll:focus-visible {
  outline: 2px solid var(--mec-blue-deep);
  outline-offset: 2px;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  /* Cells hold short clauses ("Once per 24 months"); below this they wrap to
     four lines each and the table becomes taller than the section around it.
     This is the width the horizontal scroll exists to deliver. */
  min-width: 40rem;
  font-size: 0.9375rem;
  line-height: 1.5;
}

/* The caption is a visible label, not a hidden one. Six rows of bare MBS item
   numbers are unreadable without a sentence saying what the table enumerates,
   for sighted readers as much as for screen-reader users - so it is styled to
   be read rather than pushed off-screen. */
.data-table caption {
  caption-side: top;
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--rule);
  background-color: var(--band-tint);
  font-weight: 600;
  color: var(--ink);
}

.data-table th,
.data-table td {
  padding: var(--space-xs) var(--space-md);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--rule);
}

.data-table thead th {
  background-color: var(--band-tint);
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* The row header is the MBS item number. Tabular figures so the column reads as
   a column of numbers rather than as ragged text. */
.data-table tbody th {
  font-weight: 700;
  white-space: nowrap;
  font-variant-numeric: lining-nums tabular-nums;
}

.data-table td { color: var(--ink-soft); }

/* No hairline under the last row - the wrapper's own border is already there,
   and the two together read as a double rule. */
.data-table tbody tr:last-child :is(th, td) { border-bottom: 0; }

/* --------------------------------------------------------------------------
   Download cards  (.dl-grid / .dl-card)
   --------------------------------------------------------------------------
   The referral template and printable form on /for-practices/refer.

   .dl-card opts into the shared card lift by carrying .lift-card in the markup,
   which is the mechanism that block documents. Do NOT add .dl-card to those
   :is() lists - the hook exists so this file does not accumulate a selector per
   component.

   Unlike .nav-card and .location-card these are NOT stretched-link cards. The
   whole card is not clickable, because the click has a side effect - it puts a
   file on the reader's disk - and a 300px target that silently downloads
   something when brushed past is the wrong affordance. The <a download> button
   is the only hit area. :has(a:focus-visible) in the shared block still gives
   the card its keyboard state when that button is tabbed to. */
.dl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-lg);
  list-style: none;
  margin: 0;
  padding: 0;
}

.dl-card {
  display: flex;
  flex-direction: column;
  background-color: var(--white);
  border: 1px solid var(--glow-border);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--glow-shadow);
}

/* Same chip as .pillar__icon and .check-list--icons: soft-blue rounded square,
   deep-blue line icon. Three components, one icon treatment. If the chip changes
   it changes in all three or the page grows a third dialect. */
.dl-card__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-md);
  border-radius: var(--radius);
  background-color: var(--mec-blue-soft);
  color: var(--mec-blue-deep);
}

.dl-card__icon svg { width: 22px; height: 22px; }

.dl-card__title {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.125rem;
  margin-bottom: var(--space-2xs);
}

.dl-card__desc {
  color: var(--ink-soft);
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* margin-top: auto pins the button to the bottom of the card, so the two cards'
   buttons align even though one description is a line longer than the other. */
.dl-card__action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) var(--space-sm);
  margin-top: auto;
  padding-top: var(--space-md);
}

/* --ink-soft (8.06:1), NOT --ink-mute, which the content brief asked for.
   --ink-mute is 3.14:1 on paper and tokens.css restricts it to large text; this
   is 13px, so the brief's colour would be a WCAG 1.4.3 failure on a page that
   has to hold 90+ on Lighthouse accessibility (CLAUDE.md:308). The intent -
   metadata that reads quieter than the button - is delivered by the size and
   weight instead. Flagged rather than silently substituted. */
.dl-card__meta {
  font-size: 0.8125rem;
  color: var(--ink-soft);
  font-variant-numeric: lining-nums tabular-nums;
}

/* --------------------------------------------------------------------------
   Fine print  (.fine-print)
   --------------------------------------------------------------------------
   The closing caveat under a block - the MBS table's "guidance only, confirm
   eligibility against current MBS", and the template note.

   Small and quiet, but NOT --ink-mute for the same reason as .dl-card__meta:
   this text is a compliance qualifier and it is the last text anyone should have
   to squint at. 0.875rem on --ink-soft is 8.06:1.

   The left rule marks it as an aside on the block above without needing a box,
   which at this length would be heavier than the point deserves. */
.fine-print {
  margin-top: var(--space-md);
  padding-left: var(--space-sm);
  border-left: 2px solid var(--rule);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* On navy the --rule hairline is 1.3:1 and the marker vanishes; --sky-on-navy is
   the band's own established hairline. Text goes to the band's inherited colour
   by not being restated - --ink-soft on navy would be 2.0:1. */
.band--navy .fine-print {
  border-left-color: var(--sky-on-navy);
  color: inherit;
}

/* ==========================================================================
   FAQ  (/faq)
   ==========================================================================
   Two components: the category jump bar and the accordion.

   NO NEW TOKENS. Everything below resolves to the existing colour, spacing and
   radius scales. If something here looks like it wants a value that does not
   exist yet, that is a signal the component is wrong, not the scale.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Category jump bar  (.faq-jump)
   --------------------------------------------------------------------------
   STICKY ON DESKTOP ONLY, and the breakpoint is not cosmetic. .site-header is
   already position: sticky at 73px (72px min-height + its 1px bottom border).
   On a 375x812 phone a second sticky bar wraps its five chips onto two rows,
   so the two bars together would eat ~180px - a quarter of the viewport -
   permanently, on a page whose entire purpose is reading. Below 52em it scrolls
   away like normal content and the chips still work as in-page links.

   52em is the site's list-layout breakpoint (.two-lists, .check-list--two,
   .arrow-links). Reusing it keeps the site on breakpoints it already has.

   z-index 40 is BELOW the header's 50, deliberately: when this bar sticks it
   must slide under the header, not over it.

   The background must be opaque. A sticky bar over --paper with a transparent
   fill shows the accordion text scrolling through it. */
.faq-jump {
  background-color: var(--paper);
  border-block: 1px solid var(--rule);
  padding-block: var(--space-sm);
}

@media (min-width: 52em) {
  .faq-jump {
    position: sticky;
    top: 73px;
    z-index: 40;
  }
}

.faq-jump__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

/* ONE ROW THAT SCROLLS SIDEWAYS ON PHONES. Wrapped, the five chips take four
   ragged rows and 194px at 375px - a quarter of the viewport spent on
   navigation, above the first question, on a page that exists to be read. As a
   single scrolling row it is 52px.

   This does NOT breach WCAG 1.4.10 (Reflow), which prohibits two-dimensional
   scrolling of the PAGE; a horizontally scrollable component inside a page that
   still scrolls only vertically is explicitly allowed, and the page's
   scrollWidth stays at 375.

   Keyboard access is preserved by the links themselves: every chip is a real
   <a> in the tab order, and browsers scroll a focused element into view, so the
   row can be traversed without a pointer. Do not add `scrollbar-width: none` -
   the scrollbar is the only affordance saying there is more to the right.

   The negative margin + matching padding bleeds the row to the screen edges, so
   a chip scrolled halfway out is clipped by the viewport rather than appearing
   to stop short at an invisible container edge. */
@media (max-width: 52em) {
  .faq-jump__list {
    flex-wrap: nowrap;
    overflow-x: auto;
    margin-inline: calc(var(--space-md) * -1);
    padding-inline: var(--space-md);
    scroll-padding-inline: var(--space-md);
  }

  /* THE <li> IS THE FLEX ITEM, not the <a> inside it. Putting this on
     .faq-jump__link does nothing at all: the anchor is not a child of the flex
     container, so it never receives a flex-shrink of 0, and the li shrinks
     instead - which wrapped "Bookings and referrals" onto three lines and made
     an 82px chip in a row that was supposed to be 36px. */
  .faq-jump__list > li { flex: 0 0 auto; }
}

/* --mec-blue-deep (7.31:1 on --paper), not --mec-blue. These are 15px chips and
   the primary blue is 4.27:1 on white / 4.67:1 on paper - too close to the 4.5
   floor to spend on a decorative pill. Same reasoning as .nav-card__extra a. */
.faq-jump__link {
  display: inline-block;
  padding: var(--space-3xs) var(--space-sm);
  border: 1px solid var(--rule);
  border-radius: 999px;
  background-color: var(--white);
  color: var(--mec-blue-deep);
  font-size: 0.9375rem;
  font-weight: 600;
  /* A two-word label broken across two lines stops reading as a chip. There is
     always room for the longest label on one line, at every width the bar is
     used at. */
  white-space: nowrap;
  text-decoration: none;
  transition:
    background-color 160ms ease,
    border-color 160ms ease;
}

.faq-jump__link:hover {
  background-color: var(--mec-blue-soft);
  border-color: var(--mec-blue);
}

/* --------------------------------------------------------------------------
   FAQ sections and accordion  (.faq-body, .faq-section, .faq-item)
   --------------------------------------------------------------------------
   .faq-body is capped rather than left at container width. An accordion row is
   a single line of text at rest, and a question set in one 1132px line reads as
   a table of contents rather than a question. 80ch is wider than the site's
   .content-column (70ch) because the answers are short and the chevron needs
   somewhere to sit that is not a whole screen away from the question. */
.faq-body { max-width: 80ch; }

/* Clears the sticky header AND the sticky jump bar, so a chip click does not
   drop the section heading underneath both of them.

   73px header (72px min-height + 1px border) + 68px bar (32px padding + 36px
   chip) + 16px of air = 157px. MEASURED, not estimated: the first version of
   this rule guessed the bar at 51px and the heading landed 1px underneath it,
   which is invisible in a screenshot and obvious with a bounding box. If the
   chip padding, the chip font size or the bar's padding-block changes, re-measure
   rather than adjusting by eye.

   Below 52em the bar is static, so only the header is in the way - 96px is the
   same clearance .team-member uses for the same reason. */
.faq-section { scroll-margin-top: 157px; }

@media (max-width: 52em) {
  .faq-section { scroll-margin-top: 96px; }
}

.faq-section + .faq-section { margin-top: var(--space-2xl); }

.faq-section__title {
  font-size: clamp(1.5rem, 2.5vw, 1.875rem);
  margin-bottom: var(--space-md);
}

/* Hairline-separated rows, not cards. Each row is one line at rest; giving each
   its own bordered white box turns five short questions into five objects and
   the page stops reading as a list. The rule between them is the whole
   separation the component needs. */
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-item:first-of-type { border-top: 1px solid var(--rule); }

/* list-style: none plus the -webkit- pseudo-element is the two-line incantation
   that removes the native disclosure triangle across engines. Both are needed:
   Safari ignores the first, Firefox and Chrome ignore the second. Removing
   either brings the default marker back alongside our chevron. */
.faq-q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-md);
  cursor: pointer;
  list-style: none;
}

.faq-q::-webkit-details-marker { display: none; }

/* The <h3> inside the summary. font-size is set here rather than inherited
   because base.css sizes h3 for a section heading, and at that size a list of
   nine questions reads as nine sections. */
.faq-q__text {
  font-size: 1.0625rem;
  line-height: 1.4;
  margin: 0;
  transition: color 160ms ease;
}

/* --mec-blue-deep for the same reason as the jump chips: this is 17px text, and
   --mec-blue is 4.67:1 on --paper. It clears the floor but not by enough to be
   worth spending on a hover state when a deeper blue in the same ramp is
   7.31:1 and reads as the same colour. */
.faq-q:hover .faq-q__text,
.faq-q:focus-visible .faq-q__text {
  color: var(--mec-blue-deep);
}

.faq-q__chevron {
  display: flex;
  flex-shrink: 0;
  /* Optically aligns the 20px arrow with the first line of a 17px question at
     line-height 1.4, which is 24px tall. */
  margin-top: 2px;
  color: var(--mec-blue);
  transition: transform 200ms ease;
}

.faq-q__chevron svg {
  width: 20px;
  height: 20px;
}

.faq-item[open] .faq-q__chevron { transform: rotate(180deg); }

.faq-a {
  padding-bottom: var(--space-md);
  /* Stops the answer running under the chevron's column, so the open and closed
     states share a left edge and the same right margin. */
  padding-right: var(--space-2xl);
  color: var(--ink-soft);
}

.faq-a > * + * { margin-top: var(--space-sm); }

@media (max-width: 34em) {
  .faq-a { padding-right: 0; }
}

/* Same treatment as .nav-card__extra a - a secondary destination under a block
   of copy - but without the border, because here the answer above it is already
   bounded by the accordion row. */
.faq-a__more a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  color: var(--mec-blue-deep);
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
}

.faq-a__more a:hover { text-decoration: underline; }

.faq-a__more svg {
  width: 16px;
  height: 16px;
}

/* ==========================================================================
   Hero actions and trust bar  -  shared components
   --------------------------------------------------------------------------
   This was the "Home page" section until 2026-08-21. There is no homepage:
   About became the site root on 2026-08-16 and buildHome() was deleted from
   build.js the same day.

   The dead rules that used to sit here - .home-hero, .home-hero__media and
   .home-doctor__note - were kept for a while against the homepage coming back,
   paired with content/homepage.md. Robert deleted that brief on 2026-08-21, and
   the note here said the two should live and die together, so the rules went
   with it. Recover them from git if a homepage is ever commissioned; do not
   reconstruct them from memory.

   WHAT REMAINS IS LIVE AND IS NOT HOMEPAGE CODE. .hero-actions and .trust-bar
   both moved to the About hero before the homepage was dropped and are emitted
   by heroActions() / trustBar() in build.js. They stay here because this is
   where their reasoning was worked out.
   ========================================================================== */

/* Wraps rather than shrinking the buttons: at 375px the two labels together are
   wider than the viewport, and .btn has a fixed padding scale that must not be
   overridden per page.

   SHARED, despite the "Home page" banner above - it is in the About hero too as
   of 2026-08-16. Renamed from .home-hero__actions at the same time, because a
   `home-hero__` element class sitting inside .about-hero is a lie that costs
   the next person a grep. Emitted by heroActions() in build.js. */
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

/* Trust bar: the thin strip under the hero.

   SHARED, despite living under the "Home page" banner above - it is on the
   About hero too as of 2026-08-16, emitted by trustBar() in build.js. It stayed
   here rather than moving to a components section because this is where it was
   designed and where the reasoning below was worked out; the two callers are
   the whole story. If a third page takes it, move the block.

   A <ul> because it is four peer claims and a screen reader should be told how
   many. The icons are aria-hidden - each one restates its own label, so
   announcing them would double every item.

   NO CARD, NO FILL, NO SHADOW. It sits on the same --paper as the hero above
   it and is separated by rules alone. It is a caption strip, and giving it a
   surface would make it compete with the three pillar/step grids further down
   the page. */
/* `ul.trust-bar` AND NOT `.trust-bar`, for the margin below and for no other
   reason. base.css carries `ul[role="list"], ol[role="list"] { margin: 0 }`,
   which is (0,1,1) because of the attribute selector, and a bare `.trust-bar`
   is (0,1,0) - it loses, and the strip renders flush against the hero's
   buttons. Measured at 0px on the first build; this is not a theoretical
   specificity note.

   The type selector brings this to (0,1,1) as well, and styles.css loads after
   base.css, so source order settles the tie. That is the same mechanism the
   whole file relies on instead of !important - see the cascade note in
   design-system.md. Every other role="list" grid on the site sets margin: 0,
   which is why this is the first component to trip over it. */
ul.trust-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-md) var(--space-lg);
  list-style: none;
  /* Clearance from BOTH hero columns, not just the buttons, which is why it is
     bought here rather than under .hero-actions. */
  margin: var(--space-2xl) 0 0;
  padding-block: var(--space-lg);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.trust-bar__item {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  /* --ink-soft as specified in the trust bar's sign-off ("in --ink-soft"),
     which measures 8.05:1 on --paper.
     --ink-mute would be the usual choice for a caption but it is 3.15:1, and
     these are content claims (accreditation, turnaround), not labels. */
  color: var(--ink-soft);
  font-size: 0.9375rem;
  font-weight: 500;
}

.trust-bar__item svg {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--mec-blue);
}


/* ==========================================================================
   Legal pages  (/privacy, and the accessibility statement when it is written)
   --------------------------------------------------------------------------
   A DELIBERATELY PLAIN PAGE. Every other page on this site is marketing: it
   leads with a coloured word in the headline, alternates tinted bands, and
   closes on a navy call to action. None of that applies here. Someone reading
   a privacy policy is checking what happens to their health information, and
   the design's only job is to keep long-form legal prose readable and
   navigable. If a future page needs a hero image or an accent colour, it is
   not a legal page and should not use these classes.
   ========================================================================== */

/* 68ch. .content-column next door is 70ch and is used by editorial pages; this
   is a touch tighter because the text here is denser, and because a legal
   document is read in full rather than skimmed. Left-aligned text is not a
   style choice: centred or justified prose forces the eye to hunt for the
   start of each line, and this page is ~4,500 words.

   `margin-inline: 0`, NOT auto. The column has to hang off the container's
   left edge because the page hero above it does: the eyebrow, the h1 and the
   "Last updated" line are all full-container-width. Centring the measure put
   the title at x=154 and the first paragraph at x=377 on a 1440px screen, a
   223px step that read as though the heading belonged to something else. The
   wide right gutter that this leaves is the correct trade - alignment with
   the title beats filling the viewport, and nothing is meant to sit there.
   (If a sticky sidebar contents list is ever added at >=1200px, that gutter
   is where it goes.) */
.legal-body {
  max-width: 68ch;
  margin-inline: 0;
}

/* The date under the h1. --ink-soft is 8.06:1 on --paper, so this is "muted"
   optically without dropping to --ink-mute (3.14:1), which is a label colour.
   The date is content - section 13 explicitly tells the reader to look at it
   to know which version they are reading - so it must not be styled like
   caption furniture. */
.legal-updated {
  margin-top: var(--space-sm);
  color: var(--ink-soft);
  font-size: 0.9375rem;
}

/* 96px clears the 73px sticky header (72px min-height + 1px border) plus air,
   and is the same value .info-block[id] and .team-member use for the same
   reason. Every h2 here is a jump target from the contents list, so without
   this each one lands underneath the header and the section appears to begin
   at its second paragraph.

   ON THE HEADING, NOT THE SECTION, because the browser scrolls to the element
   the fragment identifies - and the id is on the <h2>. Putting it on
   .legal-section (as .faq-section does, where the id IS on the section) would
   style an element the scroll never targets and do nothing at all. */
.legal-section__title { scroll-margin-top: 96px; }

/* The gap between sections. Keyed on a section STARTING, not on a section
   preceding it, and that distinction is the whole point of this selector.

   It was `.legal-section + .legal-section`, which silently failed exactly
   once: the contents panel sits between sections 1 and 2, so section 2's
   previous sibling is nav.legal-toc and the rule did not match it. Section 2
   got no top margin at all while the other twelve boundaries got 4rem, and
   the heading collided with the bottom edge of the tinted panel.

   Anything else dropped into .legal-body between sections would have
   reintroduced it. This form cannot: whatever precedes a section, the section
   opens with the same 4rem. */
.legal-body > * + .legal-section { margin-top: var(--space-2xl); }

.legal-section__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  letter-spacing: -0.01em;
  margin-bottom: var(--space-md);
}

/* h3 subheadings. Body face, not display: they are frequent (28 of them) and
   setting them in Instrument Sans would give the page a second bold voice
   competing with the h2s for the same attention. Weight and colour separate
   them from the paragraph beneath instead. */
.legal-section h3 {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  color: var(--ink);
  margin-top: var(--space-lg);
  margin-bottom: var(--space-2xs);
}

/* base.css zeroes margins on p, so flow spacing is set here. The adjacent
   selector means the first child never gets a top margin it would have to
   fight the heading's margin-bottom over. */
.legal-section > * + * { margin-top: var(--space-sm); }

/* ...but a heading's own margin-top above wins, so restore it. Adjacent
   sibling margins do not collapse in this direction inside a flow root, and
   without this an h3 following a paragraph sits at --space-sm rather than
   --space-lg, which flattens the section structure. */
.legal-section > * + h3 { margin-top: var(--space-lg); }

.legal-section ul {
  margin-top: var(--space-sm);
  padding-left: var(--space-md);
}

.legal-section li + li { margin-top: var(--space-2xs); }

/* <address> is the correct element for contact details and browsers italicise
   it by default. The site has no italic face (see tokens.css) so the browser
   would synthesise an oblique - which is exactly the slanted-text treatment
   that has been rejected three times here. Upright, and set apart by a rule
   and indent instead. */
.legal-contact {
  font-style: normal;
  margin-top: var(--space-md);
  padding-left: var(--space-md);
  border-left: 3px solid color-mix(in srgb, var(--mec-blue) 30%, transparent);
  line-height: 1.7;
}

/* --------------------------------------------------------------------------
   Contents list
   --------------------------------------------------------------------------
   INLINE, NOT A STICKY SIDEBAR. At a 68ch measure there is no room for a
   column beside the text at any width this site supports, and a sticky bar
   under the 72px header would cost a phone a third of its viewport on the one
   page people are most likely to read on a phone. The brief allowed either;
   this is the one that does not fight the measure.

   An <ol>, because the sections are numbered in the source document and a
   reader referring to "section 7" needs the numbering to be real. The numerals
   are in the link text rather than from a CSS counter so they are part of each
   link's accessible name - a list read out as "About this policy" pointing at
   a heading that says "1. About this policy" fails WCAG 2.5.3.
   -------------------------------------------------------------------------- */

.legal-toc {
  margin-top: var(--space-xl);
  padding: var(--space-lg);
  background-color: var(--mec-blue-soft);
  border-radius: var(--radius-lg);
}

.legal-toc__heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: var(--space-sm);
}

/* list-style: none with the numeral in the text, so the number a screen reader
   announces is the one in the link, not a second one generated by the marker.
   Two columns once there is room: fourteen items is a long single column and
   the titles are short. */
.legal-toc__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2xs);
}

@media (min-width: 40em) {
  .legal-toc__list {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-2xs) var(--space-md);
  }
}

/* Underlined in the resting state, not only on hover. These sit on a tinted
   panel where the blue-on-tint contrast does the work of signalling a link;
   underline removal here would leave colour as the sole indicator, which fails
   WCAG 1.4.1. */
.legal-toc__link {
  display: inline-block;
  padding-block: 2px;
  font-size: 0.9375rem;
  line-height: 1.4;
}
