/* ==========================================================================
   London Water Treatment. Site stylesheet.
   Single stylesheet for every page. No external requests, no web fonts.
   Mobile first. All colours meet at least 4.5:1 against their background.
   Sections:
     01 Custom properties
     02 Reset and base
     03 Typography
     04 Layout: container, section, grid
     05 Skip link and focus
     06 Header and navigation
     07 Buttons
     08 Hero
     09 Breadcrumbs
     10 Cards and card grid
     11 Feature list with tick markers
     12 Stat row
     13 Testimonials
     14 FAQ accordion
     15 CTA band
     16 Forms
     17 Prose, tables, blockquote, answer blocks
     18 Footer
     19 Utilities
     20 Reduced motion
   ========================================================================== */

/* Self hosted typefaces. Same origin, so no external request, and the site still
   works fully offline. Both are SIL Open Font Licence 1.1, latin subset, which
   permits self hosting. Figtree is a geometric humanist sans chosen to sit beside
   the logo wordmark, which is a rounded geometric lowercase. IBM Plex Mono gives
   the standards designations and figures their own voice.
   Available weights are 400, 500, 600 and 800 only. Do not ask for 700, the
   browser will synthesise it and it looks wrong next to the logo.
   ========================================================================== */

@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-800.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/assets/fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}

/* 01 Custom properties
   ========================================================================== */

:root {
  /* Brand, sampled from the client logo. The previous navy #16408C was a purple
     leaning indigo that the logo does not contain. The real navy has no red in it
     at all, and the real support colour is a vivid cyan, not a muted teal. */
  --color-primary: #004878;        /* logo navy, 9.55:1 on white */
  --color-primary-dark: #003A61;   /* hover and active, 11.81:1 */
  --color-primary-deep: #002A47;   /* dark bands and footer, 14.77:1 */

  /* The logo cyan. 2.52:1 on white, so it can NEVER carry text on a light ground.
     It is a graphic colour: rules, borders, markers, large display, dark band
     accents. Used exactly the way the logo uses it. */
  --color-cyan: #20B0E0;
  --color-cyan-deep: #0E86B4;      /* for gradients and hovers on dark */

  /* Text safe cyan. Reaches 4.5:1 on white, on the tint AND on the tint-strong
     fill, which is where links inside answer blocks sit. */
  --color-accent: #096D8F;         /* 5.84:1 white, 4.79:1 on tint-strong */
  --color-accent-dark: #075267;

  /* Neutrals */
  --color-ink: #12212E;            /* body text, 16.37:1 on white */
  --color-ink-muted: #425768;      /* secondary text, 7.51:1 on white */
  --color-surface: #FFFFFF;
  /* Tints re-based onto the cyan leaning navy, so the neutrals belong to the
     brand hue rather than sitting a few degrees off it. */
  --color-tint: #EFF6FA;           /* light section background */
  --color-tint-strong: #DCEBF4;    /* card and callout fill */
  --color-line: #C8DDE9;
  --color-line-strong: #9FC2D5;
  /* Form control borders. WCAG 1.4.11 wants 3:1 for the boundary of an input,
     and --color-line only reaches 1.75:1, so the fields read as ghosts. */
  --color-line-field: #5F8399;

  /* On dark backgrounds */
  --color-on-dark: #FFFFFF;             /* 14.77:1 on --color-primary-deep */
  --color-on-dark-muted: #B8DCEF;       /* 10.21:1 on --color-primary-deep */
  --color-on-dark-line: rgba(255, 255, 255, 0.22);

  /* Feedback */
  --color-required: #B33A2B;       /* 5.9:1 on white */
  --color-focus: #096D8F;
  --color-focus-dark: #7FD3F0;

  /* Type. System stack only, no web fonts. */
  --font-display: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Arial, sans-serif;
  --font-sans: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
    monospace;

  /* Fluid type scale */
  /* Floors raised on the two smallest steps, because substantive copy sits on
     them: the hero proof points, card bullets and the standards captions. */
  --fs-xs: clamp(0.875rem, 0.86rem + 0.10vw, 0.9375rem);
  --fs-sm: clamp(0.9375rem, 0.90rem + 0.17vw, 1rem);
  --fs-base: clamp(1rem, 0.96rem + 0.20vw, 1.0625rem);
  --fs-md: clamp(1.0625rem, 1.01rem + 0.28vw, 1.1875rem);
  --fs-lg: clamp(1.1875rem, 1.10rem + 0.42vw, 1.375rem);
  /* Top of the scale pulled in. 46px/35px headings in a system stack read as a
     starter kit and shout at an audience the brief wants reassured. The steps
     below are widened instead, so rank is carried by spacing, not shouting. */
  --fs-xl: clamp(1.25rem, 1.19rem + 0.30vw, 1.5rem);
  --fs-2xl: clamp(1.5rem, 1.34rem + 0.75vw, 2rem);
  --fs-3xl: clamp(1.875rem, 1.6rem + 1.1vw, 2.5rem);

  --leading-tight: 1.18;
  --leading-snug: 1.3;
  --leading-normal: 1.65;
  /* Interface text: cards, tables, captions, footer. One leading cannot serve a
     704px prose column, a 245px card column and a headline alike. */
  --leading-ui: 1.45;
  /* 56ch measures about 70 characters in this stack. 68ch measured 85, which is
     well past the readable range on pages of 2,000 to 2,900 words. */
  --measure: 56ch;
  /* Paragraph rhythm inside .prose, slightly looser than --space-sm. */
  --space-flow: 1.375rem;

  /* Spacing scale */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 2rem;
  --space-xl: 3rem;
  --space-2xl: clamp(3rem, 6vw, 5rem);
  --space-3xl: clamp(4rem, 9vw, 7rem);

  --section-pad: clamp(2.5rem, 5.5vw, 4.5rem);

  /* Shape. Tight radii and no card shadow. Soft 14px corners with a drop shadow
     on every panel is page builder chrome, and this audience reads engineering
     documents. --shadow is kept, but only the mobile nav panel uses it, because
     that one has to lift off the page behind it. */
  --radius-sm: 2px;
  --radius: 3px;
  --radius-lg: 3px;
  --border: 1px solid var(--color-line);
  --shadow-sm: none;
  --shadow: 0 2px 4px rgba(10, 33, 73, 0.06),
    0 8px 20px rgba(10, 33, 73, 0.08);

  /* Layout */
  --container: 72rem;
  --container-narrow: 48rem;
  --container-wide: 82rem;
  --header-height: 4.5rem;   /* phone: 48px logo plus padding */

  /* Motion */
  --transition: 160ms ease;
}

/* 02 Reset and base
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + 1rem);
}

body {
  margin: 0;
  background-color: var(--color-surface);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--leading-normal);
}

/* Kill the single word last lines that are most of the visual noise on pages
   that are nothing but paragraphs. Degrades to normal wrapping if unsupported. */
p,
li {
  text-wrap: pretty;
}

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

/* A pale placeholder while a photograph loads. Transparent artwork must opt out,
   or the tint paints a visible box behind the logo and the icons. */
img {
  background-color: var(--color-tint);
}

img[src$=".png"],
img[src$=".svg"] {
  background-color: transparent;
}

hr {
  border: 0;
  border-top: var(--border);
  margin: var(--space-lg) 0;
}

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

a {
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 1px;
}

