/* ==========================================================================
   site.css — component styles for Fill Good Aesthetics.

   Every selector is scoped under `.fga`. That is deliberate: the same markup
   and the same stylesheet have to survive being pasted into a third-party page
   builder, where an unscoped `h2 {}` would either be overridden or would leak.

   There are no colours, font sizes or spacing values in this file. Everything
   is a token from tokens.css. If a value is missing here it should be added
   there, not typed inline.
   ========================================================================== */

/* ------------------------------------------------------------------ reset */

.fga,
.fga *,
.fga *::before,
.fga *::after { box-sizing: border-box; }

.fga {
  background: var(--bg-base);
  color: var(--fg-ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  font-weight: var(--weight-body);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

/* Every reset below is wrapped in :where(), which contributes nothing to
   specificity. A bare `.fga ul { padding: 0 }` is (0,1,1) and outranks a
   component class like `.fga-trust__list` at (0,1,0) — which is how the trust
   strip lost its gutter and sat flush to the viewport edge, and how the primary
   button once rendered its label in the link colour. A reset that a component
   has to fight is a bug generator; this one cannot win an argument. */
:where(.fga) :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure, blockquote) { margin: 0; }
:where(.fga) :where(ul, ol) { padding: 0; }

:where(.fga) :where(h1, h2, h3) {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-display);
  text-wrap: balance;
}
:where(.fga) :where(h4, h5, h6) {
  font-family: var(--font-display);
  font-weight: var(--weight-display-med);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}
:where(.fga) :where(h1) { font-size: var(--step-4); }
:where(.fga) :where(h2) { font-size: var(--step-3); }
:where(.fga) :where(h3) { font-size: var(--step-2); }
:where(.fga) :where(h4) { font-size: var(--text-subheading); }

:where(.fga) :where(img, svg) { max-width: 100%; display: block; }

/* Anchors get no colour here.
   `.fga a` is specificity (0,1,1) and a component class like `.fga-btn` is
   (0,1,0), so a colour on this rule silently beats every component that styles
   its own anchors — which is how the primary button ended up rendering its label
   in the link colour on the accent fill. Colour is set by context below and by
   the components themselves, never on the bare element. */
/* A COLOUR FLOOR, at zero specificity. The rule used to set no colour at all,
   because the old `.fga a` form was (0,1,1) and silently beat every component
   that coloured its own anchors — that is how the primary button once rendered
   its label in the link colour. Wrapped in :where() it is (0,0,0) and loses to
   literally any component rule, so it cannot repeat that bug; what it does is
   stop a newly added list of links falling through to the browser's default
   blue, which has now happened three times (the rail's price-list link, the
   homepage treatment row, the location rail's popular list). A floor, not an
   override. */
:where(.fga) :where(a) {
  color: var(--text-accent);
  text-decoration: none;
  transition: color var(--transition), border-color var(--transition);
}

/* Links inside running text. The design system's underline is a painted gradient,
   not a border and never text-decoration: a gradient has a size, so it can grow
   from nothing on hover, and the rest state keeps a static hairline via box-shadow
   so the text never shifts by a pixel between states.

   The list is written once through :is(), and buttons and cards are excluded by
   :not() rather than by cancel rules further down the file. A cancel rule only
   works until someone adds a component nobody remembered to cancel; an exclusion
   means those components never match this selector in the first place. */
:is(.fga-prose, .fga-intro, .fga-longform, .fga-note, .fga-smallprint,
    .fga-map__body, .fga-checklist, .fga-faq__a, .fga-twocol, .fga-blocks,
    .fga-trust__list, .fga-practitioner__creds, .fga-booking__fallback,
    .fga-prices, .fga-compare__col, .fga-consent, .fga-ba__caption,
    .fga-review, .fga-journey, .fga-enquiry, .fga-pullquote__attrib, .fga-railcard, .fga-mega__footlinks) a:not(.fga-btn, .fga-textlink, .fga-card, .fga-card__more) {
  color: var(--text-accent);
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% var(--border-width-hairline);
  box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 var(--border-accent);
  transition: background-size var(--dur-base) var(--ease-reveal),
              color var(--dur-fast) var(--ease-reveal),
              box-shadow var(--dur-fast) var(--ease-reveal);
}
:is(.fga-prose, .fga-intro, .fga-longform, .fga-note, .fga-smallprint,
    .fga-map__body, .fga-checklist, .fga-faq__a, .fga-twocol, .fga-blocks,
    .fga-trust__list, .fga-practitioner__creds, .fga-booking__fallback,
    .fga-prices, .fga-compare__col, .fga-consent, .fga-ba__caption,
    .fga-review, .fga-journey, .fga-enquiry, .fga-pullquote__attrib, .fga-railcard, .fga-mega__footlinks) a:not(.fga-btn, .fga-textlink, .fga-card, .fga-card__more):is(:hover, :focus-visible) {
  color: var(--accent-hover);
  background-size: 100% var(--border-width-hairline);
  box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 transparent;
}

.fga :focus-visible {
  outline: var(--border-width-strong) solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
/* The teal focus ring is invisible on the deep-teal footer and inverse band.
   Butter on teal-800 is 8.3:1. */
.fga-footer :focus-visible,
.fga-band--inverse :focus-visible,
.fga-note--critical :focus-visible { outline-color: var(--fg-butter-300); }

.fga [hidden] { display: none !important; }

/* --------------------------------------------------------------- helpers */

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

.fga-skip {
  position: absolute; left: var(--space-s); top: var(--space-3xs);
  z-index: 100; transform: translateY(-200%);
  background: var(--accent); color: var(--text-on-accent);
  padding: var(--space-xs) var(--space-m); border: 0; border-radius: var(--radius-xs);
  font-size: var(--text-body-sm); letter-spacing: var(--tracking-wide);
}
.fga-skip:focus { transform: translateY(0); }

.fga-container { max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter); }
.fga-container--narrow { max-width: var(--container); }

.fga-eyebrow {
  font-family: var(--font-body);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
  color: var(--text-muted-adaptive);
}

.fga-measure { max-width: var(--measure); }
.fga-measure--intro { max-width: var(--measure-intro); }

.fga-rule { border: 0; border-top: var(--border-width-hairline) solid var(--rule); }

/* ----------------------------------------------------------------- rail */

/* The editorial two-column rail. Headings sit in a narrow left column and the
   prose runs beside them at its measure. This is what keeps a text-heavy page
   asymmetric and uses the full container without ever widening the line length
   past 62ch — the measure is the design, so it is never relaxed. */
.fga-rail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: var(--space-2xl);
  align-items: start;
}
.fga-rail__head { display: grid; gap: var(--space-xs); }
.fga-rail__head h2 { font-size: var(--step-3); }
.fga-rail__body { display: grid; gap: var(--space-m); min-width: 0; }
.fga-rail--wide { grid-template-columns: minmax(0, 1fr) minmax(0, 2.4fr); }
@media (max-width: 900px) {
  .fga-rail, .fga-rail--wide { grid-template-columns: 1fr; gap: var(--space-m); }
}

/* ---------------------------------------------------------------- prose */

.fga-prose > p + p { margin-top: var(--space-s); }
.fga-prose p { line-height: var(--leading-body); }
.fga-prose--lead p { font-size: var(--step-1); color: var(--text-secondary-adaptive); }

.fga-list { list-style: none; display: grid; gap: var(--space-xs); }
.fga-list > li { position: relative; padding-left: var(--space-m); }
.fga-list > li::before {
  content: ""; position: absolute; left: 0; top: 0.72em;
  width: var(--space-2xs); height: var(--border-width-hairline);
  background: var(--border-accent);
}

/* --------------------------------------------------------------- buttons */

.fga-btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-body); font-size: var(--text-body-sm);
  font-weight: var(--weight-medium); letter-spacing: var(--tracking-wide);
  padding: var(--space-s) calc(var(--space-m) + var(--space-3xs));
  border-radius: var(--radius-xs);
  border: var(--border-width-hairline) solid transparent;
  /* The hover state fills from the bottom rather than swapping the background
     colour. Same two colours, but the darker one arrives as a growing gradient,
     so the change has a direction instead of being a flat cut. --accent stays on
     background-color: it is the rest state and the fallback if the image fails. */
  background-color: var(--accent);
  background-image: linear-gradient(var(--accent-hover), var(--accent-hover));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0%;
  color: var(--text-on-accent);
  transition: background-size var(--dur-hover) var(--ease-reveal),
              color var(--dur-fast) var(--ease-reveal),
              border-color var(--dur-fast) var(--ease-reveal);
  cursor: pointer;
}
.fga-btn:is(:hover, :focus-visible) { background-size: 100% 100%; color: var(--text-on-accent); border-color: transparent; }
.fga-btn:active { transform: translateY(1px); }

.fga-btn--outline {
  background-color: transparent; color: var(--text-primary);
  background-image: linear-gradient(var(--accent-soft), var(--accent-soft));
  border-color: var(--border-interactive);
}
.fga-btn--outline:is(:hover, :focus-visible) { color: var(--text-accent); border-color: var(--text-accent); }

.fga-btn--inverse {
  background-color: var(--fg-ivory-000); color: var(--fg-teal-800);
  background-image: linear-gradient(var(--fg-cream-200), var(--fg-cream-200));
}
.fga-btn--inverse:is(:hover, :focus-visible) { color: var(--fg-teal-800); }

.fga-textlink {
  display: inline-block;
  font-family: var(--font-body); font-size: var(--text-body-sm);
  letter-spacing: var(--tracking-wide);
  color: var(--text-primary);
  border-bottom: var(--border-width-hairline) solid var(--border-interactive);
  padding-block: var(--space-3xs);
}
.fga-textlink:hover { color: var(--text-accent); border-color: var(--text-accent); }

.fga-actions { display: flex; flex-wrap: wrap; gap: var(--space-m); align-items: center; }

/* ---------------------------------------------------------------- header */

.fga-header {
  position: sticky; top: 0; z-index: 40;
  background: var(--glass-ivory);
  -webkit-backdrop-filter: var(--blur-glass);
  backdrop-filter: var(--blur-glass);
  border-bottom: var(--border-width-hairline) solid var(--rule);
  transition: background-color var(--dur-base) var(--ease-reveal),
              border-color var(--dur-base) var(--ease-reveal),
              transform var(--dur-base) var(--ease-reveal);
}

/* Over the hero the header has no ground and no rule, so the hero runs to the
   top of the viewport instead of starting under a bar.

   Two things about how this is expressed. It is keyed on .is-top being PRESENT
   rather than on .is-condensed being absent, so the transparent state is the
   exception a script has to ask for: without JavaScript, or before the first
   scroll handler runs, the header is solid and legible. Written the other way
   round it was transparent by default, and any page where the script did not run
   had an unreadable header over its hero.

   And the ground returns on the FIRST scroll, not at --header-trigger. The
   trigger is for the condense — the 12px of height — which is a different event.
   Tying the background to it meant hero text scrolled underneath a see-through
   bar for the whole first two-thirds of the screen, which is exactly what it
   looks like: the page overlapping its own header. */
.fga-header--over.is-top {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-bottom-color: transparent;
}
.fga-header--over.is-top .fga-header__inner { min-height: calc(var(--header-height) + var(--header-condense)); }

/* Below 900px the header gets out of the way going down and comes back coming
   up: on a phone the sticky bar is a meaningful share of the screen. */
@media (max-width: 900px) {
  .fga-header.is-away { transform: translateY(-100%); }
}

.fga-header__inner {
  max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter);
  min-height: var(--header-height);
  display: flex; align-items: center; gap: var(--space-m);
  transition: min-height var(--dur-base) var(--ease-reveal);
}
.fga-header__brand { display: flex; align-items: center; gap: var(--space-xs); flex: 0 0 auto; }
.fga-header__mark { height: calc(var(--header-height) * 0.60); width: auto; }
/* The wordmark is two stacked lines inside the image, so its cap height is a
   fraction of the box. It needs roughly 40% of the header to be readable at all. */
.fga-header__type { height: calc(var(--header-height) * 0.40); width: auto; }
.fga-header__brand img, .fga-footer__logo img { max-width: none; }
@media (max-width: 420px) { .fga-header__type { display: none; } }
.fga-header__wordmark {
  font-family: var(--font-display); font-size: var(--text-subheading);
  letter-spacing: var(--tracking-wordmark); text-transform: uppercase;
  color: var(--fg-stone-700);
}

