/* =========================================================
   FONTS
   Self-hosted (not linked to Google's CDN at runtime) so the
   site makes no third-party request for fonts - one less
   privacy/GDPR consideration for a UK medical practice, and
   one less thing that can fail if an external host is down.

   Pairing: Public Sans for headings (used across US federal
   government/civic sites - confident, geometric-humanist, built
   for exactly this kind of institutional-but-approachable
   context) with Inter for body text - one of the most
   battle-tested UI/screen fonts around, strong legibility at
   small sizes with a genuinely solid bold weight and
   conventional numerals.

   Was Atkinson Hyperlegible (Braille Institute, designed to
   distinguish easily-confused characters like 1/l/I, 0/O) -
   switched away 2026-08-06 after the crossed-zero glyph and a
   weak-looking bold weight were flagged as a real aesthetic
   problem in practice. If the low-vision-specific character
   distinction ever becomes a priority again over general screen
   legibility, that's the font to revisit.

   Inter ships as a single variable-weight file rather than one
   file per weight (both @font-face blocks below point at the
   same file) - fewer bytes for users than static per-weight
   files would be, and how Google itself serves it.
   ========================================================= */
@font-face {
  font-family: 'Public Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/public-sans-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Public Sans';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/public-sans-800.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/inter-variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/inter-variable.woff2') format('woff2');
}

/* =========================================================
   DESIGN TOKENS
   Change these to rebrand the whole site. Colour pairs below
   have been chosen to meet WCAG 2.2 AA contrast (4.5:1 body
   text, 3:1 large text/UI). If you change a colour, re-check
   contrast at https://webaim.org/resources/contrastchecker/
   ========================================================= */
:root {
  /* Colour
     Primary is NHS-familiar blue (6.7:1 on white, comfortably AA).
     Surface vs. bg is a deliberate split: --color-bg is a cool off-white
     the whole page sits on, --color-surface is true white for cards,
     the header and the mobile nav drawer, so those elements lift off
     the page instead of blending into it. */
  --color-primary: #005eb8;       /* headings, links, buttons - 6.7:1 on white */
  --color-primary-dark: #00396b;  /* hover/active state, ~9:1 on white */
  --color-accent: #1b6e43;        /* secondary actions, success states */
  --color-text: #1a1a1a;          /* body text on white: ~16:1 */
  --color-text-muted: #4d4d4d;    /* secondary text: ~8:1, still AA-safe */
  --color-bg: #f5f8fa;            /* page background */
  --color-surface: #ffffff;       /* card / header / drawer background */
  --color-bg-alt: #e9f1f7;        /* tinted section backgrounds, hover states */
  --color-border: #d5dde1;
  /* Form control borders need their own, darker token. --color-border
     measures only 1.38:1 on white / 1.29:1 on smoke / 1.21:1 on
     blue-smoke - fine for a decorative divider, but well short of the
     3:1 WCAG 2.2 SC 1.4.11 (Non-text Contrast) minimum that applies to
     the visible boundary of an input. This one measures 4.42 / 4.15 /
     3.87:1 against those same three surfaces. */
  --color-input-border: #6b7a85;
  --color-error: #a6141f;         /* form validation, urgent info - 6.9:1 on white */
  --color-warning-bg: #fff4e5;
  --color-warning-border: #f0c37e;
  --color-focus: #ffc700;         /* footer/nav hover accent only - NOT the focus ring, see below */
  /* Focus ring: measured at 1.56:1 against white and 1.59:1 against the
     lightest shortcut tile - both badly fail the 3:1 WCAG 2.2 SC 1.4.11
     (Non-text Contrast) minimum, despite the old comment here claiming
     it worked on both light and dark. No single flat colour clears 3:1
     against this site's full range of surfaces (near-white through to
     dark navy), so the ring is now two concentric colours instead - see
     the focus-visible rule below. Kept as separate tokens so each ring
     colour can be reasoned about on its own. */
  --focus-ring-inner: #ffffff;    /* contrasts against dark/coloured surfaces */
  --focus-ring-outer: #1a1a1a;    /* contrasts against white/light surfaces */
  --color-header-bg: var(--color-surface);
  --color-footer-bg: var(--color-primary-dark);
  --color-footer-text: #ffffff;
  --color-footer-legal-bg: #00253f; /* deliberately darker than the footer above it, for the copyright/legal strip */
  /* Shortcut Links tile variants: one navy, three steps of lightness.
     Cycled by position, purely as visual rhythm - the colour carries no
     meaning (see shortcut-links.php).

     Two earlier palettes were rejected and both failures are worth
     keeping in mind.

     Multi-hue (#0a8190 teal / #228463 green) measured 4.61:1 and 4.62:1
     with white text - AA by a hair - and **1.00:1 against each other**.
     Identical relative luminance, so the "alternation" existed only in
     hue: in greyscale, or with reduced colour discrimination, two of the
     three tiles were the same colour. Contrast had only ever been
     measured tile-vs-text, never tile-vs-tile.

     Light fills with near-black text scored far higher on paper (9-14.6:1)
     and were rejected by the sight-impaired reviewer as *worse*. Their
     rule: white text on a dark background, or black text on a white one -
     never text on a mid-tone colour, whatever the ratio says. The proof
     it is not about the ratio: the element they called ideal is the
     site's own primary button at 6.38:1, lower than every tile they
     rejected. Luminance ratio is a floor for compliance, not a measure
     of comfort - it says nothing about chromatic contrast.

     So: the same navy as every other button on the site, in three
     lightness steps. The lightest IS --color-primary, so a tile and a
     button are literally the same colour; the darkest is
     --color-primary-dark, already the button hover state.

       -1 #005eb8   6.38:1 white  (= --color-primary, the reviewer's benchmark)
       -2 #004b93   8.65:1 white
       -3 #00396b  11.68:1 white  (= --color-primary-dark)
       ladder: 1.35-1.36:1 between neighbours, 1.83:1 end to end

     Every tile now matches or beats the button they approved, the
     alternation survives greyscale, and nothing on the page introduces a
     colour that isn't already in the palette. Text is white and bold
     (700, matching .btn) - "avoid fine text" was part of the same
     feedback. Keep the three as a set. */
  --color-shortcut-1: #005eb8;
  --color-shortcut-2: #004b93;
  --color-shortcut-3: #00396b;
  --color-shortcut-text: #ffffff;

  /* Type */
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-heading: 'Public Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-size-base: 1.0625rem;   /* 17px - slightly above the common 16px default for readability */
  --line-height-base: 1.6;

  /* Layout */
  --content-max-width: 1140px;
  --content-narrow: 760px;       /* body copy reads best under ~760px */
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --radius: 8px;
  --radius-lg: 14px;
  --shadow: 0 1px 2px rgba(16, 35, 50, 0.06), 0 8px 24px rgba(16, 35, 50, 0.08);
}