a:hover {
  color: var(--color-primary-dark);
  text-decoration-thickness: 2px;
}

strong,
b {
  font-weight: 600;
}

small {
  font-size: var(--fs-sm);
}

code,
kbd,
samp,
pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

/* 03 Typography
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 var(--space-sm);
  color: var(--color-primary-deep);
  font-weight: 600;
  line-height: var(--leading-tight);
  text-wrap: balance;
}

/* Leading tightens as size grows, so a 40px headline and a 19px subhead do not
   share one ratio. Four clearly separated steps: 40 / 32 / 24 / 19 at the top
   of the scale. Weight stays 700 throughout on purpose, because the Android
   system stack resolves to Roboto, which has no 600 and snaps up to 700, so a
   weight tier would silently vanish on much of the mobile traffic. */
h1 {
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.028em;
}

h2 {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -0.022em;
}

h3 {
  font-size: var(--fs-xl);
  line-height: 1.24;
  letter-spacing: -0.01em;
}

h4 {
  font-size: var(--fs-lg);
  line-height: 1.3;
}

h5,
h6 {
  font-size: var(--fs-md);
  line-height: 1.3;
}

p {
  margin: 0 0 var(--space-sm);
  max-width: var(--measure);
}

.lede {
  font-size: var(--fs-md);
  color: var(--color-ink-muted);
  max-width: 52ch;
}

/* Labels, standards designations and figures are reference data, not prose, so
   they get their own voice in IBM Plex Mono. That split, prose in Figtree and
   data in mono, is the spine of the visual identity: it makes the page read as
   instrumented rather than marketed, which is what this audience is buying. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* A short cyan rule leads the label, drawn from the logo's own colour. */
.eyebrow::before {
  content: "";
  flex: 0 0 auto;
  width: 1.75rem;
  height: 2px;
  background-color: var(--color-cyan);
}

.section--navy .eyebrow,
.cta-band .eyebrow,
.hero--dark .eyebrow {
  color: var(--color-cyan);
}

/* 04 Layout
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2rem);
}

/* One left rail for the whole site. Narrow and wide sections used to centre
   themselves at a different width from the standard container, so the content
   edge jumped between 176px, 369px and 97px at 1440 wide, up to eight times on
   a single page. Now every section shares the container's left edge and narrow
   sections simply stop sooner on the right. 44rem is 704px, which is exactly
   the width narrow content already rendered at, so the measure is unchanged
   and only the horizontal position moves. */
.container--narrow {
  max-width: var(--container);
}

.container--narrow > * {
  max-width: 44rem;
}

/* Re-assert the reading measure. The rail cap above outranks the global `p`
   rule on specificity, so without this, narrow sections went back to 82
   characters per line. Blocks that carry their own width (.answer, .prose,
   .table-wrap) are declared later and keep theirs. */
.container--narrow > p,
.container--narrow > ul,
.container--narrow > ol {
  max-width: var(--measure);
}

.container--wide {
  max-width: var(--container);
}

/* A container nested inside a container must not re-pad or re-centre. */
.container .container {
  max-width: 44rem;
  margin-inline: 0;
  padding-inline: 0;
}

.section {
  padding-block: var(--section-pad);
}

.section--tight {
  padding-block: clamp(1.75rem, 3.5vw, 2.75rem);
}

.section--tint {
  background-color: var(--color-tint);
}

.section--navy {
  background-color: var(--color-primary-deep);
  color: var(--color-on-dark);
}

.section--navy h1,
.section--navy h2,
.section--navy h3,
.section--navy h4 {
  color: var(--color-on-dark);
}

.section--navy p,
.section--navy li {
  color: var(--color-on-dark-muted);
}

.section--navy a,
.cta-band a,
.hero--dark a:not(.btn) {
  color: var(--color-on-dark);
}

.section--navy a:hover,
.cta-band a:hover,
.hero--dark a:not(.btn):hover {
  color: var(--color-on-dark);
  text-decoration-thickness: 2px;
}

.section--navy .eyebrow {
  color: var(--color-on-dark-muted);
}

/* Every section opens with the brand gradient as a short rule. Repeated across
   35 pages it becomes the thing the site is recognised by, and it costs nothing
   on the pages that have no photograph. */
.section__header {
  margin-bottom: var(--space-lg);
}

.section__title {
  position: relative;
  padding-top: var(--space-sm);
}

.section__title::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 3.5rem;
  height: 3px;
  background-image: linear-gradient(90deg, var(--color-primary), var(--color-cyan));
}

.section--navy .section__title::before,
.cta-band .section__title::before {
  background-image: linear-gradient(90deg, var(--color-cyan), var(--color-on-dark));
}

.section__title {
  margin-bottom: var(--space-2xs);
}

.section__lede {
  font-size: var(--fs-md);
  color: var(--color-ink-muted);
  max-width: 52ch;
  margin-bottom: 0;
}

.grid {
  display: grid;
  gap: var(--space-md);
}

@media (min-width: 40em) {
  .grid--2,
  .grid--3,
  .grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 60em) {
  .grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .grid--4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .grid--sidebar {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--space-xl);
  }
}

/* 05 Skip link and focus
   ========================================================================== */

.skip-link {
  position: fixed;
  left: var(--space-sm);
  top: var(--space-sm);
  z-index: 100;
  transform: translateY(-200%);
  display: inline-block;
  padding: 0.75rem 1.25rem;
  background-color: var(--color-primary-deep);
  color: var(--color-on-dark);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius);
  transition: transform var(--transition);
}

.skip-link:focus {
  transform: translateY(0);
  color: var(--color-on-dark);
}

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

a:focus-visible {
  border-radius: 2px;
}

.section--navy :focus-visible,
.cta-band :focus-visible,
.site-footer :focus-visible,
.hero--dark :focus-visible {
  outline-color: var(--color-focus-dark);
}

/* Focus target for the skip link. */
#main:focus {
  outline: none;
}

/* 06 Header and navigation
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background-color: var(--color-surface);
  border-bottom: var(--border);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  min-height: var(--header-height);
  padding-block: var(--space-2xs);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--color-primary-deep);
  font-weight: 600;
  font-size: var(--fs-md);
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-decoration: none;
}

.brand:hover {
  color: var(--color-primary);
}

/* The supplied logo is a horizontal lockup: droplet, "london", then "WATER
   TREATMENT" as a strapline. It carries the company name itself, so the text
   spans are removed from the document rather than visually hidden, otherwise a
   screen reader hears the name twice, once from the alt and once from the span.
   The img alt is what carries it. */
/* Sized so the strapline inside the lockup stays legible. Smaller on a phone,
   where header height is expensive. The lockup is 146px wide at desktop against
   the 220px the text wordmark used, so the navigation gains room rather than
   losing it. */
.brand__logo {
  width: auto;
  height: 3rem;
}

@media (min-width: 74em) {
  .brand__logo {
    height: 4.25rem;
  }
}

.brand__name,
.brand__tag {
  display: none;
}

.brand__tag {
  display: none;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--color-ink-muted);
  letter-spacing: 0.02em;
}

/* The old text strapline is retired: the supplied lockup carries it. Left in the
   markup rather than deleted from 36 files, hidden here in one place. */

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: 0.6rem 0.85rem;
  min-height: 44px;
  background-color: var(--color-surface);
  color: var(--color-primary-deep);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
}