.fga-nav { margin-left: auto; }
.fga-nav__list { display: flex; align-items: center; gap: var(--space-m); list-style: none; }
.fga-nav__link {
  font-family: var(--font-body); font-size: var(--text-body-sm);
  letter-spacing: var(--tracking-wide); color: var(--text-secondary-adaptive);
  white-space: nowrap;
  border-bottom: var(--border-width-hairline) solid transparent;
  padding-bottom: var(--space-3xs);
}
.fga-nav__link:hover { color: var(--text-primary); border-color: var(--border-accent); }
.fga-nav__link[aria-current] { color: var(--text-primary); border-color: var(--border-accent); }

.fga-nav__group { position: relative; }
.fga-nav__sub {
  position: absolute; top: 100%; left: calc(var(--space-s) * -1); min-width: 15rem;
  background: var(--bg-raised); border: var(--border-width-hairline) solid var(--border-subtle);
  box-shadow: var(--shadow-md); padding: var(--space-xs);
  list-style: none; display: grid; gap: var(--space-3xs);
  opacity: 0; visibility: hidden; transform: translateY(var(--space-2xs));
  transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
}
.fga-nav__group:hover .fga-nav__sub,
.fga-nav__group:focus-within .fga-nav__sub { opacity: 1; visibility: visible; transform: none; }
.fga-nav__sub a {
  display: block; padding: var(--space-2xs) var(--space-xs);
  font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); border: 0;
}
.fga-nav__sub a:hover { color: var(--text-accent); background: var(--surface-accent-soft); }

.fga-header__cta { flex: 0 0 auto; font-size: var(--text-caption); padding: var(--space-xs) var(--space-m); }
/* Most bookings come by phone, so the number is a control in the header rather
   than a line of text in the footer. It stays visible on mobile, where the
   booking CTA does not — a phone is the one device where tapping to call is the
   fastest possible route. */
/* A phone number is a figure. Lining tabular numerals keep the digits on one
   optical line and stop the number reading as a word. */
.fga-header__call {
  flex: 0 0 auto; font-size: var(--text-caption);
  padding: var(--space-xs) var(--space-m); white-space: nowrap;
  font-variant-numeric: lining-nums tabular-nums;
}
.fga-header__call-short { display: none; }
/* The full number needs more room than the nav can spare below a wide desktop,
   so it collapses to "Call" well before anything is forced to wrap. */
@media (max-width: 1400px) {
  .fga-header__call-full { display: none; }
  .fga-header__call-short { display: inline; }
}

.fga-menu { display: none; }
.fga-menu__toggle {
  display: inline-flex; align-items: center; gap: var(--space-2xs);
  list-style: none; cursor: pointer;
  border: var(--border-width-hairline) solid var(--border-interactive);
  border-radius: var(--radius-xs); padding: var(--space-2xs) var(--space-xs);
  font-family: var(--font-body); font-size: var(--text-caption);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-primary);
}
.fga-menu__toggle::-webkit-details-marker { display: none; }
.fga-menu__panel {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--bg-base);
  border-bottom: var(--border-width-hairline) solid var(--rule);
  box-shadow: var(--shadow-md);
  max-height: calc(100dvh - var(--header-height)); overflow-y: auto;
}
.fga-menu__inner { padding: var(--space-m) var(--gutter) var(--space-xl); display: grid; gap: var(--space-m); justify-items: start; }
.fga-menu__list { list-style: none; display: grid; gap: var(--space-s); width: 100%; }
.fga-menu__list > li > a, .fga-menu__list > li > span { border: 0; color: var(--text-primary); font-size: var(--step-1); }
.fga-menu__sub { list-style: none; display: grid; gap: var(--space-2xs); padding-left: var(--space-m); margin-top: var(--space-2xs); }
.fga-menu__sub a { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); border: 0; }

@media (max-width: 940px) {
  .fga-nav { display: none; }
  .fga-header__cta { display: none; }
  .fga-header__call { margin-left: auto; }
  .fga-menu { display: block; }
}
.fga-menu__routes { list-style: none; display: grid; gap: var(--space-2xs); width: 100%; padding-top: var(--space-m); border-top: var(--border-width-hairline) solid var(--rule); }
.fga-menu__routes a { font-size: var(--text-body-sm); color: var(--text-primary); border-bottom: var(--border-width-hairline) solid var(--border-accent); }

/* ---------------------------------------------------------------- bands */

.fga-band { padding-block: var(--band-y); }
.fga-band--tight { padding-block: var(--space-xl); }
.fga-band--page { background: var(--bg-base); }
.fga-band--sunken { background: var(--bg-sunken); }
.fga-band--sage { background: var(--accent-soft); }
.fga-band--butter { background: var(--surface-butter); }
.fga-band--inverse { background: var(--bg-inverse); color: var(--fg-inverse); }
.fga-band--inverse h1, .fga-band--inverse h2, .fga-band--inverse h3, .fga-band--inverse h4 { color: var(--fg-inverse); }
.fga-band--inverse p { color: var(--footer-text); }
.fga-band--inverse .fga-eyebrow { color: var(--fg-sage-300); }
/* :not(.fga-btn) matters here and is not defensive tidying. A button paints its
   own ground — .fga-btn--inverse is teal text on ivory — and this rule is two
   classes to the button's one, so without the exclusion every CTA on a dark band
   rendered pale sage on ivory at 1.23:1. That is the same shape as the bug that
   put teal-on-teal in the primary button, and the trust strip at x=0, and the
   safety paragraphs at 1.38:1. Ground-level rules must never colour a component
   that brought its own ground. */
.fga-band--inverse :is(a, .fga-prose a, .fga-cta a):not(.fga-btn) { color: var(--fg-sage-200); border-color: var(--fg-sage-400); }

/* Self-grounded surfaces inside the dark band. Note panels, dev markers and
   cards paint cream, butter or white and set their own text colour — but the
   band-level rules above are two classes to their one, so the headings,
   paragraphs, links and eyebrows *inside* them inherited the band's ivory and
   rendered at about 1.05:1 on their own light ground. Resetting the children to
   inherit hands them back to whatever ground their panel painted, which is what
   the panel already decided. Same principle as excluding .fga-btn above: a
   ground-level rule must not colour anything that brought its own ground. */
/* The repeated :is() is deliberate, not a typo. Written once, this rule is two
   classes and a type — exactly the weight of `.fga-band--inverse .fga-cta p`,
   which sits further down the file and therefore won on source order alone. A
   reset whose correctness depends on staying below another rule is a trap for
   whoever edits next. Repeating the surface list costs one class of specificity
   and makes it win by construction wherever it is written. */
.fga-band--inverse :is(.fga-note, .fga-todo, .fga-verify, .fga-card):is(.fga-note, .fga-todo, .fga-verify, .fga-card) :is(h1, h2, h3, h4, p, li, dt, dd) { color: inherit; }
.fga-band--inverse :is(.fga-note, .fga-todo, .fga-verify, .fga-card):is(.fga-note, .fga-todo, .fga-verify, .fga-card) .fga-eyebrow { color: var(--text-muted-adaptive); }
/* .fga-cred is in THIS list but deliberately not in the two above it. It paints
   its own ivory ground inside the dark band, so its links were taking the band's
   sage link colour and rendering at 1.2:1 on ivory. Its text does not need the
   blanket reset — the component sets a colour on every element it renders, the
   way the design system's CredentialBlock does — and adding it there would
   out-specify the block's own title and note rules and undo them. */
.fga-band--inverse :is(.fga-note, .fga-todo, .fga-verify, .fga-card, .fga-cred):is(.fga-note, .fga-todo, .fga-verify, .fga-card, .fga-cred) a:not(.fga-btn) {
  color: var(--text-accent);
  border-color: var(--border-accent);
  box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 var(--border-accent);
}
.fga-band--inverse :is(a, .fga-prose a):not(.fga-btn):hover { color: var(--fg-ivory-000); border-color: var(--fg-ivory-000); }
/* The hairline is a box-shadow on the ivory ground, so the dark ground has to
   restate it — sage-400 rather than the accent, which is invisible on teal. */
.fga-band--inverse :is(.fga-prose, .fga-cta, .fga-checklist, .fga-smallprint) a:not(.fga-btn, .fga-textlink) {
  box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 var(--fg-sage-400);
}
.fga-band--inverse :is(.fga-prose, .fga-cta, .fga-checklist, .fga-smallprint) a:not(.fga-btn, .fga-textlink):is(:hover, :focus-visible) {
  box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 transparent;
}

/* Vertical rhythm inside a band. Sections are assembled from independent
   pieces (a heading, some prose, a grid) so the spacing between them is set
   once here rather than by each component. */
.fga-band > .fga-container > * + * { margin-top: var(--space-l); }
.fga-band > .fga-container > h2 + *,
.fga-band > .fga-container > h3 + * { margin-top: var(--space-m); }
.fga-band > .fga-container > .fga-prose + .fga-cards,
.fga-band > .fga-container > .fga-prose + .fga-ba,
.fga-band > .fga-container > .fga-prose + .fga-steps,
.fga-band > .fga-container > .fga-prose + .fga-reviews,
.fga-band > .fga-container > .fga-prose + .fga-compare { margin-top: var(--space-xl); }

/* --------------------------------------------------------- breadcrumbs */

.fga-crumbs { padding-block: var(--space-m); border-bottom: var(--border-width-hairline) solid var(--rule); }
.fga-crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2xs); align-items: center; }
.fga-crumbs li { display: flex; align-items: center; gap: var(--space-2xs); font-size: var(--text-caption); color: var(--text-muted-adaptive); }
/* Both of these INHERIT the muted colour from the li rather than restating it.
   The adaptive tokens are a mix of currentColor, so applying one inside
   something that already applied it mixes the mix: 68% of 68% rendered the
   breadcrumbs at 2.72:1 against a 4.5:1 floor. Set it once per subtree. */
.fga-crumbs li + li::before { content: "/"; }
.fga-crumbs a { color: inherit; border-bottom-color: transparent; }
.fga-crumbs a:hover { color: var(--text-accent); border-bottom-color: var(--text-accent); }

/* ------------------------------------------------------------ page head */

.fga-pagehead { padding-block: var(--space-xl) var(--space-l); }
.fga-pagehead__inner { display: grid; gap: var(--space-m); justify-items: start; }
.fga-pagehead__title { font-size: var(--step-4); max-width: 20ch; }
.fga-pagehead .fga-actions { margin-top: var(--space-2xs); }
/* The band immediately after the page head does not need its own top padding. */
.fga-pagehead + .fga-band { padding-top: var(--space-l); }

/* A treatment page with a hero photograph: title beside the picture, on the
   same 7 : 4.4 grid as the product layout below, so the photograph sits over
   the booking rail. Stacked on a phone, title first. Only rendered when a
   photograph exists — the plain page head above is untouched without one. */
.fga-pagehead__split { display: grid; gap: var(--space-l); align-items: center; }
.fga-pagehead__media { aspect-ratio: 4 / 3; border-radius: var(--radius-xs); }
@media (min-width: 60rem) {
  .fga-pagehead__split { grid-template-columns: minmax(0, 7fr) minmax(0, 4.4fr); gap: var(--space-2xl); }
}

/* ------------------------------------------------------------------ hero */

.fga-hero { background: var(--bg-base); }
.fga-hero__inner {
  max-width: var(--container-wide); margin-inline: auto;
  padding: var(--space-9) var(--gutter);
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--space-2xl); align-items: center;
}
/* A standfirst, not a label. The eyebrow treatment — uppercase at 0.2em — is for
   one- or two-word category labels; a full sentence set that way is three lines
   of tracked capitals and reads as shouting. */
.fga-hero__eyebrow {
  max-width: 34ch; margin-bottom: var(--space-m);
  font-size: var(--step-1); line-height: var(--leading-body);
  color: var(--text-secondary-adaptive);
}
.fga-hero__display { font-size: var(--step-5); max-width: 18ch; }
.fga-hero__body { margin-top: var(--space-l); font-size: var(--step-1); color: var(--text-secondary-adaptive); max-width: 44ch; }
.fga-hero__actions { margin-top: var(--space-xl); }
.fga-hero__media { aspect-ratio: 4 / 5; }
@media (max-width: 900px) {
  .fga-hero__inner { grid-template-columns: 1fr; gap: var(--space-xl); }
  .fga-hero__media { aspect-ratio: 4 / 3; }
}

/* ------------------------------------------------------------ image slots */

/* An image slot is a plain toned block until a real photograph arrives. In a
   production build it carries no text at all — a shot brief rendered as body
   copy reads as an unfinished draft to every visitor. */