/* =========================================================
   RESET / BASE
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: 100%;
  -webkit-text-size-adjust: 100%; /* don't block iOS zoom */
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--color-text);
  background: var(--color-bg);
}

img, svg, video { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  color: var(--color-primary-dark);
  line-height: 1.25;
  margin: 0 0 var(--space-2);
}
h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); font-weight: 800; }
h2 { font-size: clamp(1.4rem, 3vw, 1.9rem); }
h3 { font-size: 1.25rem; }

p { margin: 0 0 var(--space-2); }

a {
  color: var(--color-primary);
  text-decoration: underline; /* keep underlines - colour alone isn't enough to signal "link" */
  text-decoration-thickness: 1px;
}
/* Underline is dropped on hover so the state change is signalled by more
   than colour shift alone. The link stays identifiable while hovered (the
   cursor is on it, and it keeps the link colour), and keyboard users never
   depend on this - they get the focus ring below. Component links (nav,
   cards, buttons, shortcut tiles, footer) all set their own hover
   text-decoration at higher specificity, so this only affects inline links
   in body copy, which is where the permanent underline matters. */
a:hover { color: var(--color-primary-dark); text-decoration: none; }

ul, ol { padding-left: 1.25rem; }

/* =========================================================
   ACCESSIBILITY - FOCUS STATES
   Never remove :focus without replacing it. Two concentric rings
   (light + dark) rather than one flat colour, because this site's
   surfaces range from near-white to dark navy to mid-tone teal/green -
   no single colour clears the WCAG 2.2 SC 1.4.11 3:1 minimum against
   all of them, but a light ring next to the element plus a dark ring
   outside it guarantees at least one boundary always does, whatever
   the element sits on. box-shadow, not outline, so it follows
   border-radius on rounded elements (buttons, cards, tiles) instead of
   drawing a hard rectangle over the corners.
   ========================================================= */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring-inner), 0 0 0 5px var(--focus-ring-outer);
  border-radius: 2px;
}

/* Fallback for browsers without :focus-visible support */
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--focus-ring-inner), 0 0 0 5px var(--focus-ring-outer);
}

/* =========================================================
   SKIP LINK
   Hidden until keyboard-focused, then jumps straight to <main>.
   ========================================================= */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: var(--space-2) var(--space-3);
  background: var(--color-primary-dark);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus {
  left: var(--space-2);
  top: var(--space-2);
}

/* =========================================================
   SCREEN-READER-ONLY TEXT
   Use this class for context that sighted users don't need
   visually but screen reader users do (e.g. "(opens in new tab)").
   ========================================================= */
.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}

/* =========================================================
   LAYOUT
   ========================================================= */
.container {
  width: 100%;
  max-width: var(--content-max-width);
  margin-inline: auto;
  padding-inline: var(--space-3);
}

.content-narrow {
  max-width: var(--content-narrow);
  margin-inline: auto;
}