.nav-toggle:hover {
  background-color: var(--color-tint);
}

.nav-toggle__bars {
  display: block;
  position: relative;
  width: 20px;
  height: 2px;
  background-color: currentColor;
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 20px;
  height: 2px;
  background-color: currentColor;
}

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

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

.nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background-color: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before {
  top: 0;
  transform: rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  top: 0;
  transform: rotate(-45deg);
}

/* Mobile: the panel is closed until the toggle opens it. The no-script
   override in the page head keeps the navigation visible when JS is off. */
.site-nav {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background-color: var(--color-surface);
  border-bottom: var(--border);
  box-shadow: var(--shadow);
  padding: var(--space-sm) clamp(1rem, 4vw, 2rem) var(--space-md);
}

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

/* The phone call to action is the last item in the panel, so it is the first
   thing lost when the panel runs past the viewport, which is exactly what
   happens at large text sizes or in landscape. Scroll the panel instead. */
@media (max-width: 56.24em) {
  .site-nav.is-open {
    max-height: calc(100dvh - var(--header-height));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-list__item {
  border-bottom: var(--border);
}

.nav-list__item:last-child {
  border-bottom: 0;
}

.nav-list__link {
  display: block;
  padding: 0.7rem 0;
  color: var(--color-primary-deep);
  font-weight: 600;
  text-decoration: none;
}

.nav-list__link:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

.nav-list__link[aria-current="page"] {
  color: var(--color-primary);
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.site-nav__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin-top: var(--space-sm);
}

/* Desktop navigation. The breakpoint is set by the width the eight nav items
   plus the phone call to action actually need, not by a device size.
   From 56.25em (900px) the hamburger is gone and the full navigation shows.
   Between 56.25em and 74em the header uses two rows, because the wordmark,
   the eight links and the phone button need about 1030px on a single row and
   only 836px is available at 900px. From 74em up it is one row, as before. */
@media (min-width: 56.25em) {
  .site-header__inner {
    gap: var(--space-md);
  }

  .nav-toggle {
    display: none;
  }

  .site-nav {
    display: flex;
    position: static;
    align-items: center;
    gap: var(--space-md);
    padding: 0;
    border: 0;
    box-shadow: none;
    background: none;
  }

  .nav-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem clamp(0.7rem, 1.15vw, 0.875rem);
  }

  .nav-list__item {
    border-bottom: 0;
  }

  .nav-list__link {
    padding: 0.35rem 0;
    font-size: 0.875rem;
    white-space: nowrap;
  }

  .site-nav__cta {
    margin-top: 0;
  }

  .site-nav__cta .btn {
    padding-inline: 1rem;
    font-size: var(--fs-xs);
  }
}

@media (min-width: 74em) {
  :root {
    --header-height: 5.5rem;   /* one row, 68px logo plus padding */
  }
}

/* Two row header, 900px to 1183px. The brand takes the first row, the
   navigation and the phone button take the full container width on the second.
   This is what lets the full navigation show at 900px instead of a hamburger.
   The taller header is fed back into --header-height so the jump links on the
   pillar pages still scroll their target clear of the sticky header. */
@media (min-width: 56.25em) and (max-width: 73.99em) {
  :root {
    --header-height: 7rem;     /* two rows, compact lockup */
  }

  .site-header__inner {
    flex-wrap: wrap;
    row-gap: var(--space-2xs);
  }

  .site-nav {
    flex-basis: 100%;
    justify-content: space-between;
  }
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: 44px;
  padding: 0.7rem 1.35rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition),
    color var(--transition);
}

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

.btn--primary:hover {
  background-color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-on-dark);
  text-decoration: none;
}

.btn--secondary {
  background-color: var(--color-surface);
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.btn--secondary:hover {
  background-color: var(--color-tint-strong);
  color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  text-decoration: none;
}

.btn--ghost {
  background-color: transparent;
  border-color: var(--color-on-dark);
  color: var(--color-on-dark);
}

.btn--ghost:hover {
  background-color: rgba(255, 255, 255, 0.12);
  color: var(--color-on-dark);
  text-decoration: none;
}

.btn--accent {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-dark);
}

.btn--accent:hover {
  background-color: var(--color-accent-dark);
  border-color: var(--color-accent-dark);
  color: var(--color-on-dark);
  text-decoration: none;
}

.btn--lg {
  font-size: var(--fs-base);
  padding: 0.9rem 1.75rem;
}

.btn--block {
  width: 100%;
}

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

/* 146 inline tel: links across the site looked like any other link. */
a[href^="tel:"]:not(.btn) {
  font-weight: 600;
  white-space: nowrap;
}

/* Phone call to action. Number stays readable and selectable. */
.btn--phone {
  white-space: nowrap;
  letter-spacing: 0.01em;
}

/* 08 Hero
   ========================================================================== */

.hero {
  background-color: var(--color-tint);
  border-bottom: var(--border);
  padding-block: clamp(2.25rem, 5vw, 4rem);
}

/* The logo's own device is a navy to cyan gradient, so the dark bands use it
   rather than sitting as flat navy. The droplet is watermarked at low opacity
   from the same artwork, bled off the right edge, and a cyan rule closes the
   band. All same origin, no external request. */
.hero--dark {
  position: relative;
  isolation: isolate;
  background-color: var(--color-primary-deep);
  background-image:
    radial-gradient(120% 140% at 88% 8%, rgba(32, 176, 224, 0.30), transparent 62%),
    linear-gradient(160deg, var(--color-primary) 0%, var(--color-primary-deep) 58%);
  border-bottom: 0;
  box-shadow: inset 0 -3px 0 0 var(--color-cyan);
  color: var(--color-on-dark);
  /* The droplet watermark is positioned at right: -6rem so it bleeds off the
     edge. Without this it creates 96px of horizontal page overflow, which is
     invisible when inspecting elements because the culprit is a pseudo element. */
  overflow: hidden;
}

.hero--dark::before {
  content: "";
  position: absolute;
  z-index: -1;
  right: -6rem;
  top: 50%;
  transform: translateY(-50%);
  width: min(34rem, 55%);
  aspect-ratio: 560 / 882;
  background: url("/assets/img/droplet-white.png") no-repeat center / contain;
  opacity: 0.07;
  pointer-events: none;
}

.hero--dark .hero__title,
.hero--dark h1,
.hero--dark h2 {
  color: var(--color-on-dark);
}

/* The teal accent is only legible on a light ground. On the dark hero it drops
   to 2.74:1, so the eyebrow takes the same colour it takes on a navy band. */
.hero--dark .eyebrow {
  color: var(--color-on-dark-muted);
}

.hero--dark .hero__lede {
  color: var(--color-on-dark-muted);
}

.hero--dark .hero__points li {
  color: var(--color-on-dark-muted);
}

/* The tick is drawn with borders, so setting `color` here did nothing and the
   markers sat at about 2.3:1 on the navy. */
.hero--dark .hero__points li::before {
  border-color: var(--color-focus-dark);
}

/* On the navy hero the primary button was --color-primary on
   --color-primary-deep, which is 1.63:1, so it had no shape and the ghost
   button beside it out-popped the phone number. Every CTA band on the site
   already does the opposite, phone as the white solid, so this brings the one
   screen every visitor sees into line with the site's own pattern. */