.fga-ph {
  background: var(--surface-muted);
  display: grid; place-items: center;
  overflow: hidden;
}
.fga-ph--filled { background: none; }
.fga-ph--filled img { width: 100%; height: 100%; object-fit: cover; }

/* Dev only. Monospace and a dashed rule so it can never be mistaken for design. */
.fga-ph__brief {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  line-height: 1.5; text-align: center; color: var(--text-secondary-adaptive);
  padding: var(--space-s); margin: var(--space-2xs);
  border: var(--border-width-hairline) dashed var(--border-strong);
  max-width: 34ch;
}

/* ------------------------------------------------------------ trust strip */

.fga-trust { background: var(--bg-sunken); border-block: var(--border-width-hairline) solid var(--rule); padding-block: var(--space-m); }
.fga-trust__list {
  max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter);
  list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-l);
}
.fga-trust__list li { font-size: var(--text-caption); letter-spacing: var(--tracking-wide); color: var(--text-secondary-adaptive); line-height: 1.4; }
.fga-trust__list li + li { border-left: var(--border-width-hairline) solid var(--rule); padding-left: var(--space-l); }
@media (max-width: 900px) {
  .fga-trust__list { grid-template-columns: repeat(2, 1fr); gap: var(--space-m); }
  .fga-trust__list li + li { border-left: 0; padding-left: 0; }
  .fga-trust__list li:nth-child(even) { border-left: var(--border-width-hairline) solid var(--rule); padding-left: var(--space-m); }
}

/* -------------------------------------------------------- editorial intro */

.fga-intro { max-width: var(--measure-intro); display: grid; gap: var(--space-m); }
.fga-intro p { font-size: var(--step-1); color: var(--text-secondary-adaptive); }
.fga-intro p + p { margin-top: 0; }

/* -------------------------------------------------------------- two column */

.fga-twocol { display: grid; gap: var(--space-xl); }
.fga-twocol__title { max-width: var(--measure); }
.fga-twocol__cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-xl); }
.fga-twocol__cols--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fga-twocol__cols--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fga-twocol__col {
  border-top: var(--border-width-strong) solid var(--border-accent);
  padding-top: var(--space-m);
}
.fga-twocol__col p { max-width: var(--measure); }
.fga-twocol__note { max-width: var(--measure); color: var(--text-secondary-adaptive); }
@media (max-width: 820px) {
  .fga-twocol__cols, .fga-twocol__cols--3, .fga-twocol__cols--4 { grid-template-columns: 1fr; gap: var(--space-l); }
}

/* ----------------------------------------------------------- column blocks */

.fga-blocks { display: grid; gap: var(--space-xl); }
.fga-blocks__grid { display: grid; gap: var(--space-l); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); counter-reset: fga-block; }
.fga-blocks__item {
  border-top: var(--border-width-hairline) solid var(--rule); padding-top: var(--space-m);
  display: grid; gap: var(--space-2xs);
  counter-increment: fga-block; position: relative; isolation: isolate;
}
/* The numeral sits BEHIND the text at 16%, not beside it at 42%. That difference
   is the whole point: these are three parallel explainers, not three steps, and a
   numeral in the reading line would assert an order the content does not have.
   Behind the text it is a counting device that gives the row rhythm and tells the
   eye how many there are, without claiming the first must precede the second. */
.fga-blocks__item::before {
  content: counter(fga-block, decimal-leading-zero) / "";
  position: absolute; top: var(--space-2xs); right: 0; z-index: -1;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: var(--tracking-display);
  font-size: var(--step-4); line-height: 1;
  opacity: 0.16; pointer-events: none;
}
.fga-blocks__item h3 { font-size: var(--text-subheading); }
.fga-blocks__item p { color: var(--text-secondary-adaptive); font-size: var(--text-body-sm); }

/* ----------------------------------------------------------- split feature */

.fga-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2xl); align-items: center; }
.fga-split__media { aspect-ratio: 4 / 5; }
.fga-split__body { display: grid; gap: var(--space-m); max-width: var(--measure); }
.fga-split--image-right .fga-split__media { order: 2; }
@media (max-width: 900px) {
  .fga-split { grid-template-columns: 1fr; gap: var(--space-xl); }
  .fga-split--image-right .fga-split__media { order: 0; }
  .fga-split__media { aspect-ratio: 4 / 3; }
}

/* --------------------------------------------------------------- card grid */

.fga-cards { display: grid; gap: var(--space-l); }
.fga-cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fga-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fga-cards--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 820px) { .fga-cards { grid-template-columns: 1fr !important; } }

.fga-card {
  display: flex; flex-direction: column; gap: var(--space-xs);
  padding: var(--space-m);
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-xs);
  color: var(--fg-ink);
  transition: border-color var(--dur-base) var(--ease-calm),
              box-shadow var(--dur-base) var(--ease-calm),
              transform var(--dur-base) var(--ease-calm);
}
a.fga-card { border-bottom-width: var(--border-width-hairline); }
/* Card hover: the border shifts to the accent and the arrow slides four pixels.
   Nothing lifts and nothing casts a shadow — the design system is explicit that
   a card is a piece of paper on a surface, not a tile floating above it. The
   arrow is a pseudo-element so it stays out of the accessible name; "Read more"
   is what a screen reader should hear, not "Read more right arrow". */
a.fga-card:hover, a.fga-card:focus-visible { border-color: var(--border-accent); color: var(--fg-ink); }
.fga-card__more::after {
  content: "\2192"; margin-left: var(--space-3xs); display: inline-block;
  transition: transform var(--dur-hover) var(--ease-reveal);
}
a.fga-card:hover .fga-card__more::after,
a.fga-card:focus-visible .fga-card__more::after,
a.fga-card__more:hover::after { transform: translateX(var(--card-arrow-shift)); }
.fga-card__top { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-xs); }
.fga-card h2, .fga-card h3 {
  font-family: var(--font-display); font-weight: var(--weight-display-med);
  font-size: var(--text-heading); line-height: var(--leading-heading);
}
.fga-card h2 a, .fga-card h3 a { color: var(--fg-ink); }
.fga-card h2 a:hover, .fga-card h3 a:hover { color: var(--text-accent); }
a.fga-card__more { border: 0; }
.fga-card p { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); }
/* The chip follows TreatmentCard.jsx:19-22 exactly: BODY face, tracked, with
   lining tabular numerals. It is a tag, not a figure — I had put it in the
   figure group, which rendered it in Cormorant and silently killed the
   declarations here. The generic figure rule and this component contract
   disagreed; the more specific contract wins. */
.fga-card__price {
  font-family: var(--font-body);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  font-variant-numeric: lining-nums tabular-nums;
  padding: var(--space-3xs) var(--space-xs); border-radius: var(--radius-pill);
  background: var(--surface-butter); border: var(--border-width-hairline) solid var(--border-brass);
  white-space: nowrap; color: var(--text-primary);
}
/* The child-treatment list on a category card. The design system's TreatmentCard
   specifies these precisely: caption size, muted ink, and a 3px teal dot before
   each one — not accent-coloured links. They are links here because a hub card
   lists real destinations, so the dot treatment carries a hover state rather
   than a permanent underline, which would turn the card into a wall of rules. */
.fga-card__links { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-s); }
.fga-card__links > li { display: flex; align-items: center; gap: var(--dot-gap); }
.fga-card__links > li::before {
  content: ""; flex: 0 0 auto;
  width: var(--dot-size); height: var(--dot-size);
  border-radius: var(--radius-circle);
  background: var(--border-accent);
}
.fga-card__links a {
  font-size: var(--text-caption);
  color: var(--text-muted-adaptive);
  border-bottom: var(--border-width-hairline) solid transparent;
}
.fga-card__links a:hover { color: var(--text-accent); border-bottom-color: var(--text-accent); }
.fga-card__foot {
  margin-top: auto; padding-top: var(--space-s);
  border-top: var(--border-width-hairline) solid var(--rule);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs);
  font-size: var(--text-caption); letter-spacing: var(--tracking-wide); color: var(--text-muted-adaptive);
}
.fga-card__more { color: var(--text-accent); }

/* ----------------------------------------------------------------- stepper */

.fga-steps { list-style: none; display: grid; gap: 0; max-width: var(--measure); counter-reset: fga-step; }
.fga-steps__item {
  position: relative; padding-left: var(--space-xl); padding-bottom: var(--space-l);
  counter-increment: fga-step;
}
/* 01, not 1 — a leading zero reads as a set with a known length, which is what a
   sequence of steps is. Display face with lining tabular numerals so the digits
   sit on one optical line down the left rail. The trailing / "" is alternative
   text: the numeral is decoration over an <ol> that already conveys its own
   order, and without it a screen reader announces the number twice. */
.fga-steps__item::before {
  content: counter(fga-step, decimal-leading-zero) / "";
  position: absolute; left: 0; top: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: var(--tracking-display);
  font-size: var(--text-subheading);
  color: var(--text-accent); line-height: 1.2;
  opacity: 0.42;
}
.fga-steps__item::after {
  content: ""; position: absolute; left: calc(var(--space-xs) - 1px); top: var(--space-m); bottom: 0;
  width: var(--border-width-hairline); background: var(--rule);
}
.fga-steps__item:last-child { padding-bottom: 0; }
.fga-steps__item:last-child::after { display: none; }
.fga-steps__item h3 { font-size: var(--text-subheading); margin-bottom: var(--space-3xs); }
.fga-steps__item p { color: var(--text-secondary-adaptive); }

/* -------------------------------------------------------------- note panel */

.fga-note {
  display: grid; gap: var(--space-xs);
  padding: var(--space-xl);
  border-radius: var(--radius-xs);
  max-width: var(--container);
}
.fga-note__label { display: block; }
.fga-note h2, .fga-note h3 { font-family: var(--font-display); }
.fga-note p { max-width: var(--measure); }

.fga-note--neutral {
  background: var(--note-neutral-bg); color: var(--note-neutral-fg);
  border: var(--border-width-hairline) solid var(--note-neutral-rule);
}
.fga-note--caution {
  background: var(--note-caution-bg); color: var(--note-caution-fg);
  border: var(--border-width-hairline) solid var(--note-caution-rule);
  border-top-width: var(--border-width-strong);
}
.fga-note--critical {
  background: var(--note-critical-bg); color: var(--note-critical-fg);
  border: var(--border-width-hairline) solid var(--note-critical-bg);
  border-top: var(--border-width-loud) solid var(--note-critical-rule);
  box-shadow: var(--shadow-lg);
}
.fga-note--critical .fga-note__label { color: var(--note-critical-rule); }
.fga-note--critical h2, .fga-note--critical h3 { color: var(--note-critical-fg); font-size: var(--step-3); }
.fga-note--critical p { color: var(--note-critical-fg); font-size: var(--step-1); }
.fga-note--critical .fga-list > li::before { background: var(--note-critical-rule); }
.fga-note--critical .fga-prose a, .fga-note--critical a { color: var(--fg-butter-200); border-color: var(--fg-butter-300); }

/* --------------------------------------------------------------- checklist */

.fga-checklist { display: grid; gap: var(--space-m); max-width: var(--measure); }
.fga-checklist__items { list-style: none; display: grid; gap: 0; }
.fga-checklist__items > li {
  padding: var(--space-s) 0 var(--space-s) var(--space-l);
  border-bottom: var(--border-width-hairline) solid var(--rule);
  position: relative;
}
.fga-checklist__items > li:first-child { border-top: var(--border-width-hairline) solid var(--rule); }
.fga-checklist__items > li::before {
  content: ""; position: absolute; left: 0; top: calc(var(--space-s) + 0.5em);
  width: var(--space-s); height: var(--space-2xs);
  border-left: var(--border-width-strong) solid var(--text-accent);
  border-bottom: var(--border-width-strong) solid var(--text-accent);
  transform: rotate(-45deg);
}
.fga-checklist__download { justify-self: start; }

/* -------------------------------------------------------- comparison table */

.fga-compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: var(--space-l); }
.fga-compare__col {
  padding: var(--space-m);
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
  display: grid; gap: var(--space-xs); align-content: start;
}
.fga-compare__col h3 { font-size: var(--text-subheading); }
.fga-compare__col p { color: var(--text-secondary-adaptive); font-size: var(--text-body-sm); }