main { display: block; } /* IE/old Edge fallback */

/* =========================================================
   PAGE BUILDER SECTION BACKGROUNDS
   Optional per-section background (the "Background" field on most
   Page Builder layouts - see inc/block-helpers.php), used to visually
   break a long page into chunks. ".pb-section" is added to the outer,
   full-width wrapper of each block (not the inner .container, which
   stays width-constrained) so the colour spans edge-to-edge behind the
   normal-width content, the same way .site-header/.site-footer do.
   Only three options, matching tones used elsewhere on the site rather
   than one-off colours the client would have to keep track of.
   ========================================================= */
/* Vertical rhythm lives here, not as an inline style on each block
   template. It had drifted: text blocks used --space-3 while every other
   block used --space-4, so a text section sitting after a CTA banner
   looked visibly tighter than its neighbours. .hero and .block-cta set
   their own larger padding later in this file and still win. */
.pb-section { padding-block: var(--space-4); }
.pb-section.has-bg-white { background: var(--color-surface); }
.pb-section.has-bg-smoke { background: var(--color-bg); }
.pb-section.has-bg-blue-smoke { background: var(--color-bg-alt); }

/* =========================================================
   SITE HEADER
   Sticky, so the nav and "call us" action stay reachable while
   reading a long page - the same reason long pages use
   collapsible content instead of one long scroll.
   ========================================================= */
.site-header {
  background: var(--color-header-bg);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  z-index: 100;
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-block: var(--space-2);
}
.site-branding { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.site-branding a { text-decoration: none; }
.site-title {
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--color-primary-dark);
  margin: 0;
}
/* Sized for the practice's actual logo (a wide, lower-resolution raster
   image) - taller than a typical icon-style logo so the text inside it
   stays legible, capped on width so it can't dominate a narrow header. */
.site-logo img { max-height: 76px; max-width: 220px; width: auto; height: auto; }
@media (max-width: 480px) {
  .site-logo img { max-height: 56px; }
}

.header-actions { display: flex; align-items: center; gap: var(--space-2); }

/* =========================================================
   PRIMARY NAVIGATION
   Built to work with the accessible nav walker in
   inc/class-nav-walker.php: aria-expanded on submenu toggles,
   full keyboard operability, no hover-only interactions.

   Desktop: a plain horizontal menu inline in the header.
   Mobile (below the breakpoint further down): the same markup
   becomes an off-canvas drawer - see .nav-drawer.
   ========================================================= */
.main-navigation { position: relative; }

.nav-toggle {
  display: none; /* shown only under the mobile breakpoint below */
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.9rem;
  background: transparent;
  color: var(--color-primary-dark);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  flex: none;
}
.nav-toggle:hover { background: var(--color-bg-alt); }

.nav-toggle__icon { position: relative; width: 1.25rem; height: 0.9rem; display: block; }
.nav-toggle__bar {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle__bar:nth-child(1) { top: 0; }
.nav-toggle__bar:nth-child(2) { top: 0.45rem; }
.nav-toggle__bar:nth-child(3) { top: 0.9rem; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) { transform: translateY(0.45rem) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { transform: translateY(-0.45rem) rotate(-45deg); }

.nav-drawer__head,
.nav-drawer__close { display: none; } /* desktop: drawer chrome isn't shown, menu sits inline */

.primary-menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
.primary-menu li { position: relative; }
.primary-menu a {
  display: block;
  padding: var(--space-1) var(--space-2);
  text-decoration: none;
  color: var(--color-text);
  border-radius: var(--radius);
}
.primary-menu a:hover,
.primary-menu li.current-menu-item > a {
  background: var(--color-bg-alt);
  color: var(--color-primary-dark);
}
/* Underline is a hover-only cue, even for the current-page item - the
   background highlight above already marks "you are here" persistently,
   so it doesn't also need a permanent underline. */
.primary-menu a:hover {
  text-decoration: underline;
}

/* Submenus: click/Enter-to-open via JS, never hover-only (fails WCAG 2.1.1 / 2.5.7-adjacent issues on touch) */
.sub-menu {
  list-style: none;
  margin: 0;
  padding: var(--space-1);
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 220px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: none;
  z-index: 50;
}
.menu-item-has-children[data-submenu-open="true"] > .sub-menu { display: block; }
.sub-menu a { color: var(--color-text); }

.menu-item-has-children > a .submenu-indicator { margin-left: 0.25rem; font-size: 0.7em; }

/* =========================================================
   OFF-CANVAS MOBILE NAV
   Below the breakpoint, .nav-drawer becomes a fixed slide-in
   panel and .nav-backdrop dims the page behind it. Both are
   toggled purely by navigation.js adding/removing "is-open"
   and the "hidden" attribute - no state lives in CSS beyond
   how those two states look.
   ========================================================= */
@media (max-width: 780px) {
  .nav-toggle { display: inline-flex; }

  .nav-drawer {
    position: fixed;
    top: 0;
    right: 0;
    height: 100dvh;
    width: min(85vw, 22rem);
    background: var(--color-surface);
    box-shadow: var(--shadow);
    transform: translateX(100%);
    transition: transform 0.25s ease;
    z-index: 1000;
    overflow-y: auto;
    padding-bottom: var(--space-3);
  }
  .nav-drawer.is-open { transform: translateX(0); }

  .nav-drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-1);
  }
  .nav-drawer__title { font-weight: 700; color: var(--color-primary-dark); }
  .nav-drawer__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    background: transparent;
    border: none;
    border-radius: var(--radius);
    color: var(--color-text);
    cursor: pointer;
  }
  .nav-drawer__close:hover { background: var(--color-bg-alt); }
  .nav-drawer__close svg { width: 1.1rem; height: 1.1rem; }

  .primary-menu {
    flex-direction: column;
    gap: 0;
    padding: 0 var(--space-2);
  }
  .primary-menu a { padding: var(--space-2); }
  .sub-menu { position: static; box-shadow: none; border: none; padding-left: var(--space-2); }

  .nav-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(10, 20, 30, 0.45);
    z-index: 999;
  }

}