.hero--dark .btn--primary {
  background-color: var(--color-surface);
  border-color: var(--color-surface);
  color: var(--color-primary-deep);
}

.hero--dark .btn--primary:hover {
  background-color: var(--color-tint-strong);
  border-color: var(--color-tint-strong);
  color: var(--color-primary-dark);
}

.hero__inner {
  display: grid;
  gap: var(--space-lg);
}

@media (min-width: 62em) {
  .hero__inner--split {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    align-items: center;
    gap: var(--space-xl);
  }
}

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

.hero__lede {
  font-size: var(--fs-md);
  color: var(--color-ink-muted);
  max-width: 52ch;
  margin-bottom: var(--space-md);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin-bottom: var(--space-md);
}

/* 12 of the 13 hero pages name a standard or the UKAS laboratory here, so this
   strip carries the most verifiable claims on the page. It was set two steps
   below the generic marketing sentence above it, in muted grey, which read as
   trailing small print. Now body scale, full ink, and ruled off as a distinct
   block of evidence. */
.hero__points {
  list-style: none;
  /* Without a cap this list runs the full container on the 13 heroes that have
     no image beside them, which measured 127 characters a line. */
  max-width: var(--measure);
  margin: 0;
  padding: var(--space-md) 0 0;
  border-top: 1px solid var(--color-line);
  display: grid;
  gap: var(--space-2xs);
  font-size: var(--fs-base);
}

.hero__points li {
  position: relative;
  padding-left: 1.6rem;
  color: var(--color-ink);
}

.hero--dark .hero__points {
  border-top-color: var(--color-on-dark-line);
}

.hero--dark .hero__points li {
  color: var(--color-on-dark);
}

.hero__points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 0.7rem;
  height: 0.38rem;
  border-left: 2px solid var(--color-cyan);
  border-bottom: 2px solid var(--color-cyan);
  transform: rotate(-45deg);
}

.hero__media img {
  border-radius: var(--radius-lg);
}

/* Below the split breakpoint the hero photo sits above the copy at its full 4:3
   height, which pushed the first section heading past 1000px on the home page.
   Cropping it to a band keeps the opening content in reach on a phone. */
@media (max-width: 61.99em) {
  .hero__media img {
    aspect-ratio: 16 / 7;
    object-fit: cover;
  }
}

/* 09 Breadcrumbs
   ========================================================================== */

.breadcrumbs {
  background-color: var(--color-surface);
  border-bottom: var(--border);
  padding-block: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.02em;
  line-height: var(--leading-ui);
}

.card__text,
.table th,
.table td,
.form__hint,
.form__note,
.site-footer {
  line-height: var(--leading-ui);
}

.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  color: var(--color-ink-muted);
}

.breadcrumbs__item {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.breadcrumbs__item + .breadcrumbs__item::before {
  content: "/";
  color: var(--color-line-strong);
}

.breadcrumbs__link {
  color: var(--color-primary);
}

.breadcrumbs__item[aria-current="page"],
.breadcrumbs__current {
  color: var(--color-ink-muted);
}

/* 10 Cards and card grid
   ========================================================================== */

.card-grid {
  display: grid;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 40em) {
  .card-grid--2,
  .card-grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 60em) {
  .card-grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.card {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--space-md);
  background-color: var(--color-surface);
  /* Stronger than --border, because with the shadow gone this line is the only
     thing separating a card from a tint band, where --color-line is 1.35:1. */
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.section--tint .card {
  box-shadow: none;
}

.card--service {
  border-top: 3px solid transparent;
  border-image: linear-gradient(90deg, var(--color-primary), var(--color-cyan)) 1;
}

.card--flat {
  box-shadow: none;
}

.card__title {
  font-size: var(--fs-md);
  line-height: 1.3;
  margin-bottom: var(--space-2xs);
}

.card__title a {
  text-decoration: none;
}

.card__title a:hover {
  text-decoration: underline;
}

/* Figures line up in columns wherever they appear. */
.table,
.stat__figure,
.btn--phone,
.nap__value,
.footer-nap {
  font-variant-numeric: tabular-nums;
}

.card__meta {
  display: block;
  margin-bottom: var(--space-2xs);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.card__text {
  color: var(--color-ink-muted);
  margin-bottom: var(--space-sm);
  max-width: none;
}

.card__list {
  margin: 0 0 var(--space-sm);
  padding-left: 1.1rem;
  color: var(--color-ink-muted);
  font-size: var(--fs-base);
  line-height: var(--leading-ui);
}

.card__list li {
  margin-bottom: 0.2rem;
}

.card__link {
  margin-top: auto;
  font-weight: 600;
  align-self: flex-start;
  /* 23 of these on the blog index alone, previously 28px tall. Padding rather
     than a flex box: flex makes the ::after chevron its own flex item and
     strands it at the far edge as soon as the label wraps to two lines. */
  display: inline-block;
  padding-block: 0.5rem;
}

.card__link::after {
  content: " \203A";
  text-decoration: none;
}

/* Compact link list, used for cluster links and related posts. */
.link-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2xs);
}

.link-list li {
  padding-left: 1.1rem;
  position: relative;
}

.link-list li::before {
  content: "\203A";
  position: absolute;
  left: 0;
  color: var(--color-accent);
  font-weight: 600;
}

/* Jump links for long pillar pages. */
.jump-links {
  padding: var(--space-md);
  background-color: var(--color-tint);
  border: var(--border);
  border-radius: var(--radius-lg);
}

.jump-links__title {
  font-size: var(--fs-md);
  margin-bottom: var(--space-2xs);
}

.jump-links ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  font-size: var(--fs-sm);
}

/* 11 Feature list with tick markers
   ========================================================================== */

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

.tick-list--2col {
  gap: var(--space-2xs) var(--space-md);
}

@media (min-width: 46em) {
  .tick-list--2col {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.tick-list li {
  position: relative;
  padding-left: 1.85rem;
  max-width: var(--measure);
}

.tick-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 0.78rem;
  height: 0.42rem;
  border-left: 2.5px solid var(--color-accent);
  border-bottom: 2.5px solid var(--color-accent);
  transform: rotate(-45deg);
}

.tick-list--strong li::before {
  border-color: var(--color-primary);
}

.section--navy .tick-list li::before {
  border-color: var(--color-focus-dark);
}

.tick-list__title {
  display: block;
  font-weight: 600;
  color: var(--color-primary-deep);
}

.section--navy .tick-list__title {
  color: var(--color-on-dark);
}

/* Numbered process steps. */
.steps {
  list-style: none;
  counter-reset: step;
  margin: 0 0 var(--space-md);
  padding: 0;
  display: grid;
  gap: var(--space-sm);
}

.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 2.75rem;
  max-width: var(--measure);
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background-image: linear-gradient(140deg, var(--color-primary), var(--color-cyan));
  color: var(--color-on-dark);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: 1;
}

.steps__title {
  display: block;
  font-weight: 600;
  color: var(--color-primary-deep);
}

/* 12 Stat row
   ========================================================================== */