/* The attribute form: the same questions answered for each option. One card per
   option, each a definition list, so a phone reads one option at a time. Side by
   side, the cards become subgrids of one grid, and every row lines up with the
   matching row in its neighbours: that alignment is what makes it a comparison
   rather than three lists. Without subgrid the cards still read correctly; the
   rows just stop lining up. The row counts come from the markup, because they
   are a property of the content. */
.fga-matrix-wrap { display: grid; gap: var(--space-l); }
.fga-matrix { display: grid; gap: var(--space-m); }
.fga-matrix__col {
  display: grid; align-content: start; gap: var(--space-xs);
  padding: var(--space-m);
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
}
.fga-matrix__col h3, .fga-matrix__col h2 { font-size: var(--text-subheading); }
.fga-matrix__list { display: grid; margin: 0; }
.fga-matrix__row {
  display: grid; gap: var(--space-3xs); align-content: start;
  padding-block: var(--space-xs);
  border-top: var(--border-width-hairline) solid var(--rule);
}
.fga-matrix__row dt {
  font-size: var(--text-caption); font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-muted-adaptive);
}
.fga-matrix__row dd { margin: 0; font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); }
.fga-matrix__note { color: var(--text-secondary-adaptive); }
@media (min-width: 60rem) {
  .fga-matrix--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fga-matrix--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  @supports (grid-template-rows: subgrid) {
    .fga-matrix { row-gap: 0; }
    .fga-matrix__col { grid-row: span var(--fga-matrix-span); grid-template-rows: subgrid; row-gap: 0; }
    .fga-matrix__col > :first-child { padding-bottom: var(--space-xs); }
    .fga-matrix__list { grid-row: span var(--fga-matrix-rows); grid-template-rows: subgrid; }
  }
}

/* ------------------------------------------------------------------ prices */

.fga-prices { display: grid; gap: var(--space-m); max-width: var(--container); }
.fga-price-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-xs);
  padding-block: var(--space-s);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-price-row:first-child { border-top: var(--border-width-hairline) solid var(--rule); }
/* font-size lives with the listing rules further down, stated once. */
.fga-price-row__label { flex: 0 1 auto; min-width: 12ch; }
.fga-price-row__label a { color: var(--fg-ink); border-bottom: var(--border-width-hairline) solid transparent; }
.fga-price-row__label a:hover { color: var(--text-accent); border-bottom-color: var(--text-accent); }
.fga-price-row__dots { flex: 1 1 var(--space-l); min-width: var(--space-l); border-bottom: var(--border-width-hairline) dotted var(--border-strong); transform: translateY(-0.25em); }
.fga-price-row__note { font-size: var(--text-caption); color: var(--text-muted-adaptive); max-width: 28ch; text-align: right; }
.fga-price-row__value { font-size: var(--text-subheading);
  font-weight: var(--weight-display-med); text-align: right; flex: 0 1 auto;
}
.fga-price-row__value .fga-verify { font-size: var(--text-body-sm); }
.fga-prices__footnote { color: var(--text-secondary-adaptive); max-width: var(--measure); }

.fga-pricegroup { display: grid; gap: var(--space-m); }
.fga-pricegroup + .fga-pricegroup { margin-top: var(--space-xl); }

/* Consultation fee — the only pricing component permitted on a POM page. */
.fga-fee {
  display: grid; gap: var(--space-xs); padding: var(--space-xl);
  background: var(--accent-soft);
  border: var(--border-width-hairline) solid var(--fg-sage-300);
  border-radius: var(--radius-xs);
  max-width: var(--container);
}
.fga-fee__amount { font-family: var(--font-display); font-size: var(--step-3); line-height: var(--leading-display); }
.fga-fee__footnote { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); max-width: var(--measure); }

/* --------------------------------------------------------------- accordion */

.fga-faq { border-top: var(--border-width-hairline) solid var(--rule); max-width: var(--container); }
.fga-faq__item { border-bottom: var(--border-width-hairline) solid var(--rule); }
.fga-faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-s);
  padding-block: var(--space-m); cursor: pointer; list-style: none;
  font-family: var(--font-display); font-size: var(--text-subheading);
  font-weight: var(--weight-display-med); color: var(--text-primary);
}
.fga-faq__q::-webkit-details-marker { display: none; }
.fga-faq__sign { position: relative; width: var(--sign-size); height: var(--sign-size); flex: 0 0 auto; }
.fga-faq__sign::before, .fga-faq__sign::after {
  content: ""; position: absolute; background: var(--text-muted-adaptive);
  transition: transform var(--dur-base) var(--ease-calm);
}
.fga-faq__sign::before { top: calc(var(--sign-size) / 2 - 1px); left: 0; width: var(--sign-size); height: var(--border-width-hairline); }
.fga-faq__sign::after { top: 0; left: calc(var(--sign-size) / 2 - 1px); width: var(--border-width-hairline); height: var(--sign-size); }
.fga-faq__item[open] .fga-faq__sign::after { transform: scaleY(0); }
.fga-faq__a { padding-bottom: var(--space-m); max-width: var(--measure); }
.fga-faq__a p { color: var(--text-secondary-adaptive); }
.fga-faq__a p + p { margin-top: var(--space-xs); }

/* ------------------------------------------------------------ before/after */

/* Photographs from content/galleries.json, shown whole at their own shape: a
   pair's two halves are checked to be the same shape, so they line up without
   either being cropped. Never a CSS filter, blend or opacity on these images —
   docs/04 forbids any adjustment, and "unretouched" has to be true on screen. */
.fga-ba { display: grid; gap: var(--space-xl) var(--space-l); grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); align-items: start; }
.fga-ba__item { display: grid; gap: var(--space-xs); align-content: start; margin: 0; }
.fga-ba__pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3xs); }
.fga-ba__side, .fga-ba__single { display: grid; gap: var(--space-3xs); align-content: start; }
.fga-ba__item img { display: block; width: 100%; height: auto; border-radius: var(--radius-xs); }
.fga-ba__label { font-size: var(--text-eyebrow); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-secondary-adaptive); }
.fga-ba__caption { font-size: var(--text-caption); color: var(--text-secondary-adaptive); }
/* A pair is two photographs, so on a screen wide enough for two columns it
   takes two: each half then shows at the size a single photograph does. */
@media (min-width: 60rem) { .fga-ba__item--pair { grid-column: span 2; } }
.fga-ba__group + .fga-ba__group { margin-top: var(--space-2xl); }
.fga-ba__group-title { margin-bottom: var(--space-m); }
.fga-ba + .fga-ba__note { margin-top: var(--space-xl); }

/* ---------------------------------------------------------------- reviews */

.fga-reviews { display: grid; gap: var(--space-l); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.fga-review {
  display: grid; gap: var(--space-xs); align-content: start;
  padding: var(--space-m); background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
}
/* The quotation itself. Only the card shell and the attribution were styled, so
   the review text fell through to the body default — invisible today because
   there are no reviews yet, and a surprise the day the first one arrives. */
.fga-review blockquote { margin: 0; }
.fga-review blockquote p {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-subheading);
  line-height: var(--leading-body); /* ReviewCard.jsx:11 sets 1.5, not heading leading */
  letter-spacing: var(--tracking-display);
  hanging-punctuation: first last;
}
.fga-review__meta { font-size: var(--text-caption); color: var(--text-muted-adaptive); letter-spacing: var(--tracking-wide); }

/* -------------------------------------------------------------- map panel */

.fga-map { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2xl); align-items: start; }
.fga-map__body { display: grid; gap: var(--space-m); max-width: var(--measure); }
.fga-map__frame { aspect-ratio: 4 / 3; border: var(--border-width-hairline) solid var(--border-subtle); overflow: hidden; }
.fga-map__frame iframe { width: 100%; height: 100%; border: 0; display: block; }
/* An embedded map is in the tab order and its contents are interactive, so the
   frame needs a visible focus state. :focus rather than :focus-visible — the
   heuristic does not apply to a frame the browser hands focus to — and the
   outline sits inside the frame so it is not clipped by the wrapper. */
.fga-map__frame:focus-within, .fga-booking__frame:focus-within,
.fga-map__frame iframe:focus, .fga-booking__frame iframe:focus,
.fga-map__frame iframe:focus-within, .fga-booking__frame iframe:focus-within {
  outline: var(--border-width-strong) solid var(--focus-ring);
  outline-offset: -3px;
}
.fga-map__address { font-style: normal; }
.fga-map__address p + p { margin-top: var(--space-xs); }
@media (max-width: 900px) { .fga-map { grid-template-columns: 1fr; gap: var(--space-xl); } }

/* ------------------------------------------------------------ journey table */

.fga-journey { width: 100%; border-collapse: collapse; max-width: var(--container); }
.fga-journey caption { text-align: left; font-size: var(--text-caption); color: var(--text-muted-adaptive); padding-bottom: var(--space-xs); }
.fga-journey th, .fga-journey td {
  text-align: left; padding: var(--space-xs) var(--space-s) var(--space-xs) 0;
  border-bottom: var(--border-width-hairline) solid var(--rule);
  font-size: var(--step-0);
}
.fga-journey th { font-family: var(--font-body); font-weight: var(--weight-medium); font-size: var(--text-caption); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-muted-adaptive); }
.fga-journey td:last-child { text-align: right; }

/* ------------------------------------------------------- practitioner card */

.fga-practitioner { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); gap: var(--space-2xl); align-items: start; }
.fga-practitioner__media { aspect-ratio: 4 / 5; }
.fga-practitioner__body { display: grid; gap: var(--space-m); max-width: var(--measure); }
/* The layout rules that used to live here are gone, replaced by .fga-cred.
   They are deleted rather than left in place because `.fga-practitioner__creds div`
   is a class and a type — it out-specified `.fga-cred__row` and forced the rows
   back to flex, so the longest qualification wrapped its value onto a second
   line while the shorter ones stayed inline. Two rules describing the same
   element's layout is the bug; keeping the losing one "just in case" is how it
   happens. The class stays on the element only because the prose-link selectors
   above still reference it. */
.fga-practitioner__creds { margin: 0; }
@media (max-width: 900px) { .fga-practitioner { grid-template-columns: 1fr; gap: var(--space-xl); } }

/* --------------------------------------------------------------- routes */

.fga-routes { display: grid; gap: var(--space-m); }
.fga-routes__lead { max-width: var(--measure); color: var(--text-secondary-adaptive); font-size: var(--step-1); }
.fga-routes__grid {
  display: grid; gap: var(--space-s);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin-top: var(--space-2xs);
}
.fga-routes__item {
  display: grid; gap: var(--space-3xs); align-content: center;
  padding: var(--space-m) var(--space-l);
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-xs);
  color: var(--fg-ink);
  transition: border-color var(--dur-base) var(--ease-calm),
              box-shadow var(--dur-base) var(--ease-calm),
              transform var(--dur-base) var(--ease-calm);
}
.fga-routes__item:hover { border-color: var(--border-accent); box-shadow: var(--shadow-md); transform: translateY(-2px); color: var(--fg-ink); }
.fga-routes__item--lead {
  background: var(--accent); border-color: var(--accent);
  color: var(--text-on-accent); box-shadow: var(--shadow-sm);
}
.fga-routes__item--lead:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--text-on-accent); }
.fga-routes__label { font-family: var(--font-body); font-size: var(--text-body-sm); letter-spacing: var(--tracking-wide); }
.fga-routes__detail { font-family: var(--font-display); font-size: var(--text-display-4); line-height: var(--leading-heading); }
.fga-routes__item:not(.fga-routes__item--lead) .fga-routes__detail { font-size: var(--text-subheading); color: var(--text-secondary-adaptive); }

/* ---------------------------------------------------------- booking embed */

.fga-booking { display: grid; gap: var(--space-m); max-width: var(--container); }
.fga-booking__frame {
  min-height: 34rem; border: var(--border-width-hairline) solid var(--border-subtle);
  background: var(--bg-raised); display: grid; place-items: center; padding: var(--space-xl);
}
.fga-booking__frame iframe { width: 100%; min-height: 34rem; border: 0; }
.fga-booking__facade { display: grid; gap: var(--space-m); justify-items: center; text-align: center; max-width: 40ch; }
.fga-booking__facade p { color: var(--text-secondary-adaptive); font-size: var(--text-body-sm); }
.fga-booking__fallback { font-size: var(--step-1); }

/* -------------------------------------------------------------- link list */