/* Body scroll is locked (via this class, toggled by navigation.js)
   while the drawer is open, so the page behind it can't scroll. */
.has-nav-drawer-open { overflow: hidden; }

/* =========================================================
   HERO (ACF-driven — see template-parts/hero.php)
   ========================================================= */
.hero {
  background: var(--color-bg-alt);
  padding-block: var(--space-5);
}
/* Was max-width: var(--content-narrow) (760px) - but .hero__inner keeps
   .container's margin-inline:auto, so a narrower box gets CENTERED
   within the full-width .hero section instead of left-aligned flush
   with every other section's (wider, also centered) .container. Result:
   hero content visibly started further right than the page's other
   sections. Matching content-max-width here (same as the with-image
   variant below) fixes the alignment; hero text is short enough
   (heading + one subheading line) that losing the narrower reading
   width isn't a readability concern the way it would be for a long
   Text Block. */
.hero__inner { max-width: var(--content-max-width); }
.hero__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-accent);
}
.hero__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-3); }

/* Hero with an optional image: same width as the plain text-only hero,
   laid out as photo + text instead of just text. The image
   always comes first in the markup - on mobile that means it stacks
   above the heading, same convention as the Image + Text block, so
   editors who've used one already understand the other. The image
   itself is never used as a background behind the text: contrast
   against an editor-uploaded photo can't be guaranteed, so text always
   sits on the hero's own solid background and the photo stays in its
   own box. */