.stat-row {
  display: grid;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 40em) {
  .stat-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 60em) {
  .stat-row--4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .stat-row--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* 39 of the 42 tiles hold a standard's name, not a figure, yet they were styled
   as badge shaped hype metrics: 35px bold navy over a 15px muted caption. That
   hierarchy asserts an accreditation the copy is careful not to claim. The name
   comes down, the qualifying sentence comes up to body scale and full ink. */
.stat {
  padding: var(--space-md);
  background-color: var(--color-surface);
  border: 1px solid var(--color-line);
  border-top: 3px solid var(--color-primary);
  border-radius: var(--radius);
  text-align: left;
}

.section--navy .stat,
.hero--dark .stat {
  border-color: var(--color-on-dark-line);
  border-top-color: var(--color-focus-dark);
}

.section--navy .stat,
.hero--dark .stat {
  background-color: rgba(255, 255, 255, 0.10);
  border: 1px solid var(--color-on-dark-line);
}

/* 39 of the 42 tiles hold a standard's designation, not a metric, so they are
   set as designations: mono, tighter, at a size that informs rather than shouts. */
.stat__figure {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  font-weight: 500;
  line-height: 1.2;
  color: var(--color-primary);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}

.section--navy .stat__figure,
.hero--dark .stat__figure {
  color: var(--color-on-dark);
}

.stat__label {
  display: block;
  margin-top: var(--space-2xs);
  font-size: var(--fs-base);
  line-height: var(--leading-ui);
  color: var(--color-ink);
  text-wrap: balance;
}

.section--navy .stat__label,
.hero--dark .stat__label {
  color: var(--color-on-dark-muted);
}

/* 13 Testimonials
   ========================================================================== */

.testimonials {
  display: grid;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

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

@media (min-width: 68em) {
  .testimonials--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* A rounded, shadowed card holding a name and a role with no company and no
   date is the exact pattern readers have learned to discount, so the styling
   was making three real quotes look invented. Set them as quotations. */
.testimonial {
  display: flex;
  flex-direction: column;
  height: 100%;
  margin: 0;
  padding: 0 0 0 var(--space-md);
  background-color: transparent;
  border: 0;
  border-left: 2px solid var(--color-primary);
  border-radius: 0;
  box-shadow: none;
}

.testimonial__quote {
  margin: 0 0 var(--space-sm);
  font-size: var(--fs-base);
  line-height: 1.55;
  color: var(--color-ink);
  max-width: none;
}

.testimonial__attribution {
  margin-top: auto;
  font-size: var(--fs-sm);
  color: var(--color-ink-muted);
  font-style: normal;
}

.testimonial__name {
  display: block;
  font-weight: 600;
  color: var(--color-primary-deep);
}

.testimonial__role {
  display: block;
}

/* 14 FAQ accordion, details and summary. Works with no JavaScript.
   ========================================================================== */

.faq {
  display: grid;
  gap: var(--space-2xs);
  max-width: var(--container-narrow);
}

.faq__item {
  background-color: var(--color-surface);
  border: var(--border);
  border-radius: var(--radius);
}

.faq__item[open] {
  border-color: var(--color-line-strong);
}

.faq__q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--color-primary-deep);
  cursor: pointer;
  list-style: none;
  /* Required, or the hover and open fills square off the card's top corners. */
  border-radius: inherit;
}

.faq__q:hover {
  background-color: var(--color-tint);
}

/* The entire open state used to be a border shift of about 1.1:1, so on the FAQ
   page an expanded row was indistinguishable from the twenty collapsed ones.
   This component carries most of the interaction on the site, 69 instances. */
.faq__item[open] {
  border-color: var(--color-line-strong);
}

.faq__item[open] .faq__q {
  background-color: var(--color-tint-strong);
  border-bottom: 1px solid var(--color-line);
  border-radius: var(--radius) var(--radius) 0 0;
}

.faq__q::-webkit-details-marker {
  display: none;
}

.faq__q:hover {
  color: var(--color-primary);
}

.faq__q::after {
  content: "";
  flex: none;
  width: 0.6rem;
  height: 0.6rem;
  margin-top: 0.45em;
  border-right: 2.5px solid var(--color-accent);
  border-bottom: 2.5px solid var(--color-accent);
  transform: rotate(45deg);
  transition: transform var(--transition);
}

.faq__item[open] .faq__q::after {
  transform: rotate(-135deg);
}

.faq__a {
  padding: 0 var(--space-md) var(--space-md);
}

.faq__a > :last-child {
  margin-bottom: 0;
}

/* Question heading inside a summary keeps the document outline correct. */
.faq__q h3,
.faq__q h4 {
  margin: 0;
  font-size: inherit;
  color: inherit;
  font-weight: inherit;
}

/* 15 CTA band
   ========================================================================== */

.cta-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-color: var(--color-primary);
  background-image:
    radial-gradient(90% 160% at 6% 100%, rgba(32, 176, 224, 0.34), transparent 60%),
    linear-gradient(120deg, var(--color-primary-deep), var(--color-primary) 70%);
  color: var(--color-on-dark);
  padding-block: var(--section-pad);
  box-shadow: inset 0 3px 0 0 var(--color-cyan);
}

.cta-band::before {
  content: "";
  position: absolute;
  z-index: -1;
  right: 2%;
  bottom: -40%;
  width: min(22rem, 40%);
  aspect-ratio: 560 / 882;
  background: url("/assets/img/droplet-white.png") no-repeat center / contain;
  opacity: 0.08;
  pointer-events: none;
}

.cta-band--deep {
  background-color: var(--color-primary-deep);
}

.cta-band__inner {
  display: grid;
  gap: var(--space-md);
  align-items: center;
}

@media (min-width: 60em) {
  .cta-band__inner {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    gap: var(--space-xl);
  }
}

.cta-band__title {
  color: var(--color-on-dark);
  margin-bottom: var(--space-2xs);
}

.cta-band__text {
  color: var(--color-on-dark-muted);
  margin-bottom: 0;
  max-width: 58ch;
}

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

.cta-band .btn--secondary {
  background-color: var(--color-surface);
  border-color: var(--color-surface);
  color: var(--color-primary-deep);
}

.cta-band .btn--secondary:hover {
  background-color: var(--color-tint-strong);
  color: var(--color-primary-deep);
}

/* 16 Forms
   ========================================================================== */

.form {
  max-width: var(--container-narrow);
}

.form__grid {
  display: grid;
  gap: var(--space-sm);
}

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

  .form__field--full {
    grid-column: 1 / -1;
  }
}

.form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.form__label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-primary-deep);
}

.form__required {
  color: var(--color-required);
  font-weight: 600;
}

.form__hint {
  font-size: var(--fs-xs);
  color: var(--color-ink-muted);
}

.form__input,
.form__select,
.form__textarea {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.75rem;
  font: inherit;
  font-size: var(--fs-base);
  color: var(--color-ink);
  background-color: var(--color-surface);
  border: 1px solid var(--color-line-field);
  border-radius: var(--radius);
}

/* Inset, so an invalid field does not reflow the form. :user-invalid only fires
   after the visitor has actually interacted, unlike :invalid. */
.form__input:user-invalid,
.form__select:user-invalid,
.form__textarea:user-invalid {
  border-color: var(--color-required);
  box-shadow: inset 0 0 0 1px var(--color-required);
}

.form__textarea {
  min-height: 9rem;
  resize: vertical;
}

.form__select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--color-accent) 50%),
    linear-gradient(135deg, var(--color-accent) 50%, transparent 50%);
  background-position: calc(100% - 1.15rem) 1.15rem,
    calc(100% - 0.75rem) 1.15rem;
  background-size: 0.4rem 0.4rem, 0.4rem 0.4rem;
  background-repeat: no-repeat;
  padding-right: 2.25rem;
}