.fga-linklist { list-style: none; display: grid; gap: 0; max-width: var(--container); }
.fga-linklist > li { border-bottom: var(--border-width-hairline) solid var(--rule); }
.fga-linklist > li:first-child { border-top: var(--border-width-hairline) solid var(--rule); }
.fga-linklist a, .fga-linklist span {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-s);
  padding-block: var(--space-s); border: 0; font-size: var(--step-1); color: var(--text-primary);
}
.fga-linklist a::after { content: "\2192"; color: var(--text-accent); flex: 0 0 auto; }
.fga-linklist a:hover { color: var(--text-accent); }

/* --------------------------------------------------------------- cta band */

.fga-cta { display: grid; gap: var(--space-m); justify-items: center; text-align: center; max-width: 44ch; margin-inline: auto; }
.fga-cta p { color: var(--text-secondary-adaptive); }
.fga-cta .fga-actions { justify-content: center; margin-top: var(--space-xs); }
.fga-band--inverse .fga-cta p { color: var(--footer-text); }

/* ------------------------------------------------------------- small print */

.fga-smallprint { max-width: var(--measure); font-size: var(--step--1); color: var(--text-secondary-adaptive); display: grid; gap: var(--space-xs); }
.fga-smallprint h2, .fga-smallprint h3 { font-size: var(--text-subheading); color: var(--fg-ink); }

/* ---------------------------------------------------------- article/legal */

.fga-longform { max-width: var(--measure); display: grid; gap: var(--space-m); }
.fga-longform h2 { font-size: var(--step-3); margin-top: var(--space-l); }
.fga-longform h3 { font-size: var(--text-subheading); }
.fga-byline { display: flex; flex-wrap: wrap; gap: var(--space-s); font-size: var(--text-caption); color: var(--text-muted-adaptive); letter-spacing: var(--tracking-wide); }

/* -------------------------------------------------------------- editorial */

/* Unconfirmed-fact marks (.fga-verify) and unfinished-content panels. These are
   meant to be impossible to mistake for finished copy, and the production build
   refuses to ship them. */
.fga-verify {
  background: var(--fg-butter-200);
  border-bottom: var(--border-width-strong) solid var(--fg-danger-500);
  padding: 0 var(--space-3xs); font-weight: var(--weight-medium);
  color: var(--fg-ink-950);
}
.fga-todo {
  display: grid; gap: var(--space-xs); padding: var(--space-m);
  border: var(--border-width-strong) dashed var(--fg-danger-500);
  background: var(--fg-butter-100); border-radius: var(--radius-xs);
  max-width: var(--measure);
  /* Paints butter, so it has to state its own text colour. Without this its
     contents inherit from whatever band it lands on, and on the dark band that
     is ivory — butter-on-butter at 1.02:1. Any rule that sets a background
     belongs next to a rule that sets a colour. */
  color: var(--text-primary);
}
.fga-todo__label { color: var(--fg-danger-500); font-weight: var(--weight-semibold); }
.fga-todo p { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); }

/* ---------------------------------------------------------------- footer */

.fga-footer { background: var(--bg-inverse); color: var(--fg-inverse); padding-block: var(--space-9) var(--space-l); }
.fga-footer__top {
  max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: var(--space-xl); align-items: start;
}
.fga-footer__brand { display: grid; gap: var(--space-s); }
/* The same lockup as the header: mark beside wordmark. The light wordmark is the
   one that survives the deep teal; the mark's own colours carry on it unchanged.
   Both carry height:auto so the width/height attributes — which are there for
   layout stability — cannot stretch them. */
.fga-footer__logo { display: inline-flex; align-items: center; gap: var(--space-xs); border: 0; }
.fga-footer__mark { height: 3.25rem; width: auto; }
.fga-footer__type { height: 1.4rem; width: auto; opacity: 0.92; }
.fga-footer__wordmark { font-family: var(--font-display); font-size: var(--text-heading); letter-spacing: var(--tracking-wordmark); text-transform: uppercase; }
.fga-footer p, .fga-footer li { font-size: var(--text-body-sm); color: var(--footer-text); }
.fga-footer h2 { font-family: var(--font-body); font-size: var(--text-eyebrow); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--fg-sage-300); font-weight: var(--weight-medium); margin-bottom: var(--space-xs); }
.fga-footer ul { list-style: none; display: grid; gap: var(--space-2xs); }
.fga-footer a { color: var(--footer-text); border-bottom-color: transparent; }
.fga-footer a:hover { color: var(--fg-ivory-000); border-bottom-color: var(--footer-quiet); }
.fga-footer address { font-style: normal; display: grid; gap: var(--space-2xs); }
.fga-footer__bottom {
  max-width: var(--container-wide); margin: var(--space-xl) auto 0; padding: var(--space-m) var(--gutter) 0;
  border-top: var(--border-width-hairline) solid var(--footer-rule);
  display: flex; justify-content: space-between; gap: var(--space-s); flex-wrap: wrap;
}
.fga-footer__bottom span, .fga-footer__bottom a { font-size: var(--text-caption); color: var(--footer-quiet); }
@media (max-width: 1000px) { .fga-footer__top { grid-template-columns: repeat(2, 1fr); gap: var(--space-l); } }
@media (max-width: 620px) { .fga-footer__top { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------- enquiry */

.fga-enquiry {
  width: min(38rem, calc(100vw - var(--space-l)));
  max-height: min(46rem, calc(100dvh - var(--space-l)));
  margin: auto; padding: var(--space-xl);
  background: var(--bg-raised); color: var(--fg-ink);
  border: var(--border-width-hairline) solid var(--border-interactive);
  border-radius: var(--radius-xs); box-shadow: var(--shadow-lg);
  overflow: auto;
}
.fga-enquiry::backdrop { background: var(--overlay-scrim); }
.fga-enquiry[open] { display: grid; gap: var(--space-m); align-content: start; }
/* The frame reserves its height BEFORE the iframe exists. Without this the host
   is a zero-height div until the iframe lands and JavaScript sets an inline
   min-height, so the dialog opened short and then jumped — the layout shift.
   The reservation matches the inline min-height the mount uses, so the box never
   changes size; LeadConnector's resizer only ever grows it from there. */
.fga-enquiry__frame { min-height: 30rem; }
.fga-enquiry__frame iframe { display: block; width: 100%; border: 0; }
.fga-enquiry__head { display: flex; align-items: start; justify-content: space-between; gap: var(--space-m); }
.fga-enquiry h2 { font-size: var(--step-3); }
.fga-enquiry p { color: var(--text-secondary-adaptive); max-width: var(--measure); }
.fga-enquiry__close {
  flex: 0 0 auto; background: transparent; cursor: pointer; line-height: 1;
  border: var(--border-width-hairline) solid var(--border-interactive);
  border-radius: var(--radius-xs); color: var(--text-primary);
  /* A <button> does not inherit the document font — user agents give form
     controls their own. Every other control on the site states it; this one
     was relying on a default. */
  font-family: var(--font-body);
  font-size: var(--text-heading); padding: 0 var(--space-2xs);
}
.fga-enquiry__close:hover { color: var(--text-accent); border-color: var(--text-accent); }
.fga-enquiry__frame:empty { min-height: 0; }
.fga-enquiry__frame iframe { width: 100%; min-height: 30rem; border: 0; }
.fga-enquiry__frame iframe:focus, .fga-enquiry__frame:focus-within {
  outline: var(--border-width-strong) solid var(--focus-ring); outline-offset: -3px;
}

.fga-footer__enquiry {
  background: transparent; cursor: pointer; padding: 0;
  border: 0; border-bottom: var(--border-width-hairline) solid var(--footer-quiet);
  font-family: var(--font-body); font-size: var(--text-caption); color: var(--footer-text);
}
.fga-footer__enquiry:hover { color: var(--fg-ivory-000); border-bottom-color: var(--fg-ivory-000); }

.fga-map__enquiry { display: grid; gap: var(--space-m); margin-top: var(--space-l); }
.fga-map__enquiry h3 { font-size: var(--text-subheading); }

/* --------------------------------------------------------------- consent */

.fga-consent {
  position: fixed; inset: auto var(--space-m) var(--space-m) auto;
  max-width: 26rem; width: calc(100% - var(--space-l)); margin: 0; z-index: 60;
  background: var(--bg-raised); color: var(--fg-ink);
  border: var(--border-width-hairline) solid var(--border-interactive);
  border-radius: var(--radius-xs); box-shadow: var(--shadow-lg);
  padding: var(--space-m); display: none;
}
.fga-consent[open] { display: grid; gap: var(--space-xs); }
.fga-consent p { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); }
.fga-consent .fga-actions { margin-top: var(--space-2xs); }

/* ---------------------------------------------------------------- reveal */

/* The hidden state is applied only once JavaScript has confirmed it can reveal
   it again. Without this a no-JS visitor gets a blank page. */
.fga-js .fga-reveal { opacity: 0; transform: translateY(var(--reveal-distance)); }
.fga-js .fga-reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur-reveal) var(--ease-reveal), transform var(--dur-reveal) var(--ease-reveal);
}

/* Stagger runs WITHIN a section and never across the page — a band that scrolls
   into view is one event, not the fourth in a sequence. Items inside it fade in
   sequence, opacity only: the band is already rising, and a second translate on
   the children compounds into a slide nobody asked for. The index is set here by
   position rather than in JavaScript, so no-JS and reduced-motion cost nothing. */