.hero__inner--with-image {
  max-width: var(--content-max-width);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  align-items: center;
}
@media (min-width: 720px) {
  .hero__inner--with-image { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
  .hero__inner--image-right .hero__media { order: 2; }
}
.hero__media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

/* =========================================================
   BUTTONS
   Real <button>/<a> elements only — never a <div onclick>,
   which screen readers and keyboard users can't operate.
   ========================================================= */
.btn {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius);
  font-weight: 700;
  text-decoration: none;
  border: 2px solid transparent;
  cursor: pointer;
  font-size: 1rem;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.btn-primary { background: var(--color-primary); color: #fff; }
/* Explicit color here, not just inherited from .btn-primary above: the
   global `a:hover { color: var(--color-primary-dark) }` rule has higher
   specificity (element+pseudo-class beats a single class) and was
   winning on hover, turning the text the same colour as this rule's own
   background - text and background became identical, so the label
   visually disappeared. */
.btn-primary:hover { background: var(--color-primary-dark); color: #fff; text-decoration: underline; }
/* The outline is the only thing that makes this a button rather than a
   line of blue text, so it has to clear the 3:1 SC 1.4.11 minimum on
   every surface a hero can sit on. --color-border does not, anywhere:
   1.38:1 on white, 1.29:1 on smoke, 1.21:1 on blue-smoke. Reuses
   --color-input-border, added for form fields and sized for exactly this
   job - 4.42 / 4.15 / 3.87:1 on those same three. Slightly darker grey,
   same shape. */
.btn-secondary { background: transparent; color: var(--color-primary-dark); border-color: var(--color-input-border); }
.btn-secondary:hover { background: var(--color-bg-alt); border-color: var(--color-primary-dark); text-decoration: underline; }

/* =========================================================
   CARDS / CONTENT BLOCKS
   ========================================================= */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-block: var(--space-4);
}
@media (min-width: 560px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 860px) {
  /* Fixed at 3, not auto-fit - same reasoning as .shortcut-links: cards
     should read as 3 deliberately-sized columns on a wide screen, not
     however many 240px slots happen to fit (which was cramming 4+ in
     per row and forcing button text like "Order a prescription" onto
     two lines). */
  .card-grid { grid-template-columns: repeat(3, 1fr); }
  /* Per-block override. Three is right for most grids, but four cards in
     a three-column grid leaves an orphan on its own row, and two long
     cards read better side by side than squeezed into thirds. Declared
     after the default so they win on source order. .card-grid is shared
     with the staff grid, which carries no modifier and keeps the
     default. */
  .card-grid--cols-2 { grid-template-columns: repeat(2, 1fr); }
  .card-grid--cols-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1100px) {
  /* Four only once there is genuinely room; below this it stays at two
     rather than producing four cramped columns. */
  .card-grid--cols-4 { grid-template-columns: repeat(4, 1fr); }
}
.card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  background: var(--color-surface);
  box-shadow: var(--shadow);
}
.card img { border-radius: var(--radius); margin-bottom: var(--space-2); }
.card h3 { margin-top: 0; }
/* Card title/"Find out more" links are nav-like (a distinct, positioned
   element), not links inline in a sentence - same reasoning as the
   footer links above. */
.card a { text-decoration: none; }
.card a:hover { text-decoration: underline; }
/* Full-width rather than inline-block sized to its own text: an
   inline-block button's width comes from its own label, so a longer
   one ("Order a prescription") could wrap onto two lines in the same
   card width a shorter label ("Find out more") fit on one - even with
   the wider 3-per-row cards above. Full width removes that inconsistency. */
.card .btn { display: block; width: 100%; text-align: center; }

/* =========================================================
   BLOCK: SHORTCUT LINKS
   Solid-colour tiles (Book an Appointment, Order a Repeat Prescription,
   etc.) - see template-parts/blocks/shortcut-links.php. Each tile is an
   <a> when it has a link, or a plain <div> when it doesn't (never an
   <a> without href - that isn't a real link to a browser or a screen
   reader). Hover/focus feedback is scoped to the linked ones only,
   since the div variant isn't interactive.
   ========================================================= */
.shortcut-links {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2);
  margin-block: var(--space-3);
}
@media (min-width: 480px) {
  .shortcut-links { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  /* Fixed count, not auto-fit - a wide container should still read as
     4 deliberate columns, not however many 170px slots happen to fit. */
  .shortcut-links { grid-template-columns: repeat(4, 1fr); }
}
.shortcut-link {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 5.5rem;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  color: var(--color-shortcut-text);
  font-weight: 700; /* matches .btn - the reviewer asked for bold and called the site's own button ideal */
  font-size: 1.3rem;
  line-height: 1.3;
  text-decoration: none;
}
@media (min-width: 720px) {
  .shortcut-link { font-size: 1.6em; }
}
a.shortcut-link { transition: filter 0.15s ease; }
a.shortcut-link:hover,
a.shortcut-link:focus-visible {
  /* Explicit color: without it the global a:hover rule wins on
     specificity and repaints the label navy. Underline added per
     request, so hover/focus has a cue that doesn't rely on colour alone.
     brightness(0.92) darkens the navy, so text contrast goes up on
     hover, never down (worst case 6.38:1 -> 7.23:1). */
  filter: brightness(0.92);
  color: var(--color-shortcut-text);
  text-decoration: underline;
}

/* No border: the tiles are solid blocks, the way the button the reviewer
   approved is. Ratios and reasoning live with the tokens in :root - read
   that comment before changing any of the three, because they are a set:
   each one's distance from the other two is doing as much work as its
   own contrast with the text. */
.shortcut-link--a { background: var(--color-shortcut-1); }
.shortcut-link--b { background: var(--color-shortcut-2); }
.shortcut-link--c { background: var(--color-shortcut-3); }

/* The map opt-in sits where the map will appear, so the layout does not
   jump when it loads. The note is deliberately not muted grey - it is the
   only place a visitor is told what pressing the button does. */
.catchment__map-optin { margin-block: var(--space-3); }
.catchment__map-note { margin-top: var(--space-2); font-size: 0.9375rem; }

/* =========================================================
   BLOCK: CQC RATING
   Rendered from data we fetch server-side, so this is our markup and our
   type - no third-party stylesheet, and nothing to break when the CQC
   change their widget. The rating is stated in words; the panel is one
   neutral style whatever the rating says, so colour is never carrying
   the meaning on its own.
   ========================================================= */
.cqc__intro { margin-bottom: var(--space-3); }
.cqc__card {
  border: 2px solid var(--color-input-border); /* 4.42:1 on white - see the token note */
  border-radius: var(--radius);
  padding: var(--space-3);
  background: var(--color-surface);
  text-align: center;
}
.cqc__name { font-weight: 700; margin: 0; }
.cqc__label { margin: var(--space-1) 0 0; color: var(--color-text-muted); }
.cqc__rating {
  font-family: var(--font-heading);
  font-size: 2rem;
  font-weight: 800;
  color: var(--color-primary-dark); /* 11.68:1 on white */
  margin: var(--space-1) 0 0;
}
.cqc__date { margin: var(--space-1) 0 0; color: var(--color-text-muted); }
.cqc__fallback { margin-top: var(--space-3); }

/* =========================================================
   FORMS
   Labels are always programmatically associated (see
   template-parts and comment form) — never placeholder-only.
   ========================================================= */
label { display: block; font-weight: 600; margin-bottom: 0.25rem; }
input[type="text"], input[type="email"], input[type="tel"], input[type="search"], textarea, select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 1rem;
  font-family: inherit;
  margin-bottom: var(--space-2);
}
.form-error { color: var(--color-error); font-weight: 600; font-size: 0.9rem; margin-top: -0.5rem; margin-bottom: var(--space-2); }
.required-indicator { color: var(--color-error); }