.form__input:hover,
.form__select:hover,
.form__textarea:hover {
  border-color: var(--color-accent);
}

.form__input::placeholder,
.form__textarea::placeholder {
  color: var(--color-ink-muted);
  opacity: 1;
}

.form__checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2xs);
  font-size: var(--fs-sm);
}

.form__checkbox input {
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.2rem;
  flex: none;
  accent-color: var(--color-primary);
}

/* This is the end of the funnel, so the submit matches every other primary
   call to action rather than being the quietest button on the site. */
.form__actions .btn {
  font-size: var(--fs-base);
  padding: 0.9rem 1.75rem;
}

.form__actions {
  margin-top: var(--space-md);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: center;
}

.form__note {
  font-size: var(--fs-sm);
  color: var(--color-ink-muted);
  margin-top: var(--space-sm);
}

/* Contact page layout: form plus contact details. */
.contact-layout {
  display: grid;
  gap: var(--space-xl);
}

@media (min-width: 62em) {
  .contact-layout {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  }
}

.contact-panel {
  padding: var(--space-md);
  background-color: var(--color-tint);
  border-radius: var(--radius-lg);
}

.nap {
  font-style: normal;
  display: grid;
  gap: var(--space-2xs);
}

.nap__label {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

.nap__value {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--color-primary-deep);
}

.nap a {
  color: var(--color-primary);
}

/* 17 Prose, tables, blockquote, answer blocks
   ========================================================================== */

.prose {
  max-width: 44rem;
}

.prose > * + * {
  margin-top: var(--space-flow);
}

/* Subheads bind downward to the section they introduce: a large gap above, a
   small one below. This is the mechanical fix for the wall of text feel on the
   2,900 word pages, and it is what lets a skimmer see which paragraphs belong
   to which standard. Direct child selectors only, so the 69 h3 elements inside
   FAQ summaries and the 44 classed ones in card titles are untouched. */
.prose h2 {
  margin-top: var(--space-2xl);
  margin-bottom: var(--space-xs);
  padding-top: 0;
}

.section > .container > h3,
.prose > h3,
.flow > h3 {
  margin-top: var(--space-xl);
  margin-bottom: var(--space-2xs);
}

.section > .container > h4,
.prose > h4,
.flow > h4 {
  margin-top: var(--space-lg);
  margin-bottom: var(--space-3xs);
}

.prose h3 {
  margin-top: var(--space-md);
}

.prose h4 {
  margin-top: var(--space-sm);
}

.prose h2 + p,
.prose h3 + p,
.prose h4 + p {
  margin-top: var(--space-2xs);
}

.prose ul,
.prose ol {
  margin-top: var(--space-2xs);
  padding-left: 1.35rem;
  max-width: var(--measure);
}

.prose li + li {
  margin-top: var(--space-3xs);
}

.prose img,
.prose figure img {
  border-radius: var(--radius);
}

.prose figure {
  margin: var(--space-md) 0;
}

.prose figcaption {
  margin-top: var(--space-2xs);
  font-size: var(--fs-sm);
  color: var(--color-ink-muted);
}

/* Answer first block. The extractable answer under a question heading. */
/* The answer first block is the thing this site does that its competitors do
   not, so it gets its own size and weight rather than being body copy in a
   faint tint. The box hugs the text instead of running the full section width,
   and it gets a bottom margin so the following paragraph stops colliding. */
.answer {
  /* A block formatting context, so the panel sits beside a floated figure
     instead of wrapping its tint around it, which made the image look like it
     was inside the answer. */
  display: flow-root;
  margin-block: var(--space-sm) var(--space-md);
  padding: var(--space-sm) var(--space-md);
  max-width: calc(var(--measure) + 2 * var(--space-md));
  background-color: var(--color-tint-strong);
  border-left: 5px solid transparent;
  border-image: linear-gradient(180deg, var(--color-primary), var(--color-cyan)) 1;
  border-radius: 0;
  font-size: var(--fs-md);
  line-height: 1.5;
}

.answer p {
  max-width: none;
}

/* A tinted panel on a tinted band measured 1.07:1, and callouts 1.00:1, so the
   panel simply disappeared. On tint bands the fill inverts to white and the
   boundary is carried by a border instead. */
.section--tint .answer {
  background-color: var(--color-surface);
}

.section--tint .callout,
.section--tint .jump-links,
.section--tint .contact-panel {
  background-color: var(--color-surface);
  border: 1px solid var(--color-line-strong);
}

/* A navy left bar now means "this is the direct answer" and nothing else, so
   quotations step down to a neutral hairline. */
blockquote,
.pullquote {
  border-left: 3px solid var(--color-line-strong);
}

.answer > :last-child {
  margin-bottom: 0;
}

.answer p {
  max-width: var(--measure);
}

.callout {
  display: flow-root;

  padding: var(--space-md);
  background-color: var(--color-tint);
  border: var(--border);
  border-radius: var(--radius-lg);
}

.callout > :last-child {
  margin-bottom: 0;
}

.callout__title {
  font-size: var(--fs-md);
  margin-bottom: var(--space-2xs);
}

blockquote,
.pullquote {
  margin: var(--space-md) 0;
  padding: var(--space-2xs) 0 var(--space-2xs) var(--space-md);
  border-left: 4px solid var(--color-accent);
  color: var(--color-ink);
  font-size: var(--fs-md);
  line-height: 1.55;
}

blockquote > :last-child,
.pullquote > :last-child {
  margin-bottom: 0;
}

blockquote cite,
.pullquote cite {
  display: block;
  margin-top: var(--space-2xs);
  font-size: var(--fs-sm);
  font-style: normal;
  color: var(--color-ink-muted);
}

.table-wrap {
  overflow-x: auto;
  margin: var(--space-md) 0;
  border: var(--border);
  border-radius: var(--radius);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  background-color: var(--color-surface);
}

.table caption {
  padding: var(--space-2xs) var(--space-sm);
  text-align: left;
  font-size: var(--fs-sm);
  color: var(--color-ink-muted);
}

.table th,
.table td {
  padding: 0.65rem 0.85rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--color-line);
}

.table thead th {
  background-color: var(--color-tint-strong);
  color: var(--color-primary-deep);
  font-weight: 600;
}

.table tbody tr:last-child th,
.table tbody tr:last-child td {
  border-bottom: 0;
}

.table--striped tbody tr:nth-child(even) {
  background-color: var(--color-tint);
}

/* Blog post header and meta. */
.post-header {
  margin-bottom: var(--space-md);
}

.post-meta {
  font-size: var(--fs-sm);
  color: var(--color-ink-muted);
}

.post-meta__item + .post-meta__item::before {
  content: "\00B7";
  margin: 0 0.4rem;
  color: var(--color-line-strong);
}