.fga-js .fga-reveal :is(.fga-cards, .fga-blocks, .fga-numbered) > * { opacity: 0; }
.fga-js .fga-reveal.is-in :is(.fga-cards, .fga-blocks, .fga-numbered) > * {
  opacity: 1;
  transition: opacity var(--dur-reveal) var(--ease-reveal);
  transition-delay: calc(var(--reveal-index, 0) * var(--reveal-stagger));
}
:is(.fga-cards, .fga-blocks, .fga-numbered) > *:nth-child(2) { --reveal-index: 1; }
:is(.fga-cards, .fga-blocks, .fga-numbered) > *:nth-child(3) { --reveal-index: 2; }
:is(.fga-cards, .fga-blocks, .fga-numbered) > *:nth-child(4) { --reveal-index: 3; }
:is(.fga-cards, .fga-blocks, .fga-numbered) > *:nth-child(n+5) { --reveal-index: 4; }
@media (prefers-reduced-motion: reduce) {
  .fga-js .fga-reveal, .fga-js .fga-reveal.is-in { opacity: 1; transform: none; transition: none; }
  .fga-js .fga-reveal :is(.fga-cards, .fga-blocks, .fga-numbered) > * { opacity: 1; transition: none; }
  .fga *, .fga *::before, .fga *::after {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------------ print */

@media print {
  .fga-header, .fga-consent, .fga-skip, .fga-menu { display: none !important; }
  .fga-faq__a { display: block !important; }
  .fga { background: var(--fg-ivory-000); }
}

/* ------------------------------------------------- typographic detail */

/* Quotes and opening brackets hang into the margin on display type, so the
   optical left edge lines up with the text below it rather than being pushed in
   by a punctuation mark. Display sizes only — at body size the effect is noise. */
:where(.fga) :where(h1, h2) { hanging-punctuation: first last; }

/* Anything that is a figure rather than a word: prices, years, phone numbers.
   Lining tabular numerals so a column of prices aligns on the digit, and the
   display serif so a price reads as a statement rather than as interface text. */
/* THE figure treatment. Every element on the site that renders a number as a
   number belongs in this list rather than restating the three properties, which
   is how components ended up with two of the three: a price in the display face
   with tabular numerals but no tracking, or a phone number in tabular numerals
   with no face. One list, one place to change it, and nothing can drift to
   two-thirds of the treatment. */
.fga-figure,
.fga-card__linkprice,
.fga-price__amount,
.fga-price-row__value,
.fga-prices td:last-child,
.fga-numbered__num,
.fga-mega__price,
.fga-railcard__price,
.fga-fee__amount,
.fga-routes__detail,
.fga-facts dd,
.fga-journey td {
  font-family: var(--font-display);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: var(--tracking-display);
}

/* ------------------------------------------------------ accordion motion */

/* Native height animation. ::details-content is what makes an accordion openable
   without measuring anything in JavaScript; browsers that do not support it get
   an instant open, which is the correct fallback rather than a broken one. */
.fga-faq__item::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size var(--dur-base) var(--ease-reveal),
              content-visibility var(--dur-base) allow-discrete;
  transition-behavior: allow-discrete;
}
.fga-faq__item[open]::details-content { block-size: auto; }
.fga-faq__sign::before, .fga-faq__sign::after { transition: transform var(--dur-base) var(--ease-reveal); }

/* ------------------------------------------------------------ hero settle */

/* The page arriving, not animating. The image is already at rest by the time
   the eye reaches it; only the text rises, and only once. Guarded on .fga-js so
   that without JavaScript the hero is simply there. */
@media (prefers-reduced-motion: no-preference) {
  .fga-js .fga-hero__media > img { animation: fga-hero-settle var(--hero-settle-dur) var(--ease-reveal) both; }
  .fga-js .fga-hero__inner > div > * { animation: fga-hero-rise var(--hero-fade-dur) var(--ease-reveal) both; }
  .fga-js .fga-hero__inner > div > :nth-child(2) { animation-delay: calc(var(--hero-text-delay) * 1); }
  .fga-js .fga-hero__inner > div > :nth-child(3) { animation-delay: calc(var(--hero-text-delay) * 1.5); }
  .fga-js .fga-hero__inner > div > :nth-child(n+4) { animation-delay: calc(var(--hero-text-delay) * 2); }
}
@keyframes fga-hero-settle {
  from { transform: scale(var(--hero-settle-scale)); opacity: 0; }
  to { transform: none; opacity: 1; }
}
@keyframes fga-hero-rise {
  from { transform: translateY(var(--hero-text-rise)); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* ---------------------------------------------------------- pull quote */

/* The same paragraph the author wrote, set at display scale and pulled left out
   of the text column so the measure visibly breaks. The offset is what makes it
   read as a lift rather than as a heading that lost its level: text that starts
   left of everything around it cannot be mistaken for the next section.

   No quotation marks. Nobody is being quoted — this is the clinic's own prose,
   and adding marks would attribute it to a speaker who does not exist. */
.fga-pullquote {
  margin-block: var(--space-l);
  padding-top: var(--space-m);
  border-top: var(--border-width-hairline) solid var(--border-accent);
  max-width: var(--measure-intro);
}
/* Two classes, not one. `.fga-prose p` and `.fga-intro p` are a class and a type,
   so a single class here loses to them and the quote renders at body size — which
   is the entire failure this component exists to avoid. */
.fga-pullquote .fga-pullquote__line {
  font-family: var(--font-display);
  font-size: var(--text-display-3);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  font-weight: var(--weight-display);
  /* Inherit, never --fg-ink. A pull quote is promoted from the page's own prose,
     and the promotion can land on any ground the section happens to have — this
     one landed on the dark band on /safety-and-standards and rendered ink on
     teal at 1.39:1. The quote is the same voice as the paragraph around it, so
     it should be the same colour as the paragraph around it. */
  color: inherit;
  text-wrap: balance;
  hanging-punctuation: first last;
  margin: 0;
}
/* Pulled into the left margin only where there is a margin to pull into. Below
   the rail breakpoint the text column already starts at the gutter, and a
   negative indent would push it off the screen. */
@media (min-width: 60rem) {
  .fga-pullquote { margin-left: calc(-1 * var(--space-l)); }
}

/* ------------------------------------------------------- product layout */

/* Treatment pages read as a product, not an article: prose in the main column,
   and a rail beside it carrying the price and the booking action. The ratio is
   the design system's asymmetric grid — 7fr to 4.4fr — which keeps the prose
   close to its natural measure instead of stretching it to fill half a page. */
.fga-product__grid { display: grid; gap: var(--space-2xl) var(--space-2xl); align-items: start; }
.fga-product__main { min-width: 0; display: grid; gap: var(--space-2xl); }
.fga-blk { min-width: 0; }

.fga-rail-product { min-width: 0; }

@media (min-width: 60rem) {
  .fga-product__grid { grid-template-columns: minmax(0, 7fr) minmax(0, 4.4fr); }
  /* Sticks below the header rather than at the viewport top, or the card slides
     under the sticky header and loses its first line. */
  .fga-rail-product { position: sticky; top: calc(var(--header-height) + var(--space-m)); }
}

.fga-railcard {
  display: grid; gap: var(--space-s);
  padding: var(--space-l);
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
}
.fga-railcard__from {
  font-family: var(--font-body); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  font-weight: var(--weight-medium); color: var(--text-muted-adaptive);
}
/* The price is the largest figure on the page and the reason the rail exists.
   Display face, lining tabular numerals, and the unit set beside it in body type
   so "£190" reads as the number and "0.5ml" reads as the qualifier. */
.fga-railcard__price {
  font-size: var(--step-4); line-height: 1; margin: 0;
}
.fga-railcard__price small {
  font-family: var(--font-body); font-size: var(--text-body-sm);
  font-variant-numeric: normal; letter-spacing: var(--tracking-body);
  color: var(--text-muted-adaptive); margin-left: var(--space-3xs);
}
.fga-railcard__line { font-size: var(--text-body); margin: 0; }
.fga-railcard__note { font-size: var(--text-body-sm); margin: 0; }
.fga-btn--block { display: flex; width: 100%; }

/* Comparison columns: ruled rows, so the two sides can be read across as well as
   down. The rule under each item is what makes a pair of lists legible as a
   comparison — without it they are two stacks of text that happen to be adjacent. */
.fga-twocol__lead { font-weight: var(--weight-medium); }
.fga-twocol__list { list-style: none; display: grid; }
.fga-twocol__list > li {
  padding-block: var(--space-xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-twocol__list > li::before { content: none; }

/* ------------------------------------------------------------ mega menu */

/* Full-width panel under the header, opened by hover or keyboard focus. No
   JavaScript: the trigger stays a real link to /treatments, so a touch device
   gets the directory page — which is the same information, laid out for a phone
   — rather than a hover panel it has no way to dismiss. */
.fga-nav__group--mega { position: static; }
.fga-mega {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--bg-raised);
  border-top: var(--border-width-hairline) solid var(--rule);
  border-bottom: var(--border-width-hairline) solid var(--rule);
  box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; transform: translateY(var(--space-3xs));
  transition: opacity var(--dur-hover) var(--ease-reveal),
              transform var(--dur-hover) var(--ease-reveal),
              visibility var(--dur-hover) var(--ease-reveal);
}
.fga-nav__group--mega:hover .fga-mega,
.fga-nav__group--mega:focus-within .fga-mega { opacity: 1; visibility: visible; transform: none; }

/* THE HOVER BRIDGE, and why it is needed.

   The About dropdown is positioned against its own <li>, so it opens 1px ABOVE
   that link's bottom edge — the two boxes overlap and the pointer never leaves
   a hovered element. The mega panel is full-width, so it has to be positioned
   against the header instead, and the header centres its nav links inside a
   min-height. That leaves a 29px dead strip between the bottom of "Treatments"
   and the top of the panel: cross it and :hover is lost, the panel hides, then
   the pointer reaches the panel and it comes back. That is the flicker.

   The bridge is a transparent strip hanging below the link, so the pointer stays
   inside a hovered element the whole way down. It is attached to the LINK, not
   to the panel: a full-width bridge would mean hovering that strip anywhere
   along the header — under "About", under "Results" — opened the treatments
   panel. Constrained to the link's own width, it only catches a pointer that is
   genuinely on its way from Treatments to the panel.

   Height is --space-l (32px) against a 29px gap: the header height is a clamp,
   so the gap flexes, and a bridge slightly taller than the gap costs nothing
   while one slightly shorter reintroduces the bug at some viewport width. */
.fga-nav__group--mega .fga-nav__link { position: relative; }
.fga-nav__group--mega .fga-nav__link::after {
  content: "";
  position: absolute;
  top: 100%; left: 0; right: 0;
  height: var(--space-l);
}
/* The bridge must not sit over the panel and swallow its clicks, and it is only
   wanted while the group is being hovered. */
.fga-nav__group--mega:not(:hover) .fga-nav__link::after { display: none; }

/* Three packed category columns plus the exit. The categories are balanced by
   item count in megamenu.mjs, so a three-item category shares a column rather
   than sitting alone in a half-empty track. */
.fga-mega__inner {
  max-width: var(--container-wide); margin-inline: auto;
  /* Four EQUAL columns, matching the handover: three packed category columns
     plus the exit, every track the same width. The exit had a narrower track,
     which is what made the panel look hand-arranged rather than set on a grid.
     Measured off the handover — 4 x 1fr, 56px gutters, 36px panel padding —
     mapped to the nearest tokens (48 and 32), because inventing values to hit a
     prototype's exact pixels is how a token system stops being one. */
  padding: var(--space-l) var(--gutter);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-xl); align-items: start;
}
.fga-mega__col { display: grid; gap: var(--space-l); align-content: start; }

/* The category name is a label, not a heading. Mono, uppercase and small keeps
   the panel quiet: 22 treatment names is already a lot of type, and setting four
   category names in the display serif on top of them makes it shout. */
.fga-mega__cat {
  display: block;
  /* --font-body, not --font-mono. The handover prototype sets its labels in
     JetBrains Mono, and copying that treatment here was a mistake: this design
     system defines --font-mono as a bare SYSTEM stack with no webfont behind it,
     so the label rendered in SF Mono on a Mac and something else everywhere
     else — the one piece of type on the page nobody had chosen. The design
     system's eyebrow is --font-body, uppercase and tracked, which is what every
     other label on this site already uses. */
  font-family: var(--font-body); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  font-weight: var(--weight-medium);
  color: var(--text-muted-adaptive); border: 0;
  /* 8px above the rule, 16px below it — the handover's rhythm. Equal gaps both
     sides made the label float between the rule and its list rather than
     belonging to the list. */
  padding-bottom: var(--space-2xs); margin-bottom: var(--space-s);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
a.fga-mega__cat:hover { color: var(--text-accent); }
.fga-mega__list { list-style: none; display: grid; }
/* No rules between rows. The hairline under the category label is the only one
   the panel needs; ruling every row turns a menu into a table. */
/* DELIBERATE DEPARTURE FROM THE DESIGN SYSTEM, at the client's decision.
   SiteHeader.jsx:36 and TreatmentCard.jsx:25 both set a list of treatment names
   in the body face; this sets them in the display serif at --weight-display
   (300) instead. Recorded here rather than left to look like an oversight,
   because the next person to compare this file with the component sources will
   otherwise "correct" it back.

   Two things the serif needs that the body face did not. It goes up to
   --text-subheading, because Cormorant at body-small is too light to hold a
   column of 22 names. And the name gets min-width:0 so it can wrap while the
   price stays put — the price already carries white-space: nowrap, so a long
   treatment name pushes itself onto two lines rather than breaking its price. */
.fga-mega__list a {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-s);
  padding-block: var(--space-3xs);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  /* --text-body-sm (15px), against the handover's 14px. Cormorant's small
     x-height means it reads a shade smaller than a sans at the same value, so
     15 lands about where the handover's 14 does optically. 17px was tried and
     is too big: the columns are equal and therefore narrower, and at 17 the
     longer treatment names wrapped to two lines and pushed the panel from the
     handover's 539px to 625px. */
  font-size: var(--text-body-sm);
  line-height: var(--leading-body);
  color: var(--text-secondary-adaptive); border: 0;
}
.fga-mega__list a > span:first-child { min-width: 0; }
.fga-mega__list a:hover { color: var(--text-accent); }
.fga-mega__price {
  font-size: var(--text-body-sm);
  /* Same compounding as the card price above, and it went unnoticed longer
     because the panel is visibility:hidden until hover and the rendered contrast
     check skips hidden text. The check now reveals it; this mixes from the ink. */
  color: color-mix(in oklab, var(--fg-ink) 70%, transparent);
  white-space: nowrap;
}

/* The prescription-only flag is a chip, not a paragraph. Set as running text it
   read as an apology halfway down the column; as a tinted chip under the list it
   reads as a policy — which is what it is. */
.fga-mega__flag {
  display: block; margin: var(--space-s) 0 0;
  padding: var(--space-3xs) var(--space-2xs);
  background: var(--accent-soft); border-radius: var(--radius-xs);
  font-size: var(--text-caption); color: var(--text-primary);
}
.fga-mega__note {
  font-size: var(--text-body-sm); color: var(--text-secondary-adaptive);
  margin: 0 0 var(--space-s);
}
.fga-mega__col--exit { gap: 0; }

.fga-mega__foot {
  border-top: var(--border-width-hairline) solid var(--rule);
  padding: var(--space-m) var(--gutter);
  max-width: var(--container-wide); margin-inline: auto;
  display: flex; flex-wrap: wrap; gap: var(--space-m);
  align-items: center; justify-content: space-between;
  font-size: var(--text-body-sm);
}
.fga-mega__foot p { margin: 0; color: var(--text-muted-adaptive); max-width: 42ch; }
.fga-mega__footlinks { display: flex; gap: var(--space-l); }

/* Below the nav breakpoint the panel never opens — the drawer takes over. The
   number here MUST match the breakpoint at which .fga-nav is hidden and the
   drawer appears (940px). They were 1100 and 940, which left 941-1100px with a
   visible nav bar whose Treatments panel had been switched off and no drawer to
   replace it: on any laptop in that range the treatment menu simply did not
   exist. Between 940 and 1180 the panel drops to two columns rather than
   disappearing, because five tracks in 950px is not a menu. */
@media (max-width: 1180px) {
  .fga-mega__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-l); }
}
@media (max-width: 940px) { .fga-mega { display: none; } }

/* Drawer accordions — the phone's route into the treatment pages. */
.fga-menu__sub--groups { display: grid; gap: var(--space-3xs); }
.fga-menu__group > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs);
  padding-block: var(--space-2xs);
  font-size: var(--text-body-sm); color: var(--text-primary);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-menu__group > summary::-webkit-details-marker { display: none; }