/* =========================================================
   BLOCK: IMAGE + TEXT
   ========================================================= */
.image-text {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  align-items: center;
}
@media (min-width: 720px) {
  .image-text { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
  .image-text--right .image-text__media { order: 2; }
}
.image-text__media img { border-radius: var(--radius); }

/* =========================================================
   BLOCK: TWO COLUMN TEXT
   Two plain text columns, equal width, stacked below the same
   breakpoint every other two-up block on this theme uses. Unlike
   Image + Text there's no order/position logic - both columns are
   the same kind of content, so there's no "which side" decision.
   ========================================================= */
.two-column-text {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 720px) {
  .two-column-text { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
}

/* =========================================================
   NINJA FORMS
   The plugin ships structural CSS only, so forms otherwise
   render as unstyled browser defaults next to the rest of
   the site. Everything here is scoped to .nf-form-cont so it
   can't leak into the boundary checker's own input.
   ========================================================= */

/* The honeypot MUST be hidden - Ninja Forms does not hide it itself.
   Left visible, real people fill it in ("Leave this field blank" is not
   universally read) and their submission is silently rejected, which is
   worse than having no spam protection at all. display:none also takes
   it out of the tab order and the accessibility tree, which position
   off-screen would not. */
.nf-form-cont .nf-field-container.spam-container { display: none; }

.nf-form-cont .nf-field-container { margin-bottom: var(--space-3); }
.nf-form-cont .nf-field-label label {
  display: block;
  font-weight: 600;
  margin-bottom: var(--space-1);
  color: var(--color-text);
}
/* NF marks required fields with a red asterisk. #a6141f measures 6.9:1
   on white - the same token the theme uses for validation elsewhere. */
.nf-form-cont .ninja-forms-req-symbol { color: var(--color-error); }

.nf-form-cont input[type="text"],
.nf-form-cont input[type="email"],
.nf-form-cont input[type="tel"],
.nf-form-cont input[type="number"],
.nf-form-cont input[type="date"],
.nf-form-cont textarea,
.nf-form-cont select {
  width: 100%;
  padding: 0.7rem 0.85rem;
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-input-border); /* 3:1+ on every section background, see token */
  border-radius: var(--radius);
}
.nf-form-cont textarea { min-height: 9rem; resize: vertical; }

/* Checkbox and its label sit on one line, label clickable. */
.nf-form-cont .listcheckbox-container .nf-field-element ul { list-style: none; margin: 0; padding: 0; }
.nf-form-cont .listcheckbox-container li { display: flex; align-items: flex-start; gap: var(--space-1); }
.nf-form-cont .listcheckbox-container label { font-weight: 400; }

/* Submit reuses the theme's primary button rather than approximating it. */
.nf-form-cont input[type="submit"],
.nf-form-cont button[type="submit"] {
  display: inline-block;
  width: auto;
  padding: 0.75rem 1.5rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  background: var(--color-primary);
  color: #fff;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.nf-form-cont input[type="submit"]:hover,
.nf-form-cont button[type="submit"]:hover { background: var(--color-primary-dark); }
.nf-form-cont input[type="submit"][disabled] { opacity: .7; cursor: progress; }

/* Validation. NF puts errors after the field; give them the same weight
   as the rest of the theme's error styling rather than plugin defaults. */
.nf-form-cont .nf-error-msg,
.nf-form-cont .nf-error .nf-error-msg {
  color: var(--color-error); /* 6.9:1 on white */
  font-size: 0.9rem;
  font-weight: 600;
  margin-top: var(--space-1);
}
.nf-form-cont .nf-error input,
.nf-form-cont .nf-error textarea,
.nf-form-cont .nf-error select { border-color: var(--color-error); border-width: 2px; }

.nf-form-cont .nf-form-title { display: none; } /* page already supplies the heading */
.nf-form-cont .nf-response-msg { font-size: 1.05rem; }

/* =========================================================
   BLOCK: SITEMAP
   Two columns of links on wider screens; stays a <ul> so it
   is announced as a list of N items.
   ========================================================= */
.sitemap__intro { margin-bottom: var(--space-3); }
.sitemap__intro > :last-child { margin-bottom: 0; }
.sitemap__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .1rem;
}
@media (min-width: 560px) { .sitemap__list { grid-template-columns: repeat(2, 1fr); column-gap: var(--space-3); } }
.sitemap__list li { border-bottom: 1px solid var(--color-border); }
.sitemap__list a { display: block; padding: .6rem .2rem; text-decoration: none; font-weight: 600; }
.sitemap__list a:hover { text-decoration: underline; }
.sitemap__count {
  margin-top: var(--space-3);
  font-size: .85rem;
  color: var(--color-text-muted); /* 8.45:1 on white */
}

/* =========================================================
   BLOCK: DOCUMENT LIST
   A grid of downloads. Stays a <ul> so it is announced as a
   list of N items; the grid is layout only. Collapses to one
   column on narrow screens regardless of the chosen count.
   ========================================================= */
.doc-list__intro { margin-bottom: var(--space-3); }
.doc-list__intro > :last-child { margin-bottom: 0; }

.doc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-1);
}
@media (min-width: 560px) {
  .doc-list--cols-2 { grid-template-columns: repeat(2, 1fr); }
  .doc-list--cols-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 860px) {
  .doc-list--cols-3 { grid-template-columns: repeat(3, 1fr); }
}