.tag {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background-color: var(--color-tint-strong);
  color: var(--color-primary-deep);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* 18 Footer
   ========================================================================== */

.site-footer {
  background-color: var(--color-primary-deep);
  color: var(--color-on-dark-muted);
  padding-block: var(--space-2xl) var(--space-md);
  font-size: var(--fs-sm);
}

.site-footer__grid {
  display: grid;
  gap: var(--space-lg);
}

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

@media (min-width: 68em) {
  .site-footer__grid {
    grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
    gap: var(--space-xl);
  }
}

.site-footer h2,
.site-footer h3,
.footer-col__title {
  color: var(--color-on-dark);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: var(--space-2xs);
}

.site-footer p {
  color: var(--color-on-dark-muted);
}

.site-footer a {
  color: var(--color-on-dark);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.site-footer a:hover {
  color: var(--color-on-dark);
  text-decoration-thickness: 2px;
}

.footer-brand {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--color-on-dark);
  margin-bottom: var(--space-2xs);
  text-transform: none;
  letter-spacing: -0.01em;
}

.footer-nap {
  font-style: normal;
  display: grid;
  gap: var(--space-3xs);
  color: var(--color-on-dark-muted);
}

.footer-nap__line {
  display: block;
}

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

.footer-nav a,
.footer-nap a,
.nap__value,
.link-list li {
  padding-block: 0.25rem;
}

.footer-bottom {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-on-dark-line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-md);
  justify-content: space-between;
  align-items: center;
  font-size: var(--fs-xs);
  color: var(--color-on-dark-muted);
}

.footer-bottom p {
  margin: 0;
  color: var(--color-on-dark-muted);
}

/* 19 Utilities
   ========================================================================== */

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

.text-center {
  text-align: center;
}

.text-center p,
.text-center .lede {
  margin-inline: auto;
}

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

.mt-0 {
  margin-top: 0;
}

.mb-0 {
  margin-bottom: 0;
}

.mt-md {
  margin-top: var(--space-md);
}

.mb-md {
  margin-bottom: var(--space-md);
}

.mt-lg {
  margin-top: var(--space-lg);
}

.mb-lg {
  margin-bottom: var(--space-lg);
}

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

.no-wrap {
  white-space: nowrap;
}

/* 20b Figures in the gutter
   ==========================================================================
   Narrow sections cap their text at the 56ch measure inside a 72rem container,
   which leaves roughly 26rem of empty gutter on the right at desktop. Figures
   used to stack in the text column and leave that gutter blank. They now float
   into it, so the image sits beside the text it belongs to.

   The paragraphs do not need to know: `p` is capped at --measure globally, so
   they keep their line length and simply run to the left of the float. At 1088px
   of container that is 600px of text, 48px of gap and a 320px figure, so nothing
   is squeezed. Below 62em the float is dropped and the figure goes full width
   above the text, which is the right order on a phone.
   ========================================================================== */

/* .prose caps itself at 44rem, which would trap the figure inside the text
   column, so a prose block that contains a figure releases its own width. The
   paragraphs inside it stay at --measure regardless. */
.prose:has(> figure) {
  max-width: none;
}

@media (min-width: 62em) {
  .prose > figure,
  .container--narrow > figure,
  .container > figure {
    float: right;
    width: 20rem;
    margin: 0 0 var(--space-md) var(--space-xl);
  }

  /* Rhythm, so a six figure page does not read as the same block six times.
     Keyed on the section's position, so it varies down a page and differs
     between pages without any markup or class names.

       odd sections    image right, 20rem, and 23rem with a brand accent on
                       every third, where there is room for it
       even sections   image left, 17rem

     Left hand figures are deliberately the smaller size. A wide image on the
     left leaves too little room for the text beside it, and a paragraph that
     wraps into a 150px column then jumps full width below reads as a mistake.

     Alternation rather than randomness: a rhythm reads as deliberate, a
     scatter reads as an accident. */
  main > section:nth-of-type(3n) .prose > figure {
    width: 23rem;
    position: relative;
    isolation: isolate;
  }

  /* An offset block in the brand gradient behind the lower corner. Drawn, not
     an asset, and only on every third figure so it stays an accent. */
  main > section:nth-of-type(3n) .prose > figure::before {
    content: "";
    position: absolute;
    z-index: -1;
    width: 5.5rem;
    height: 5.5rem;
    bottom: -0.85rem;
    left: -0.85rem;
    background-image: linear-gradient(135deg, var(--color-primary), var(--color-cyan));
  }

  /* Declared after the size rules so the left variant always wins on width. */
  main > section:nth-of-type(even) .prose > figure {
    float: left;
    width: 17rem;
    margin: 0 var(--space-xl) var(--space-md) 0;
  }

  main > section:nth-of-type(even) .prose > figure::before {
    left: auto;
    right: -0.85rem;
  }

  /* Anything full width that follows must clear the float, or it slides up
     beside the figure and the layout collapses. */
  .card-grid,
  .table-wrap,
  .steps,
  .testimonials,
  .stat-row,
  .tick-list--2col,
  .faq,
  .jump-links,
  .contact-layout,
  .section__header {
    clear: both;
  }
}

/* Image captions were removed: 90 identical mono boxes down the site read as
   filler, and the alt text already carries the description. .testimonial is also
   a figure with a figcaption, and that one is a quotation attribution, so it
   keeps its own styling untouched. */

/* 20c The droplet as a section watermark
   ==========================================================================
   A right floated figure only works if there is prose beside it to wrap. In
   sections whose body is a full width component, a stat row, a card grid,
   numbered steps or a two column tick list, the float is orphaned: nothing can
   sit next to it, so the image drops below and leaves a large void. 26 figures
   were removed for exactly that reason.

   Those sections get the logo droplet as a watermark instead, the same device
   as the hero and the CTA band. Scoped to the tint bands only, because they are
   already the pause in the page rhythm, so a large pale mark reads as texture
   there. On a white section it would read as a stray smudge.

   The droplet is the logo's own navy, not the white version, because white on a
   #EFF6FA tint is invisible.
   ========================================================================== */

main > section.section--tint:not(:has(figure)) {
  position: relative;
  isolation: isolate;
  /* Required. The watermark is bled off the right edge, and without this it
     creates horizontal page overflow that is invisible when inspecting
     elements, because the culprit is a pseudo element. */
  overflow: hidden;
}

main > section.section--tint:not(:has(figure))::after {
  content: "";
  position: absolute;
  z-index: -1;
  right: -4rem;
  top: 50%;
  transform: translateY(-50%);
  width: min(24rem, 40%);
  aspect-ratio: 560 / 882;
  background: url("/assets/img/droplet-navy.png") no-repeat center / contain;
  opacity: 0.09;
  pointer-events: none;
}

/* Below the split breakpoint the content uses the full width, so there is no
   void to fill and the mark would only sit behind live text. */
@media (max-width: 61.99em) {
  main > section.section--tint:not(:has(figure))::after {
    display: none;
  }
}

/* 21 Page type identity
   ==========================================================================
   The markup is frozen and no class names can be added, so page types are
   reached with :has() on structure that already exists and is unique to them:

     body:has(.post-header)    the 21 guide posts
     body:has(.jump-links)     the 3 pillar pages
     body:has(.contact-layout) the contact page

   Without this every page is the same template with the same section rule, and
   35 pages read as one long document. Each type now gets its own device.
   ========================================================================== */

/* -- The 21 guides: an editorial masthead ---------------------------------- */

/* The section wrapping .post-header becomes a full bleed navy band, so a guide
   announces itself as a guide rather than opening like a sales page. */