.fga-menu__group > summary::after { content: "+"; color: var(--text-muted-adaptive); }
.fga-menu__group[open] > summary::after { content: "\2212"; }
.fga-menu__group .fga-menu__sub { padding: var(--space-2xs) 0 var(--space-xs) var(--space-s); }
.fga-menu__group .fga-menu__sub a { display: flex; justify-content: space-between; gap: var(--space-xs); }

/* Category cards on /treatments: the child treatments as ruled rows carrying
   their own price. Twenty-two cards would be a wall; twenty-two rows scan in
   seconds and give the page that many exact-match internal links.

   One column, overriding the flex-wrap chip row this list used to be. A wrapped
   row puts two treatments on a line with their prices between them, so the price
   stops belonging visibly to either. */
.fga-card__links { display: grid; grid-template-columns: 1fr; gap: 0; }
.fga-card__links > li { display: block; }
.fga-card__links > li > a {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-xs);
  padding-block: var(--space-2xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-card__linkprice {
  /* Mixed from --fg-ink, NOT from currentColor. The anchor around it already
     carries an adaptive mix, so --text-muted-adaptive would mix a mix: 68% of
     82% rendered every price at 2.73:1 against a 4.5:1 floor. A card paints its
     own light ground and sets its own ink, so mixing from that ink is both safe
     and honest about what this colour depends on. */
  color: color-mix(in oklab, var(--fg-ink) 70%, transparent);
  white-space: nowrap; font-size: var(--text-body-sm);
}

/* Header controls share one height. "Call" is a .fga-btn and "Menu" is a
   <summary> styled to look like one, so they inherit different padding scales
   and rendered at visibly different heights sitting next to each other on a
   phone. Matching the padding alone would fix it until either scale changed;
   a shared min-height means they agree regardless, and it is derived from the
   header height rather than typed, so it stays in proportion as that flexes. */
.fga-header__call,
.fga-menu__toggle {
  min-height: calc(var(--header-height) * 0.55);
  padding-block: var(--space-2xs);
}

/* ---------------------------------------------------------- price index */

/* /pricing is the longest page on the site and one of the highest-intent, and it
   dead-ended: someone who came for one number scrolled past every other one to
   find it. The index is generated from the groups themselves so it can neither
   list a section that is not there nor miss one that is. Sticky on desktop,
   where there is a margin to put it in; a plain block on a phone, where a
   sticky rail would eat a third of the screen. */
.fga-prices--indexed { display: grid; gap: var(--space-l); align-items: start; max-width: none; }
.fga-prices__groups { display: grid; gap: var(--space-xl); min-width: 0; max-width: var(--container); }
.fga-priceindex { display: grid; gap: var(--space-2xs); }
.fga-priceindex ul { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2xs) var(--space-m); }
.fga-priceindex a { font-size: var(--text-body-sm); }

@media (min-width: 60rem) {
  .fga-prices--indexed { grid-template-columns: minmax(0, 1fr) minmax(11rem, 0.34fr); }
  .fga-priceindex { grid-column: 2; grid-row: 1; position: sticky; top: calc(var(--header-height) + var(--space-m)); }
  .fga-priceindex ul { flex-direction: column; gap: var(--space-2xs); }
  .fga-prices__groups { grid-column: 1; grid-row: 1; }
}

/* Anchor targets clear the sticky header, or a jump lands with the heading
   hidden underneath it. */
.fga-pricegroup { scroll-margin-top: calc(var(--header-height) + var(--space-m)); }

/* A prescription-only row is tinted, not missing. Among priced rows a blank one
   reads as a price somebody forgot; tinted and carrying its reason it reads as
   a clinic declining to advertise a medicine, which is what it is. */
.fga-price-row--pom {
  background: var(--accent-soft);
  padding-inline: var(--space-xs);
  border-radius: var(--radius-xs);
}
.fga-price-row--pom .fga-price-row__dots { border-bottom-style: none; }

/* ------------------------------------- listings share one typographic voice */

/* The mega menu settled the treatment of a list of treatments: a small mono
   uppercase label over a hairline, quiet rows at body-small, and the figure in
   the display face with lining tabular numerals so a column of prices aligns on
   the digit. The price list and the category cards were each doing their own
   version of the same job — a display-serif heading over ruled rows in one, a
   serif heading over dotted leaders in the other — so the same information
   looked like three different components depending which page you were on.
   These rules pull them onto the mega menu's treatment. */

/* A section heading, in the display face — design-system/ui_kits/website/
   PriceListScreen.jsx sets exactly this element as an h4 in --font-display at
   --text-heading. I had demoted it to the eyebrow treatment to match the mega
   menu, but the mega menu's label is a span in a nav panel and this opens a
   section on the site's longest page. The mega menu's ROW treatment is what
   actually made that panel scan, and that is kept below. */
.fga-pricegroup > :is(h2, h3) {
  font-family: var(--font-display);
  font-weight: var(--weight-display-med);
  font-size: var(--text-heading);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-display);
  text-transform: none;
  padding-bottom: var(--space-2xs); margin-bottom: var(--space-2xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-price-row { padding-block: var(--space-2xs); }
.fga-price-row__label { font-size: var(--text-body-sm); }
.fga-price-row__value {
  font-family: var(--font-display);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: var(--tracking-display);
}

/* Category cards take the same ROW treatment as the mega menu. Their titles
   stay in the display face — an earlier version of this comment claimed they
   took the label voice too, which was never true, and made the price list's
   demoted heading look like a deliberate pair rather than an outlier. */
/* Matching the mega menu, per the same client decision noted above. */
.fga-card__links > li > a {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-subheading);
  line-height: var(--leading-heading);
}
.fga-card__links > li > a > span:first-child { min-width: 0; }
.fga-pricegroup__note {
  font-size: var(--text-caption); color: var(--text-muted-adaptive);
  margin: var(--space-xs) 0 0; max-width: var(--measure);
}

/* One ruled row of treatments under the homepage category cards — the only
   direct route from the homepage into the 22 pages that carry the commercial
   intent. Deliberately a row and not more cards: four cards plus eight cards is
   a wall, and this has to cost one line of vertical space to be worth adding. */
.fga-toprow {
  margin-top: var(--space-l); padding-top: var(--space-m);
  border-top: var(--border-width-hairline) solid var(--rule);
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--space-2xs) var(--space-m);
}
.fga-toprow__list {
  list-style: none; display: flex; flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-m); flex: 1 1 auto;
}
/* These links style themselves rather than relying on the prose-link rule —
   which does not cover this row, so they were rendering in the browser's default
   blue. Quiet by default and accent on hover, like the mega menu rows: this is a
   directory, and eight underlined links in a line reads as a paragraph of links. */
.fga-toprow__list a {
  font-size: var(--text-body-sm);
  color: var(--text-secondary-adaptive);
  border: 0; box-shadow: none; background-image: none;
}
.fga-toprow__list a:hover, .fga-toprow__list a:focus-visible { color: var(--text-accent); }
/* Spaced by gap alone. Middot separators were tried and removed: they need a
   negative margin to sit between items rather than after them, and the moment
   the row wraps that leaves a separator dangling at the start of the next line. */
.fga-toprow__all {
  font-size: var(--text-body-sm); white-space: nowrap;
  color: var(--text-accent); font-weight: var(--weight-medium);
  border: 0; box-shadow: inset 0 calc(-1 * var(--border-width-hairline)) 0 0 var(--border-accent);
}
.fga-toprow__all:hover { color: var(--accent-hover); }

/* Journey facts: label over value, ruled, across the top of a location page.
   The value takes the display face and lining tabular numerals so "15 minutes"
   reads as a figure rather than as more prose. */
.fga-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-m); margin-top: var(--space-l);
  border-top: var(--border-width-hairline) solid var(--rule);
  padding-top: var(--space-m);
}
.fga-facts dt {
  /* --font-body: see the note on .fga-mega__cat above. */
  font-family: var(--font-body); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  font-weight: var(--weight-medium);
  color: var(--text-muted-adaptive); margin-bottom: var(--space-3xs);
}
.fga-facts dd {
  margin: 0; 
  font-size: var(--text-subheading); line-height: var(--leading-heading);
}

/* ----------------------------------------------------- credential block */

/* The register data set as a document rather than a spec table. The inset
   hairline is the only decorative move on the site and it earns its place: it
   is what makes this read as a certificate instead of a list, which is the
   difference between a claim and an artefact. Rules between rows, never around
   cells — the design system is explicit that this is not a table. */
.fga-cred {
  border: var(--border-width-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xs);
  padding: var(--space-m);
  background: var(--bg-raised);
  display: grid; gap: 0;
}
.fga-cred::before {
  content: ""; display: block;
  border-top: var(--border-width-hairline) solid var(--border-subtle);
  margin: 0 0 var(--space-s);
}
.fga-cred__row {
  display: grid; grid-template-columns: minmax(7rem, 0.4fr) 1fr;
  gap: var(--space-s); align-items: baseline;
  padding-block: var(--space-2xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-cred__row:last-child { border-bottom: 0; }
/* MONO here, and this is a correction of an earlier correction. The typography
   audit moved every uppercase label to --font-body on the grounds that the design
   system's eyebrow is body-face — true of .fg-eyebrow, and NOT true of this
   component. CredentialBlock.jsx sets its row labels in --font-mono with
   --tracking-wide, because the monospace column is what makes the block read as
   a document rather than as a list. The component contract is more specific than
   the general eyebrow rule and wins. */
.fga-cred dt {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap; line-height: var(--leading-body);
}
/* Tabular figures: these rows carry dates — a prescriber-recorded date and a
   renewal date — and the design system's CredentialBlock sets them as figures so
   the two align on the digit rather than drifting past each other. */
.fga-cred dd {
  margin: 0; font-size: var(--text-body-sm);
  font-variant-numeric: lining-nums tabular-nums;
}
/* The tick means "the linked register shows this", so it appears only on rows
   the register actually evidences — never as decoration on the whole block. */
.fga-cred__tick { color: var(--text-accent); margin-left: var(--space-3xs); }
.fga-cred__row--check dd a { font-weight: var(--weight-medium); }

/* The editorial rail collapses inside the booking rail. .fga-rail splits a
   section into heading and body columns, which is right across a page and wrong
   inside a 420px sidebar: both halves end up around 180px and every line wraps
   three or four times. Nested inside the product rail it becomes one column. */
.fga-rail-product .fga-rail { grid-template-columns: 1fr; gap: var(--space-m); }
.fga-rail-product .fga-steps { max-width: none; }
.fga-rail-product .fga-steps__item { padding-left: var(--space-l); }

/* ------------------------------------------------------- concern router */

/* Nine complaints, each pointing at the treatments that answer it. Set as a
   grid of small blocks rather than a list, because the answer to a complaint is
   usually several treatments and one of them might be none — the shape has to
   let a row carry three or four destinations without looking like a mistake. */
.fga-concerns { margin-block: var(--space-l) var(--space-xl); }
.fga-concerns__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-l) var(--space-xl);
}
.fga-concern { display: grid; gap: var(--space-2xs); align-content: start; }
/* The complaint is set in the display face: it is the visitor's own words, and
   it should read as the thing being asked rather than as a form label. */
.fga-concern__says {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-subheading);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-display);
  margin: 0; text-wrap: balance;
  padding-bottom: var(--space-2xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-concern__to { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-3xs) var(--space-s); }