/* Grid, not a flex row: the icon takes its own column and the label sits
   above its meta, so a long label ("Permission from patient complaint
   form") wraps naturally instead of being crushed into a sliver by the
   file-size text sitting beside it. */
.doc-list__link {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  column-gap: var(--space-1);
  height: 100%;
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--color-primary); /* 6.7:1 on white */
}
.doc-list__link:hover {
  border-color: var(--color-primary);
  background: var(--color-bg-alt);
  /* Underline the label specifically, so hover isn't colour-only. */
}
.doc-list__link:hover .doc-list__label { text-decoration: underline; }

.doc-list__icon {
  grid-column: 1;
  grid-row: 1 / span 2;
  width: 1.4rem;
  height: 1.4rem;
  margin-top: 0.1em; /* optically align with the first line of the label */
  color: var(--color-primary);
}
.doc-list__label {
  grid-column: 2;
  font-weight: 600;
}
.doc-list__meta {
  grid-column: 2;
  margin-top: 0.15em;
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--color-text-muted); /* 8.45:1 on white */
}

/* =========================================================
   BLOCK: PRACTICE BOUNDARY CHECKER
   The text result is the authoritative answer, so every state
   states its outcome in words - colour and weight are layered
   on top and are never the only signal (WCAG 2.2 SC 1.4.1).
   Ratios below measured against all three section backgrounds
   this block can sit on: white #ffffff, smoke #f5f8fa and
   blue-smoke #e9f1f7.
   ========================================================= */
.catchment__intro { margin-bottom: var(--space-3); }
.catchment__intro > :last-child { margin-bottom: 0; }

.catchment__label {
  display: block;
  font-weight: 600;
  margin-bottom: var(--space-1);
}
.catchment__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
.catchment__input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.7rem 0.85rem;
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-input-border); /* 4.42 / 4.15 / 3.87:1 - clears the 3:1 SC 1.4.11 minimum */
  border-radius: var(--radius);
  text-transform: uppercase; /* postcodes read conventionally; value is normalised in JS anyway */
}
.catchment__button { flex: 0 0 auto; }
.catchment__button[disabled] { opacity: 0.7; cursor: progress; }

.catchment__result {
  margin: var(--space-2) 0 0;
  font-weight: 600;
  min-height: 1.6em; /* reserve the line so announcing a result doesn't shift layout */
}
.catchment__result:empty { margin-top: 0; }
/* 6.25 / 5.86 / 5.48:1 */
.catchment__result.is-yes { color: var(--color-accent); }
/* 7.66 / 7.18 / 6.71:1 - also used for lookup failures */
.catchment__result.is-no,
.catchment__result.is-error { color: var(--color-error); }
/* 8.45 / 7.93 / 7.40:1 */
.catchment__result.is-pending { color: var(--color-text-muted); font-weight: 400; }