.section:has(> .container > .post-header) {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: var(--space-2xl) var(--space-xl);
  background-color: var(--color-primary-deep);
  background-image:
    radial-gradient(100% 130% at 92% 0%, rgba(32, 176, 224, 0.26), transparent 60%),
    linear-gradient(165deg, var(--color-primary), var(--color-primary-deep) 62%);
  box-shadow: inset 0 -3px 0 0 var(--color-cyan);
  /* Set the inherited colour, the way .hero--dark and .section--navy both do.
     Without it anything added to this band later inherits dark ink on navy at
     1.11:1. The individual overrides below are belt and braces, not the fix. */
  color: var(--color-on-dark);
}

.section:has(> .container > .post-header)::before {
  content: "";
  position: absolute;
  z-index: -1;
  right: -4rem;
  bottom: -55%;
  width: min(24rem, 46%);
  aspect-ratio: 560 / 882;
  background: url("/assets/img/droplet-white.png") no-repeat center / contain;
  opacity: 0.08;
  pointer-events: none;
}

/* Dark ground overrides for everything inside that band. */
.post-header h1 {
  color: var(--color-on-dark);
}

.post-header .lede {
  color: var(--color-on-dark-muted);
  max-width: 58ch;
}

.post-header .eyebrow {
  color: var(--color-cyan);
}

.section:has(> .container > .post-header) .btn--primary {
  background-color: var(--color-surface);
  border-color: var(--color-surface);
  color: var(--color-primary-deep);
}

.section:has(> .container > .post-header) .btn--primary:hover {
  background-color: var(--color-tint-strong);
  border-color: var(--color-tint-strong);
  color: var(--color-primary-dark);
}

.section:has(> .container > .post-header) .btn--secondary {
  background-color: transparent;
  border-color: var(--color-on-dark);
  color: var(--color-on-dark);
}

.section:has(> .container > .post-header) .btn--secondary:hover {
  background-color: rgba(255, 255, 255, 0.12);
  border-color: var(--color-on-dark);
  color: var(--color-on-dark);
}

/* Guides are sequential, so their sections are numbered. Every body heading in
   a post is an h2.section__title, verified across all 23, and the four footer
   column headings carry .footer-col__title so they are never caught. The number
   replaces the gradient rule, which is what the service pages use, so the two
   page types are told apart at a glance. */
body:has(.post-header) {
  counter-reset: guide-section;
}

body:has(.post-header) .section__title {
  counter-increment: guide-section;
  padding-top: var(--space-md);
}

body:has(.post-header) .section__title::before {
  content: counter(guide-section, decimal-leading-zero);
  width: auto;
  height: auto;
  background-image: none;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--color-accent);
}

/* A hairline runs from the number to the right, so the heading sits on a rule. */
body:has(.post-header) .section__title::after {
  content: "";
  position: absolute;
  top: 0.62em;
  left: 2.4rem;
  /* Fixed length, not right: 0. The posts mix narrow and wide containers, so a
     rule tied to the container edge changed length section to section and read
     as an accident rather than a device. */
  width: 10rem;
  height: 1px;
  background-image: linear-gradient(90deg, var(--color-cyan), transparent);
}

/* The masthead heading is not part of the numbered run. */
.post-header h1::before,
.post-header h1::after {
  content: none;
}

/* -- Cards, 23 on the blog index plus the home grid ----------------------- */

/* The cluster label is data, so it joins the mono voice. */
.card__meta {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* A card is a link target, so it should acknowledge the pointer. Border and rule
   only, no lift and no shadow, because this audience reads documents. */
.card {
  transition: border-color var(--transition);
}

.card:hover {
  border-color: var(--color-cyan);
}

/* -- The 3 pillar pages: a proper on-page index ---------------------------- */

/* The component, the ids and scroll-padding-top already existed, but it read as
   a paragraph of link text: a 19px regular label above 15px links in a wrapped
   flex row with 25px targets. */
.jump-links {
  padding: var(--space-md) var(--space-lg);
  background-color: var(--color-surface);
  border: 1px solid var(--color-line-strong);
  border-left: 3px solid transparent;
  border-image: linear-gradient(180deg, var(--color-primary), var(--color-cyan)) 1;
  border-radius: 0;
}

.jump-links__title {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin-bottom: var(--space-xs);
}

.jump-links ul {
  display: grid;
  gap: 0 var(--space-lg);
  font-size: var(--fs-base);
  counter-reset: jump;
}

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

@media (min-width: 68em) {
  .jump-links ul {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.jump-links li {
  counter-increment: jump;
  border-top: 1px solid var(--color-line);
}

.jump-links a {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: 44px;
  text-decoration: none;
}

.jump-links a::before {
  content: counter(jump, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--color-cyan);
  flex: 0 0 auto;
}

.jump-links a:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

/* -- Tables, on 13 of the guides ------------------------------------------ */

/* The comparison tables are the most credible thing in the build, so they are
   set as data: navy head, mono figures, a cyan rule closing the header. */
.table-wrap {
  border-top: 3px solid transparent;
  border-image: linear-gradient(90deg, var(--color-primary), var(--color-cyan)) 1;
}

.table caption {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  padding-block: var(--space-xs);
}

.table thead th {
  background-color: var(--color-primary-deep);
  color: var(--color-on-dark);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.table tbody th {
  color: var(--color-primary);
}

/* -- Process steps, on 18 guides and all 3 pillars ------------------------ */

/* A rule joins the discs, so the list reads as a sequence rather than as eight
   unrelated paragraphs. Drawn on the list, not per item, so it cannot show
   below the final step. */
.steps {
  position: relative;
  gap: var(--space-md);
}

.steps::before {
  content: "";
  position: absolute;
  left: 0.95rem;
  top: 1.9rem;
  bottom: 1.9rem;
  width: 2px;
  background-image: linear-gradient(180deg, var(--color-cyan), var(--color-line));
}

.steps li::before {
  z-index: 1;
}

.steps__title {
  color: var(--color-primary);
}

/* -- The contact page ------------------------------------------------------ */

/* The four route blocks are the page's real content, so they get weight, and
   the panel is lifted off the tint band it sits on. */
body:has(.contact-layout) .answer {
  background-color: var(--color-surface);
  border: 1px solid var(--color-line);
  border-left: 5px solid transparent;
  border-image: linear-gradient(180deg, var(--color-primary), var(--color-cyan)) 1;
}

.contact-panel {
  border-top: 3px solid transparent;
  border-image: linear-gradient(90deg, var(--color-primary), var(--color-cyan)) 1;
}

.nap__label {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

/* -- FAQ, on 15 pages ----------------------------------------------------- */

.faq__q::after {
  border-right-color: var(--color-cyan);
  border-bottom-color: var(--color-cyan);
}

/* Windows High Contrast and forced colours. The hamburger icon and the select
   arrow are both drawn with backgrounds, which forced colours mode discards, so
   without this the menu button and every dropdown lose their affordance
   entirely. Panel boundaries are restated for the same reason.
   ========================================================================== */

@media (forced-colors: active) {
  .nav-toggle__bars,
  .nav-toggle__bars::before,
  .nav-toggle__bars::after {
    background-color: CanvasText;
  }

  .form__select {
    appearance: auto;
    background-image: none;
    padding-right: 0.75rem;
  }

  .btn {
    border-color: currentColor;
  }

  .card,
  .faq__item,
  .answer,
  .stat,
  .testimonial {
    border: 1px solid CanvasText;
  }
}

/* 20 Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