.fga-concern__to a {
  font-size: var(--text-body-sm); color: var(--text-secondary-adaptive);
  border: 0; box-shadow: none; background-image: none;
}
.fga-concern__to a:hover, .fga-concern__to a:focus-visible { color: var(--text-accent); }

/* ------------------------------------------------------- location pages */

/* Numbered route steps. This is the only genuinely unique content on the five
   town pages — the thing that stops a search engine reading them as doorway
   pages — so it is set to be scanned and quoted, not read as prose. */
.fga-route__steps { list-style: none; display: grid; counter-reset: fga-step; max-width: var(--measure); }
.fga-route__steps > li {
  counter-increment: fga-step; position: relative;
  padding-left: var(--space-xl); padding-block: var(--space-s);
  border-top: var(--border-width-hairline) solid var(--rule);
}
.fga-route__steps > li:last-child { border-bottom: var(--border-width-hairline) solid var(--rule); }
.fga-route__steps > li::before {
  content: counter(fga-step, decimal-leading-zero) / "";
  position: absolute; left: 0; top: var(--space-s);
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: var(--tracking-display);
  font-size: var(--text-subheading); line-height: 1.2;
  color: var(--text-accent); opacity: 0.42;
}

.fga-arearail { display: grid; gap: var(--space-m); }
.fga-arearail__map { aspect-ratio: 4 / 3; border-radius: var(--radius-xs); overflow: hidden; background: var(--bg-sunken); }
.fga-arearail__map iframe { width: 100%; height: 100%; border: 0; display: block; }

/* The map before it is asked for: a button in the frame the map will fill. */
.fga-mapfacade {
  height: 100%; display: grid; place-content: center; justify-items: center;
  gap: var(--space-s); padding: var(--space-m); text-align: center;
  background: var(--bg-sunken);
}
.fga-mapfacade__note { font-size: var(--text-body-sm); color: var(--text-secondary-adaptive); max-width: 30ch; }
.fga-arearail__address { display: grid; gap: var(--space-3xs); font-style: normal; font-size: var(--text-body-sm); }
.fga-arearail__actions { display: grid; gap: var(--space-xs); justify-items: start; }
.fga-arearail__popular { display: grid; gap: var(--space-2xs); padding-top: var(--space-m); border-top: var(--border-width-hairline) solid var(--rule); }
.fga-arearail__popular ul { list-style: none; display: grid; gap: var(--space-3xs); }
.fga-arearail__popular a { font-size: var(--text-body-sm); }

/* The location rail's treatment list — quiet, like the mega menu rows. */
.fga-arearail__popular a { color: var(--text-secondary-adaptive); border: 0; box-shadow: none; background-image: none; }
.fga-arearail__popular a:hover, .fga-arearail__popular a:focus-visible { color: var(--text-accent); }
.fga-arearail__address { color: var(--text-secondary-adaptive); }
.fga-route__note { font-size: var(--text-body-sm); margin-top: var(--space-s); max-width: var(--measure); }

/* Opening hours on /contact, from site.json. Day and time on one ruled line,
   so a week reads down the page like a timetable. */
.fga-hours { display: grid; gap: var(--space-2xs); }
.fga-hours__title { font-size: var(--text-subheading); }
.fga-hours__list { display: grid; max-width: var(--measure-narrow); }
.fga-hours__list > div {
  display: flex; justify-content: space-between; gap: var(--space-m);
  padding-block: var(--space-3xs);
  border-bottom: var(--border-width-hairline) solid var(--rule);
}
.fga-hours__list dt { font-weight: var(--weight-medium); }
.fga-hours__list dd { margin: 0; font-variant-numeric: tabular-nums; }

/* The /areas map: a static drawing, no tiles and no third-party request. SVG
   text is sized in the drawing's own units and scales with it, so the figure is
   capped at the prose measure, which keeps the labels near body-small size from
   a phone up. It paints its own ground, so its text colours are fixed rather
   than adaptive and hold their contrast on whichever band it lands on. */
.fga-drivemap { display: grid; gap: var(--space-xs); max-width: var(--measure); margin: 0; }
.fga-drivemap__svg {
  display: block; width: 100%; height: auto;
  background: var(--bg-raised);
  border: var(--border-width-hairline) solid var(--rule); border-radius: var(--radius);
}
.fga-drivemap__ring { fill: none; stroke: var(--border-strong); stroke-width: var(--border-width-hairline); stroke-dasharray: 2 5; }
.fga-drivemap__ringlabel { font-family: var(--font-body); font-size: var(--text-eyebrow); letter-spacing: var(--tracking-wide); fill: var(--text-muted); }
.fga-drivemap__line { stroke: var(--accent); stroke-opacity: 0.45; stroke-width: var(--border-width-strong); stroke-dasharray: 1 5; stroke-linecap: round; }
.fga-drivemap__town { fill: var(--accent); }
.fga-drivemap__label { font-family: var(--font-body); font-size: var(--text-caption); font-weight: var(--weight-semibold); fill: var(--text-primary); }
.fga-drivemap__time { font-weight: var(--weight-body); fill: var(--text-muted); }
.fga-drivemap__clinic { fill: var(--fg-butter-300); stroke: var(--text-primary); stroke-width: var(--border-width-strong); }
.fga-drivemap__clinicname { font-family: var(--font-body); font-size: var(--text-caption); font-weight: var(--weight-semibold); fill: var(--text-primary); }
.fga-drivemap__caption { font-size: var(--text-caption); color: var(--text-muted-adaptive); }
/* A halo in the map's own ground, so a dashed line passing behind a label never
   cuts through the letters. */
.fga-drivemap__label, .fga-drivemap__clinicname, .fga-drivemap__ringlabel {
  paint-order: stroke; stroke: var(--bg-raised); stroke-width: var(--border-width-loud); stroke-linejoin: round;
}

/* ------------------------------------------- credential block, per the DS */

/* Values from CredentialBlock.jsx: a card ground with an explicit colour reset —
   the component sets it itself so its currentColor-derived parts stay legible
   when it lands in an inverse section, which is exactly where design 1a puts it. */
.fga-cred { color: var(--text-primary); padding: var(--space-l); }
/* Scoped under .fga-cred, and that is load-bearing rather than tidy. This block
   lands inside the inverse band, where `.fga-band--inverse p` is a class and a
   type; a single class here loses to it and the title and the note both rendered
   ivory-on-ivory — invisible on the card's own ground. Two classes wins. The DS
   component sets a colour on every element it renders for the same reason. */
.fga-cred .fga-cred__title {
  margin: 0 0 var(--space-m);
  font-size: var(--text-eyebrow); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; font-weight: var(--weight-medium);
  color: var(--eyebrow-color);
}
.fga-cred__rows { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--space-s); margin: 0; }
.fga-cred__rows > .fga-cred__row { display: contents; }
.fga-cred__rows dt, .fga-cred__rows dd {
  padding: var(--space-xs) 0; border-top: var(--border-width-hairline) solid var(--border-hairline);
  border-bottom: 0;
}
.fga-cred__rows dd { margin: 0; font-size: var(--text-body-sm); color: var(--text-primary); text-wrap: pretty; line-height: var(--leading-body); }
/* The tick is drawn, and it is labelled. A bare tick states nothing; "Verified"
   next to a row says what has been checked, which is the component's whole job. */
.fga-cred__verified {
  display: inline-flex; align-items: center; gap: var(--space-3xs);
  margin-left: var(--space-xs);
  font-size: var(--text-eyebrow); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--fg-success-500); white-space: nowrap;
}
.fga-cred__tick {
  width: 8px; height: 4px;
  border-left: var(--border-width-strong) solid currentColor;
  border-bottom: var(--border-width-strong) solid currentColor;
  transform: rotate(-45deg) translateY(-1px);
}
.fga-cred .fga-cred__note { margin: var(--space-s) 0 0; font-size: var(--text-caption); color: var(--text-muted); }

/* ------------------------- design 1a: credentials beside the argument */

/* The defining shape of direction 1a. The claim and the document that backs it
   sit side by side, so a sceptical reader can check the second while reading the
   first. Single column below the split, with the document AFTER the argument —
   the claim has to be made before the evidence means anything. */
.fga-cred-split { display: grid; gap: var(--space-xl); align-items: start; }
@media (min-width: 60rem) {
  .fga-cred-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-2xl); }
}
.fga-cred-split__body { min-width: 0; }
.fga-sectionheading__title { font-size: var(--step-3); }
.fga-sectionheading__intro {
  margin-top: var(--space-m);
  font-size: var(--text-body-lg); line-height: var(--leading-body);
  color: var(--intro-color); max-width: var(--measure-prose);
}

/* Section eyebrow: the small label above a section heading, per the design
   system's SectionHeading. Sits tight to the heading it labels. */
.fga-section__eyebrow { margin: 0 0 var(--space-s); }

/* ------------------------------------------------- mobile booking bar */

/* Design 1a pins the primary action to the bottom of the screen on a phone.
   Phone only: on a laptop the header CTA never leaves the viewport, so a second
   permanent button solves a problem that does not exist there. */
.fga-bookbar { display: none; }
@media (max-width: 940px) {
  .fga-bookbar {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    padding: var(--space-xs) var(--gutter);
    padding-bottom: max(var(--space-xs), env(safe-area-inset-bottom));
    background: var(--glass-ivory);
    -webkit-backdrop-filter: var(--blur-glass);
    backdrop-filter: var(--blur-glass);
    border-top: var(--border-width-hairline) solid var(--rule);
  }
  /* Room for it, or it covers the last thing on the page — usually the footer's
     final link, which is exactly the sort of thing a fixed bar quietly eats. */
  .fga-footer { padding-bottom: calc(var(--space-9) + var(--space-2xl)); }
}

/* --------------------------------- mobile: the shorter credential block */

/* 1a shows three rows on a phone, not six. The rows it drops are the two dates —
   the prescriber and renewal entries — because on a small screen the question is
   "is she registered and can I check", and the dates answer neither. The verify
   row always survives: it is the reason the block exists. */
@media (max-width: 640px) {
  .fga-cred__row:nth-of-type(n+4):not(.fga-cred__row--check) { display: none; }
}

/* ------------------------------- mobile: treatments as a list, not cards */

/* On a phone 1a lists the treatment families as rows — name and price, ruled —
   rather than as four stacked cards. Four cards is four screens of scrolling to
   learn four names and four prices. */
@media (max-width: 640px) {
  /* The grid's own gap goes too: these are ruled rows now, and the rule is the
     separator. A 32px gap on top of it leaves each row floating. */
  .fga-cards--home-categories { gap: 0; }
  .fga-cards--home-categories .fga-card {
    /* flex-direction: row is REQUIRED, not tidying — .fga-card is a column flex
       by default, so without it the price dropped onto its own line under the
       name and each "row" was two. */
    display: flex; flex-direction: row; flex-wrap: nowrap;
    align-items: baseline; justify-content: space-between;
    gap: var(--space-s);
    padding: var(--space-s) 0;
    background: none; border: 0; border-bottom: var(--border-width-hairline) solid var(--rule);
    border-radius: 0; box-shadow: none;
  }
  .fga-cards--home-categories .fga-card p { display: none; }
  .fga-cards--home-categories .fga-card__top { display: contents; }
  .fga-cards--home-categories .fga-card :is(h2, h3) { font-size: var(--text-body); margin: 0; }
  /* The foot stays, minus its arrow. On the prescription-only row it carries
     "Consultation required", which is the only thing standing in for a price —
     hiding the whole foot left that row blank, which reads as an omission. */
  .fga-cards--home-categories .fga-card__foot {
    margin: 0; padding: 0; border: 0; flex: 0 0 auto;
    font-size: var(--text-caption); color: var(--text-muted-adaptive); white-space: nowrap;
  }
  .fga-cards--home-categories .fga-card__more { display: none; }
}

/* --------------------------- mobile: what the booking bar makes redundant */

/* With the action pinned to the bottom of the screen, the hero's own primary
   button is the same green button twice in one viewport — 1a's mobile hero
   carries no CTA at all for exactly this reason. The secondary link stays: it is
   a different destination, and it is not duplicated anywhere on the screen. */
@media (max-width: 940px) {
  .fga-hero__actions .fga-btn { display: none; }
  .fga-hero__actions { margin-top: var(--space-s); }
}

/* The popular-treatments row sits under a list that already rules its last item,
   so its own top rule made an empty band between the two. */
@media (max-width: 640px) {
  .fga-toprow { border-top: 0; margin-top: var(--space-s); padding-top: 0; }
}