.catchment__map {
  margin-top: var(--space-3);
  width: 100%;
  height: 420px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
@media (max-width: 480px) {
  .catchment__map { height: 300px; }
}

.catchment__note {
  margin-top: var(--space-3);
  color: var(--color-text-muted); /* 8.45 / 7.93 / 7.40:1 */
  font-size: 0.95rem;
}

/* =========================================================
   BLOCK: STAFF GRID
   The cards themselves reuse .card-grid, so the only thing
   needing its own rule is the optional per-group intro
   paragraph that sits between the heading and the cards.
   ========================================================= */
.staff-grid__intro {
  max-width: var(--content-narrow); /* prose, so hold it to reading width */
  margin-block: 0 var(--space-3);
}
.staff-grid__intro > :last-child { margin-bottom: 0; }

/* =========================================================
   BLOCK: FAQ ACCORDION
   Built on native <details>/<summary> - see faq-accordion.php
   for why. Styling below just makes the native element look
   intentional; the interaction itself needs no JS or ARIA.
   ========================================================= */
.faq-list { display: flex; flex-direction: column; gap: var(--space-1); }
.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2);
  background: var(--color-surface);
}
.faq-item__question {
  cursor: pointer;
  font-weight: 600;
  color: var(--color-primary-dark);
  list-style: none; /* remove default disclosure triangle, we add our own */
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
}
.faq-item__question::-webkit-details-marker { display: none; }
/* The question text is wrapped in its own span purely so the hover
   underline lands on the words and not on the chevron beside them -
   underlining the whole <summary> would decorate the icon too, since the
   flex children each inherit the decoration. */
.faq-item__title { text-decoration-thickness: 1px; }
.faq-item__question:hover .faq-item__title { text-decoration: underline; }
.faq-item__chev {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
  color: var(--color-text-muted);
  transition: transform 0.18s ease;
}
.faq-item[open] .faq-item__chev { transform: rotate(180deg); }
.faq-item__answer { padding-top: var(--space-2); }

/* =========================================================
   BLOCK: CTA BANNER
   ========================================================= */
/* No outer margin. The banner is a full-bleed band with its own
   background, so a margin just exposes a strip of the page background
   between it and the section above or below - which reads as an
   accidental gap rather than deliberate spacing. Its own padding-block
   gives it all the breathing room it needs. */
.block-cta { padding-block: var(--space-5); }
.block-cta--dark {
  background: var(--color-primary-dark);
  color: #fff;
}
.block-cta--dark h2 { color: #fff; }
/* .btn-primary's normal hover (background -> --color-primary-dark) is invisible
   here since that's this banner's own background - the button would blend
   straight into it, leaving only the text floating. Invert to a white fill
   on hover instead, so there's still a visible, contrasting hover state. */
.block-cta--dark .btn-primary:hover {
  background: #fff;
  color: var(--color-primary-dark);
}
.block-cta--light {
  background: var(--color-bg-alt);
}
/* btn-primary's default hover (--color-primary-dark) stays perfectly
   legible on this pale banner - 10.23:1 against it, white label at
   11.68:1 - so unlike the dark banner above, no inverted hover is
   needed here. */

/* =========================================================
   SITE-WIDE ALERT BANNER
   ========================================================= */
.site-alert {
  background: var(--color-warning-bg);
  border-bottom: 1px solid var(--color-warning-border);
  padding-block: var(--space-1);
  font-weight: 600;
  text-align: center;
}

/* =========================================================
   SITE FOOTER
   ========================================================= */
.site-footer {
  background: var(--color-footer-bg);
  color: var(--color-footer-text);
  padding-top: var(--space-4);
  margin-top: var(--space-5);
}
/* Footer links (menu, phone/address, and the legal bar underneath it -
   all descendants of .site-footer) are nav-like, not links embedded in
   a sentence of running text, so they don't need a permanent underline
   to signal "this is clickable" - position/context already does that. */
.site-footer a { color: #fff; text-decoration: none; }
.site-footer a:hover { color: var(--color-focus); text-decoration: underline; }
/* Four deliberate columns - address, opening hours, and two menus -
   rather than auto-fit, which reflowed to however many 200px slots
   happened to fit and left the contact block stranded. Two columns at
   tablet, one on mobile. */
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 560px) {
  .footer-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  .footer-grid { grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
}

.footer-menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer-menu li { margin-bottom: var(--space-1); }
.footer-menu li:last-child { margin-bottom: 0; }
.site-footer h2, .site-footer h3 {
  color: #fff;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.site-footer h2.footer-brand {
  font-size: 1.15rem;
  text-transform: none;
  letter-spacing: normal;
}
.footer-menu { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }

/* Bottom "legal" strip - darker than the footer above it so it reads as
   a distinct closing band, not just more footer. Sits flush with the
   true bottom of the page (the footer's own bottom padding was removed
   above so this isn't inset from the edge by an extra gap). */
.site-footer__legal {
  background: var(--color-footer-legal-bg);
  margin-top: var(--space-4);
  padding-block: var(--space-2);
  text-align: center;
}
.site-footer__legal p { margin: 0; font-size: 0.85rem; }

/* =========================================================
   MOTION
   Respect reduced-motion preference (WCAG 2.3.3 / general
   good practice) — disable non-essential animation/transition.
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =========================================================
   PRINT
   Practice info pages often get printed — keep it usable.
   ========================================================= */
@media print {
  .site-header, .site-footer, .skip-link, .nav-toggle, .nav-drawer, .nav-backdrop { display: none; }
  a { color: #000; text-decoration: underline; }
}
