/* Synergy Safeguarding official brand palette. See docs/brand.md
   Contrast ratios on white are noted against each token. Body text needs 4.5:1,
   large text and UI components 3:1. Default to purple for anything carrying text. */

:root {
  /* Brand, official values */
  --purple: #6d417e;        /* 7.8:1. Safe anywhere: body text, buttons, headings */
  --purple-light: #9a52c1;  /* 4.8:1. Passes AA; fine for links and text */
  --green: #3d9140;         /* 3.9:1. LARGE TEXT AND UI ONLY. Not body copy,
                                      not white text on a green button. */
  --green-light: #92bd45;   /* 2.2:1. Graphics only, never text */
  --green-deep: #2f7a34;    /* 5.3:1. Darkened variant for green that must carry text */
  --charcoal: #3b3b3b;

  /* Applied theme, light */
  --bg: #ffffff;
  --surface: #f7f4f9;
  --text: var(--charcoal);
  --muted: #5d5566;
  --heading: var(--purple);
  --accent: var(--purple);
  --accent-hover: #55305f;
  --border: #e5dfea;

  /* Role tokens that have to flip between themes to hold contrast.
     --eyebrow and --icon carry the green that sits on the page background;
     brand green is too dark on the dark theme. */
  --eyebrow: var(--green-deep);   /* 5.3:1 on white */
  --icon: var(--green);           /* 3.9:1 on white, UI-component threshold */

  /* Depth. Tinted with the brand hue rather than neutral black, so shadows
     read as part of the palette instead of dirtying it. */
  --shadow-sm: 0 1px 2px rgba(45, 24, 55, 0.05), 0 2px 6px rgba(45, 24, 55, 0.05);
  --shadow-md: 0 4px 10px rgba(45, 24, 55, 0.06), 0 12px 28px rgba(45, 24, 55, 0.08);
  --shadow-lg: 0 10px 20px rgba(45, 24, 55, 0.08), 0 24px 52px rgba(45, 24, 55, 0.12);

  /* Hero wash. Painted on <body> as a background layer so it is full-bleed
     without any 100vw trickery that would cause horizontal scroll. */
  --wash-purple: rgba(154, 82, 193, 0.16);
  --wash-green: rgba(146, 189, 69, 0.20);

  /* The brand's two-tone swirl, reused wherever an edge needs brand colour */
  --brand-sweep: linear-gradient(90deg, var(--purple) 0%, var(--purple-light) 45%, var(--green-light) 55%, var(--green) 100%);

  --radius: 10px;
  --radius-lg: 16px;
  --max-width: 1040px;

  /* Motion. One easing curve and three durations across the whole site,
     so nothing feels like it came from a different page. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 140ms;
  --dur: 260ms;
  --dur-slow: 620ms;
}

/* Dark theme lightens the brand purple. #6D417E on a dark ground is unreadable.
   These are tints of the brand hue, not new brand colours. */
:root[data-theme="dark"] {
  --bg: #16121a;
  --surface: #1f1926;
  --text: #ece7f0;
  --muted: #a9a0b3;
  --heading: #c79ae2;
  --accent: #c79ae2;
  --accent-hover: #d9b8ee;
  --border: #322a3b;

  /* green-deep is only 3.5:1 on the dark ground, which fails AA for the
     small uppercase eyebrow. green-light is 8.5:1 here. */
  --eyebrow: var(--green-light);
  --icon: var(--green-light);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.40), 0 2px 6px rgba(0, 0, 0, 0.30);
  --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.45), 0 12px 28px rgba(0, 0, 0, 0.38);
  --shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.50), 0 24px 52px rgba(0, 0, 0, 0.55);

  --wash-purple: rgba(154, 82, 193, 0.22);
  --wash-green: rgba(146, 189, 69, 0.10);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Sticky header would otherwise cover the target of an in-page anchor */
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  line-height: 1.65;
  color: var(--text);
  background-color: var(--bg);
  background-image:
    radial-gradient(62% 58% at 10% 0%, var(--wash-purple) 0%, transparent 62%),
    radial-gradient(58% 52% at 90% 4%, var(--wash-green) 0%, transparent 60%);
  background-repeat: no-repeat;
  background-position: top center;
  background-size: 100% 860px;
  /* Only colour transitions on theme switch. Transitioning the gradient layers
     would repaint the whole wash every frame. */
  transition: background-color var(--dur) ease, color var(--dur) ease;
}

img {
  max-width: 100%;
}

h1,
h2,
h3 {
  color: var(--heading);
  line-height: 1.2;
  letter-spacing: -0.018em;
  text-wrap: balance;
}

a {
  color: var(--accent);
  text-underline-offset: 0.18em;
  transition: color var(--dur-fast) ease;
}

a:hover {
  color: var(--accent-hover);
}

::selection {
  background: var(--purple);
  color: #fff;
}

/* Header */
.site-header {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0.85rem 1.5rem;
  border-bottom: 1px solid transparent;
  position: sticky;
  top: 0;
  z-index: 10;
  /* Fallback first: opaque bar for anything without color-mix support. */
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  -webkit-backdrop-filter: saturate(1.7) blur(14px);
  backdrop-filter: saturate(1.7) blur(14px);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

/* .scrolled is added by script.js. Without JS the header keeps the translucent
   panel above, which stays readable over scrolled content; it just never picks
   up the lift. */
.site-header.scrolled {
  border-bottom-color: var(--border);
  box-shadow: var(--shadow-sm);
}

/* At the very top there is nothing to separate from, and the frosted panel
   leaves a visible seam across the hero wash. Drop it until the page moves.
   Gated on .js so a no-JS page keeps the always-readable panel. */
.js .site-header:not(.scrolled) {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  transition: transform var(--dur) var(--ease);
}

.brand:hover {
  transform: scale(1.04);
}

/* Primary logo is stacked (mark above wordmark), near-square at 3299x3221 */
.brand img {
  height: 64px;
  width: auto;
}

.nav {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}

.nav a {
  position: relative;
  color: var(--muted);
  text-decoration: none;
  font-weight: 500;
  padding-block: 0.15rem;
}

/* Underline wipes in from the left in brand colours */
.nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  border-radius: 2px;
  background: var(--brand-sweep);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease);
}

.nav a:hover,
.nav a:focus-visible,
.nav a[aria-current="page"] {
  color: var(--accent);
}

.nav a:hover::after,
.nav a:focus-visible::after,
.nav a[aria-current="page"]::after {
  transform: scaleX(1);
}

.theme-toggle {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.35rem 0.6rem;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  color: inherit;
  transition: transform var(--dur) var(--ease), border-color var(--dur) ease,
    background-color var(--dur) ease;
}

.theme-toggle:hover {
  transform: rotate(-12deg) scale(1.08);
  border-color: var(--accent);
}

/* On narrow screens the nav items wrap to several lines and the sticky
   header swallows a third of the viewport. Put the brand and toggle on one
   row and the nav on its own scrollable row beneath. */
@media (max-width: 720px) {
  .site-header {
    flex-wrap: wrap;
    gap: 0.4rem 1rem;
    padding: 0.6rem 1rem;
  }

  .brand img {
    height: 46px;
  }

  .theme-toggle {
    margin-left: auto;
  }

  .nav {
    order: 3;
    width: 100%;
    margin-left: 0;
    /* The audience links drop to their first word below this breakpoint, so the
       three of them fit one row at 320px with room to spare. Accreditation and
       Partners take a second row beneath. The row wraps rather than scrolls, so
       if a large text setting ever does overflow it, the header grows taller and
       nothing is hidden. Centred rather than spread: space-between strands the
       two links on the second row at opposite edges. */
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 0.9rem;
    font-size: 0.83rem;
    padding-bottom: 0.2rem;
  }

  /* The rest of each label stays in the DOM and is only clipped from view, so
     the accessible name and the crawlable text are both still the full label.
     Visible "Community" is contained in accessible "Community Organisations",
     which is what WCAG 2.5.3 Label in Name asks for. */
  .nav-tail {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* The Home link is a desktop convenience. On a phone the brand logo sits in
     the same header, links to the same place and is the pattern people expect,
     so hiding the duplicate keeps the three audience links at a readable size. */
  .nav a[href="/"] {
    display: none;
  }

  .nav a {
    white-space: nowrap;
  }
}

/* Layout */
.hero,
.section {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 4rem 1.5rem;
}

.hero {
  text-align: center;
  padding-block: 5.5rem 4rem;
  /* Wider than --max-width, which the rest of the page keeps. Hazel asked on
     14 August 2026 for the first line of the headline to hold on one line, and
     that line is 42 characters: at the old 1040px measure it could not, at any
     size still worth calling a headline. This buys 140px of the gap and the
     headline size below buys the rest. Only the hero is widened, so the reading
     measure of the body copy underneath is untouched. */
  max-width: 1180px;
}

/* The horizontal lockup above the headline. Sized from its real dimensions so
   it reserves its own space, and centred with the rest of the hero. */
.hero-lockup {
  display: block;
  width: 220px;
  height: auto;
  margin: 0 auto 1.6rem;
}

/* On the dark theme it sits on white, the same treatment the accreditation
   marks and the partner logo panels already use, and for the same reason.
   Measured, not assumed: the "SYNERGY SAFEGUARDING" wordmark inside this PNG is
   rgb(56,56,56), which is 1.58:1 against the dark ground and simply cannot be
   read. On white it is 11.7:1. Without this the hero shows a coloured swirl with
   the company name invisible beside it, which is worse than no logo at all.

   The proper fix is a light on dark artwork from the designer, which would also
   settle the same problem in the site header. Until that exists, this is the
   established pattern in this stylesheet rather than a new idea. See
   docs/brand.md. */
:root[data-theme="dark"] .hero-lockup {
  background: #fff;
  border-radius: 12px;
  padding: 0.85rem 1.1rem;
  box-sizing: content-box;
}

@media (max-width: 720px) {
  .hero-lockup {
    width: 180px;
    margin-bottom: 1.2rem;
  }
}

@media (max-width: 720px) {
  .hero {
    padding-block: 3rem 2.5rem;
  }

  .hero,
  .section {
    padding-inline: 1.25rem;
  }

  .section {
    padding-block: 3rem;
  }
}

.hero h1 {
  /* Cap lowered from 3.5rem on 14 August 2026, as the other half of getting
     line one onto a single line. See the note on .hero above. */
  font-size: clamp(2.1rem, 1.1rem + 3.2vw, 3.05rem);
  letter-spacing: -0.028em;
  margin: 0 0 1.1rem;
}

/* Each sentence of the headline sits on its own line. Block spans rather than
   <br>, so the break is a layout decision the stylesheet owns and the editable
   text stays plain. text-wrap: balance is on h1 already and still applies to
   each line separately, so a long line breaks evenly on a phone. */
.hero h1 .line {
  display: block;
}

/* "Safer workforces create safer communities." on one line, asked for on
   14 August 2026. There is deliberately no `white-space: nowrap` here, and that
   is the point: the line is made to fit rather than forced to fit.

   Measured in headless Chrome across fourteen viewport widths. At the 3.05rem
   cap the rendered line is 931px wide, and the hero measure is 937px at a
   1000px viewport and 1132px at the full 1180px. Text only wraps when it does
   not fit, so above 1000px it stays on one line on its own, and below that it
   wraps normally instead of overflowing. nowrap would have bought nothing above
   1000px and caused horizontal scroll below it, which is the WCAG 1.4.10 reflow
   failure this repo has already had once in the nav.

   It cannot be one line on a phone: at 390px, 42 characters would need a 17px
   headline, smaller than the body copy underneath. The wrap on small screens is
   the correct outcome, not a shortfall. Re-measure if the wording changes. */

/* "We help you prove it!" two pixels down from the line above it, and in brand
   green. Asked for on 14 August 2026.
   --green (#3D9140) is 3.9:1 on white and 4.7:1 on the dark ground, and this
   line paints at 47px, well past the 24px large-text threshold that needs 3:1.
   It is the one place on the site where brand green carries display text, and
   it clears the bar in both themes. --green-light (2.2:1) would not, which is
   why the palette forbids it for text at all. See docs/brand.md. */
.hero h1 .line-prove {
  font-size: calc(1em - 2px);
  color: var(--green);
}

.hero p {
  color: var(--muted);
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.2rem);
  max-width: 42rem;
  margin: 0 auto 2rem;
  text-wrap: pretty;
}

/* The bold line between the headline and the introduction. Full text colour
   rather than --muted, because a bold muted line reads as a disabled control. */
.hero .hero-lead {
  color: var(--text);
  margin-bottom: 0.9rem;
}

.hero .cta-group {
  justify-content: center;
  margin-top: 2rem;
}

/* Hero entrance. Gated behind .js for the same reason as the scroll reveals:
   animation-fill-mode: both holds the element at opacity 0 until the animation
   runs, so an unstyled-motion environment would leave the headline blank. With
   the gate, a page without JavaScript paints the hero immediately. */
.js .hero > * {
  animation: rise-in var(--dur-slow) var(--ease) both;
}

.js .hero > .eyebrow { animation-delay: 40ms; }
.js .hero > h1 { animation-delay: 90ms; }
.js .hero > p { animation-delay: 180ms; }
.js .hero > .cta-group { animation-delay: 260ms; }

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.section h2 {
  font-size: clamp(1.6rem, 1.3rem + 1.1vw, 2.1rem);
  margin-top: 0;
}

/* Sub-headings that sit partway down a section, "Our values" and "Our six-step
   process". The browser default h3 is 1.17rem, which against a 2.1rem h2 reads
   as body copy in bold rather than as a heading. This sits between the two. */
.section h3.subhead {
  font-size: clamp(1.3rem, 1.15rem + 0.6vw, 1.6rem);
  margin: 2.75rem 0 1rem;
}

/* The brand's two-tone swirl, used as a section rule. Wipes open on reveal.
   The collapsed start state is gated behind .js so a no-JS page still draws
   the rule at full width. */
.rule {
  height: 4px;
  border: none;
  border-radius: 2px;
  background: var(--brand-sweep);
  max-width: var(--max-width);
  margin: 0 auto;
}

.js .rule {
  transform: scaleX(0.12);
  transition: transform 900ms var(--ease);
}

.js .rule.is-visible {
  transform: scaleX(1);
}

/* Shared card surface. Everything that reads as a "tile" uses this base so the
   depth, radius and hover feel are identical across the site. */
.audience-card,
.tier,
.process li,
.solutions li,
.values li,
.partner,
.level-block {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    border-color var(--dur) ease;
}

.tier:hover,
.process li:hover,
.solutions li:hover,
.values li:hover,
.partner:hover,
.level-block:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}

/* Buttons */
.btn {
  display: inline-block;
  position: relative;
  background: var(--purple);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  padding: 0.8rem 1.6rem;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(45, 24, 55, 0.18);
  transition: background-color var(--dur) ease, transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

.btn:hover,
.btn:focus-visible {
  background: var(--accent-hover);
  color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(45, 24, 55, 0.26);
}

.btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 6px rgba(45, 24, 55, 0.18);
}

/* Dark theme buttons invert: a light tint of the brand hue carrying near-black
   text. #9A52C1 with dark text is only 3.89:1, so the ground is lifted to
   #B478DD (5.9:1) and brightens to #C79AE2 on hover (8.2:1). Both are theme
   tints of the brand purple, not new brand colours. See docs/brand.md. */
:root[data-theme="dark"] .btn {
  background: #b478dd;
  color: #14101a;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
}

:root[data-theme="dark"] .btn:hover,
:root[data-theme="dark"] .btn:focus-visible {
  background: var(--accent-hover);
  color: #14101a;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55);
}

/* Founder portrait */
.founder {
  display: grid;
  grid-template-columns: minmax(200px, 320px) 1fr;
  gap: 2rem;
  align-items: start;
}

.founder img,
.founder-bio img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  display: block;
  box-shadow: var(--shadow-md);
}

@media (max-width: 640px) {
  .founder {
    grid-template-columns: 1fr;
  }
}

/* Form */
.contact-form {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  max-width: 32rem;
}

.contact-form label {
  font-weight: 600;
  margin-top: 0.75rem;
}

.contact-form input,
.contact-form textarea {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  transition: border-color var(--dur) ease, box-shadow var(--dur) ease;
}

.contact-form input:hover,
.contact-form textarea:hover {
  border-color: var(--accent);
}

.contact-form input:focus,
.contact-form textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  box-shadow: 0 0 0 4px rgba(109, 65, 126, 0.12);
}

.contact-form .btn {
  margin-top: 1.25rem;
  align-self: flex-start;
}

/* Honeypot. Off screen rather than display:none, because some bots skip
   anything hidden outright. The input also carries tabindex="-1", which keeps
   it out of the tab order and out of the accessibility tree. */
.botcheck {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  min-height: 1.5rem;
  color: var(--muted);
  margin: 0.5rem 0 0;
}

.form-status.pending {
  color: var(--muted);
}

.form-status.error {
  color: #b3261e;
}

.form-status.success {
  color: var(--green-deep);
}

:root[data-theme="dark"] .form-status.error {
  color: #f2b8b5;
}

:root[data-theme="dark"] .form-status.success {
  color: var(--green-light);
}

/* Data protection notice under the Send button. Smaller than the form, because
   it is a note rather than a field, but --muted at 0.9rem is 7.0:1 on the light
   theme and 7.4:1 on the dark one, so it stays well clear of AA. */
.form-privacy {
  margin: 0.75rem 0 0;
  font-size: 0.9rem;
  color: var(--muted);
  max-width: 46rem;
  text-wrap: pretty;
}

/* Visible focus for keyboard users throughout */
a:focus-visible,
button:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Available to a screen reader, invisible on screen. Used for the "opens in a
   new tab" note on the two partner links that open in one. */
/* Text for assistive technology only. It stays in the accessibility tree and in
   the accessible name of whatever contains it, and is never painted.

   `clip` is the legacy half of the pattern, kept for older engines alongside
   `clip-path`. Deliberately NOT `opacity: 0`, and not a zero size box: both were
   tried on 6 August 2026 to stop a contrast checker measuring this text, and
   both are worse than the problem. A checker decides an element is painted from
   `getClientRects()`, and clipping does not empty that list, so the only thing
   that silences it is opacity, which then also removes the element from every
   other rule that filters on visibility, heading order included. The right place
   to fix a contrast reading on invisible text is the colour it inherits, which
   is what `.partner-link` below now sets. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Skip link */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--purple);
  color: #fff;
  padding: 0.75rem 1.25rem;
  z-index: 100;
}

.skip-link:focus {
  left: 0;
}

/* Trust strip.
   Deliberately hard-coded rather than built from --accent-hover: that token
   flips to a pale lilac on the dark theme, which would put white text on a
   near-white ground. Both stops here are dark enough for white text in either
   theme (#6D417E is 7.8:1, #55305F is darker still). */
.trust-strip {
  position: relative;
  background: linear-gradient(115deg, #6d417e 0%, #55305f 100%);
  color: #fff;
  padding: 1rem 1.5rem;
  text-align: center;
  font-size: 0.95rem;
}

/* Brand sweep as a hairline along the top edge */
.trust-strip::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--brand-sweep);
}

.trust-strip ul {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: var(--max-width);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 2rem;
}

.trust-strip li {
  position: relative;
  font-weight: 500;
}

/* Dot separators, hidden on the wrapped/mobile layout */
.trust-strip li + li::before {
  content: "";
  position: absolute;
  left: -1rem;
  top: 50%;
  width: 4px;
  height: 4px;
  margin-top: -2px;
  border-radius: 50%;
  background: var(--green-light);
}

@media (max-width: 640px) {
  .trust-strip li + li::before {
    display: none;
  }
}

/* Audience selector cards */
.audience-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
}

.audience-card {
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
  padding: 1.75rem 1.5rem 1.5rem;
  text-decoration: none;
  color: inherit;
}

/* Accent bar along the top that fills across on hover */
.audience-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 4px;
  background: var(--brand-sweep);
  transform: scaleX(0.34);
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease);
}

.audience-card:hover,
.audience-card:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: var(--accent);
  color: inherit;
}

.audience-card:hover::before,
.audience-card:focus-visible::before {
  transform: scaleX(1);
}

.audience-card h3 {
  margin: 0 0 0.5rem;
}

.audience-card p {
  color: var(--muted);
  margin: 0 0 1rem;
}

.audience-card .go {
  margin-top: auto;
  font-weight: 600;
  color: var(--accent);
  transition: transform var(--dur) var(--ease);
}

.audience-card:hover .go,
.audience-card:focus-visible .go {
  transform: translateX(5px);
}

/* Icon lists and value blocks */
.icon-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.9rem;
}

.icon-list li {
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: 0.85rem;
  align-items: start;
}

/* Bold list text, where the list is the point of the section rather than a
   supporting detail. Applied to the <ul> as a modifier, not to .icon-list
   itself, so the audience pages keep their lighter treatment. The weight is in
   CSS because a <strong> inside the tagged <span> would be deleted the first
   time the line was edited through /admin. */
.icon-list.is-strong li span {
  font-weight: 700;
  color: var(--text);
}

/* The other half of the same idea, added 14 August 2026 for the benefits ticks:
   only the lead phrase is bold and the rest of the line stays regular. That
   cannot be done from CSS alone, so those seven lines carry a real <strong> in
   the markup, outside the tagged span, wrapped with it in .tick-text. This rule
   only has to supply the text colour that .is-strong was supplying before. */
.icon-list li .tick-text {
  color: var(--text);
}

/* The tick sits in a soft tinted disc rather than floating on the page */
.icon-list svg {
  box-sizing: content-box;
  width: 18px;
  height: 18px;
  padding: 7px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--icon) 14%, transparent);
  color: var(--icon);
  flex: none;
  margin-top: 0.1rem;
}

.values {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  list-style: none;
  padding: 0;
}

.values li {
  padding: 1.5rem;
}

.values svg {
  width: 32px;
  height: 32px;
  color: var(--accent);
  margin-bottom: 0.6rem;
}

.values h3,
.values h4 {
  margin: 0 0 0.35rem;
  font-size: 1.15rem;
  color: var(--heading);
}

.values p {
  margin: 0;
  color: var(--muted);
}

/* The line that closes the About us section. It was italic, then bold only on
   10 August 2026. Hazel asked on 14 August 2026 for it to be italic as well as
   bold, which is what it now is. Recording the round trip so the next person
   does not "fix" it back: this is the asked-for state, not a leftover.
   The bold comes from the <strong> in the markup, the italic from here, so the
   tagged text inside stays plain and survives an edit through /admin. */
.synergy-line {
  margin-top: 2rem;
  font-size: 1.1rem;
  color: var(--heading);
  font-style: italic;
  text-wrap: pretty;
}

/* Who issues the Synergy Safe mark. Set apart from the paragraph above it so a
   procurement reader skimming the section still lands on it, without dressing a
   plain statement of fact up as a promotional callout. */
.issuer-note {
  margin-top: 1.1rem;
  padding-left: 1rem;
  border-left: 3px solid var(--green);
  color: var(--text);
}

/* The three Synergy Safe marks on the homepage, in level order.
   auto-fit rather than three fixed columns, so the row becomes a single column
   on a phone without a second breakpoint. */
.level-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  list-style: none;
  padding: 0;
  margin: 2rem 0 0;
  text-align: center;
}

.level-row li {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.level-row img {
  width: 120px;
  height: auto;
  margin-bottom: 0.9rem;
}

/* Same reason as .level-mark on /accreditation: the "SYNERGY SAFE" wordmark
   inside these PNGs is dark charcoal, so on the dark ground it vanishes and the
   mark reads as a floating swirl with no name attached. Sit it on white. */
:root[data-theme="dark"] .level-row img {
  background: #fff;
  border-radius: 10px;
  padding: 0.7rem;
}

.level-caption {
  margin: 0;
}

/* Level number in the body text colour, level name in bold purple. Asked for on
   14 August 2026 as "black" and "bold purple": --text is this site's black, the
   charcoal that matches the wordmark, and it is what every other piece of body
   copy uses. True #000 appears nowhere in the palette. See docs/brand.md. */
.level-row .level-num {
  display: block;
  color: var(--text);
  font-size: 0.95rem;
}

.level-row .level-name {
  display: block;
  color: var(--heading);
  font-weight: 700;
  font-size: 1.05rem;
  margin-top: 0.15rem;
}

/* Six-step process */
.process {
  list-style: none;
  counter-reset: step;
  padding: 0;
  margin: 1.75rem 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
}

.process li {
  counter-increment: step;
  padding: 1.25rem 1rem;
  font-weight: 600;
  /* Numbered badge and step icon share the top row, label spans beneath both.
     The label is a <span> rather than a bare text node because the span is what
     carries data-edit; see the note in the six-step markup. */
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.7rem 0.65rem;
}

.process li::before {
  content: counter(step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--purple), var(--purple-light));
  color: #fff;
  font-size: 0.9rem;
  box-shadow: 0 3px 8px rgba(45, 24, 55, 0.22);
  transition: transform var(--dur) var(--ease);
}

/* Pushed to the far edge so the solid numbered disc and the line icon read as
   two separate things rather than one crowded cluster. */
.process .step-icon {
  width: 24px;
  height: 24px;
  color: var(--accent);
  justify-self: end;
}

.process li > span {
  grid-column: 1 / -1;
}

.process li:hover::before {
  transform: scale(1.12) rotate(-6deg);
}

/* Accreditation tier cards */
.tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
  margin-top: 1.5rem;
}

.tier {
  padding: 1.5rem;
  border-top-width: 4px;
  border-top-style: solid;
}

.tier-1 { border-top-color: var(--green-light); }
.tier-2 { border-top-color: var(--green); }
.tier-3 { border-top-color: var(--purple); }

.tier h3 {
  margin: 0 0 0.25rem;
  font-size: 1.15rem;
}

.tier .level {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.6rem;
}

/* Legal reality uses native details/summary, so content stays in the DOM when collapsed */
.legal-reality {
  border: 1px solid var(--border);
  border-left: 4px solid var(--purple);
  border-radius: var(--radius-lg);
  background: var(--surface);
  margin-top: 2.5rem;
  box-shadow: var(--shadow-sm);
}

.legal-reality > summary {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  padding: 1.1rem 1.35rem;
  font-weight: 600;
  color: var(--heading);
  list-style: none;
}

.legal-reality > summary::-webkit-details-marker {
  display: none;
}

/* Chevron that rotates from pointing right to pointing down */
.legal-reality > summary::before {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--dur) var(--ease);
}

.legal-reality[open] > summary::before {
  transform: rotate(45deg);
}

.legal-reality > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.legal-reality .legal-body {
  padding: 0 1.35rem 1.35rem;
  font-size: 0.95rem;
}

.legal-reality .disclaimer {
  font-style: italic;
  color: var(--muted);
}

/* CTA group. Health Check carries the visual weight */
.cta-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
}

.cta-group .btn-link {
  font-weight: 600;
  color: var(--accent);
  text-decoration-color: transparent;
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
  transition: text-decoration-color var(--dur) ease, color var(--dur-fast) ease;
}

.cta-group .btn-link:hover,
.cta-group .btn-link:focus-visible {
  text-decoration-color: currentColor;
}

/* Eyebrow label above section headlines */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--eyebrow);
  margin-bottom: 0.6rem;
}

.eyebrow::before {
  content: "";
  width: 1.5rem;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

/* Centred hero eyebrow needs the dash on both sides to stay balanced */
.hero .eyebrow::after {
  content: "";
  width: 1.5rem;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

/* Link-through line beneath a neutral reference section */
.link-through {
  margin-top: 2rem;
  font-size: 1.05rem;
}

/* Accreditation levels are descriptive, not pricing cards.
   No tick-lists, no "most chosen" badge, no prices. See the accreditation addendum. */
.levels {
  display: grid;
  gap: 1.5rem;
  margin-top: 2rem;
}

.level-block {
  border-left-width: 5px;
  border-left-style: solid;
  padding: 1.65rem;
}

.level-block.level-1 { border-left-color: var(--green-light); }
.level-block.level-2 { border-left-color: var(--green); }
.level-block.level-3 { border-left-color: var(--purple); }

/* The Synergy Safe mark for this level. The colourway carries the same meaning
   as the border colour above, so the two must stay in step: green for Level 1,
   purple and green for Level 2, purple for Level 3. See docs/brand.md. */
.level-mark {
  display: block;
  width: 120px;
  height: auto;
  margin: 0 0 1rem;
}

/* The "SYNERGY SAFE" wordmark inside these PNGs is dark charcoal, so on the
   dark ground it disappears and the mark reads as a floating swirl. Same fix
   as the partner logo panels: sit it on white. The files are transparent PNGs,
   so this shows through properly rather than boxing them in. */
:root[data-theme="dark"] .level-mark {
  background: #fff;
  border-radius: 10px;
  padding: 0.7rem;
}

.level-block h3 {
  margin: 0 0 0.75rem;
}

.level-block .who,
.level-block .becoming {
  margin: 0 0 0.75rem;
}

.level-block .label {
  font-weight: 700;
  color: var(--heading);
}

/* Solutions grid */
.solutions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1rem;
  list-style: none;
  padding: 0;
  margin-top: 1.5rem;
  /* All twelve cards carry a description as of the August 2026 solutions
     addendum, so the row can stretch again. The previous align-items: start was
     there only because ten cards were title-only and stretching made them look
     broken. Equal height cards read better now that they all have copy. */
}

.solutions li {
  position: relative;
  padding: 1.2rem 1.2rem 1.2rem 1.35rem;
  overflow: hidden;
}

/* Green edge that grows down the card on hover */
.solutions li::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--green);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform var(--dur) var(--ease);
}

.solutions li:hover::before {
  transform: scaleY(1);
}

/* One line icon per card, purple, on a lilac badge. Asked for by Hazel on
   10 August 2026. The badge is drawn on the icon itself rather than added as a
   wrapper element, so all twelve cards change from one rule and the markup
   stays as it is.

   content-box, so the padding adds to the 22px drawing rather than eating into
   it.

   REVERSED on 14 August 2026, at Hazel's request: light icon on a dark badge,
   where it was a dark icon on a light badge. It is the same lilac in both
   directions, it has simply swapped sides. That lilac is the old badge colour
   made opaque: the badge used to be brand purple at 18% over the page, and the
   icon is now exactly that colour painted solid, so nothing new enters the
   palette.

   Both values are fixed rather than theme dependent, which is a change from
   what this rule used to do. --purple and the lilac mixed from it do not move
   between themes, so the badge reads the same way on both, and the icon stays
   light on dark in the dark theme instead of flipping back to dark on light,
   which is what following --accent would have done. Measured: 5.9:1 in the
   light theme and 3.4:1 in the dark one, against the 3:1 that a UI component
   needs. See docs/brand.md. */
.solutions .solution-icon {
  display: block;
  box-sizing: content-box;
  width: 22px;
  height: 22px;
  padding: 10px;
  border-radius: 12px;
  background: var(--purple);
  color: color-mix(in srgb, var(--purple) 18%, #fff);
  margin-bottom: 0.85rem;
}

/* In the dark theme the badge keeps the deep brand purple, but the icon lifts
   to the theme's lightened purple: the flat lilac above is close enough to the
   card surface there to lose its edge, and --accent is 3.4:1 on the badge. */
:root[data-theme="dark"] .solutions .solution-icon {
  color: var(--accent);
}

.solutions h3 {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
}

.solutions p {
  margin: 0;
  color: var(--muted);
  font-size: 0.95rem;
}

/* Partners is a logo-led showcase, not a text directory */
.partners {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  list-style: none;
  padding: 0;
  margin-top: 1.5rem;
}

.partner {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Logo panel stays white in both themes. Partner logos are supplied on white
   and must not be tinted by the dark theme. */
.partner .logo-panel {
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.75rem 1.5rem;
  /* Fixed, not min-height, so all three panels are the same depth and the card
     bodies start on the same line. The three supplied logos are different
     shapes, one portrait and two landscape banners, so their painted heights
     differ by design; with min-height the tallest pushed its own panel down and
     the row lost its baseline. 170px is the tallest logo at its cap, 110px,
     plus the padding above. */
  height: 170px;
  border-bottom: 1px solid var(--border);
}

/* The whole logo panel is the link target, so it stays a comfortable click and
   tap area whether the panel holds a placeholder or a real logo file.

   The colour is pinned to the brand purple rather than left to inherit. The
   panel above is #fff in both themes, because a logo showcase has to sit on
   white whatever the rest of the page is doing, but the link colour it would
   otherwise inherit is theme dependent: in dark theme that is --accent, the
   #C79AE2 tint, which measures 2.30:1 on white and fails AA. Today the only text
   in here is the placeholder, which sets its own colour, and the "(opens in a
   new tab)" span, which is never painted, so nothing was visibly wrong. It would
   have become visible the moment any other text was added to this link. Pinning
   the colour fixes it for both cases at once. --purple is a brand token and does
   not change between themes, so this is 7.8:1 in light and dark alike. */
.partner .logo-panel .partner-link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 80px;
  text-decoration: none;
  color: var(--purple);
}

.partner .logo-panel img {
  max-width: 100%;
  max-height: 110px;
  width: auto;
  height: auto;
  transition: transform var(--dur-slow) var(--ease);
}

.partner:hover .logo-panel img {
  transform: scale(1.05);
}

/* Commission disclosure beneath the partner logos. Small print by convention,
   but --muted still measures 7.1:1 on white and 7.4:1 on the dark ground at
   this size, so it is readable rather than merely present. */
.partner-disclosure {
  margin: 1.75rem 0 0;
  font-size: 0.9rem;
  color: var(--muted);
  max-width: 46rem;
}

.partner .partner-body {
  padding: 1.35rem;
}

.partner h3 {
  margin: 0 0 0.5rem;
  font-size: 1.15rem;
}

.partner p {
  margin: 0;
  font-size: 0.95rem;
}

/* The partner's own address, repeated as visible text beneath the description.
   The logo panel above already links to the same place, but a logo is not
   obviously clickable, and it gives a reader nothing to recognise, read aloud
   or type into a phone. This line does all three. It carries the same href,
   rel and target as the panel, so a referral link stays declared as one
   whichever of the two routes a visitor takes.

   Specificity is ".partner .partner-url", not ".partner-url", because
   ".partner p" above would otherwise win on the margin and the size.

   DELIBERATELY NOT TAGGED FOR /admin, and it cannot be. The editor replaces a
   tagged element's contents with escaped plain text, so the anchor would be
   deleted on the first save and the undo refused; tools/check-editable.js
   fails the build on exactly that. A partner's address changes about as often
   as the commission disclosure does, so needing a developer for it is the
   right amount of friction rather than a gap. */
.partner .partner-url {
  margin: 0.75rem 0 0;
  font-size: 0.9rem;
  /* Domains are single unbreakable words. Without this, a long one widens the
     card past its grid track on a narrow screen instead of wrapping. */
  overflow-wrap: anywhere;
}

/* Founder biography */
.founder-bio {
  display: grid;
  grid-template-columns: minmax(220px, 340px) 1fr;
  gap: 2.5rem;
  align-items: start;
}

.founder-bio .signoff {
  font-weight: 600;
  margin-bottom: 0;
}

.founder-bio .signoff span {
  display: block;
  font-weight: 400;
  color: var(--muted);
}

@media (max-width: 700px) {
  .founder-bio {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
}

/* Licence note */
/* A single paragraph lifted out of the flow because it qualifies everything
   around it. .licence-note is the accreditation logo licence on /accreditation;
   .policy-note is the "this does not replace your own route" paragraph that
   appears on /safeguarding-policy and /accreditation-policy. Same treatment,
   two names, because a reader of either page should be able to tell what the
   class is doing there. */
.licence-note,
.policy-note {
  margin-top: 2rem;
  padding: 1.1rem 1.35rem;
  border-left: 4px solid var(--green);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--surface);
  font-size: 0.95rem;
}

/* Long-form policy pages. Narrower measure than the marketing sections,
   because policy text is read line by line rather than scanned. */
.prose {
  max-width: 46rem;
}

.prose > *:first-child {
  margin-top: 0;
}

.prose h2 {
  font-size: 1.3rem;
  margin: 2.75rem 0 0.75rem;
}

.prose h3 {
  font-size: 1.05rem;
  margin: 1.85rem 0 0.5rem;
}

.prose ul,
.prose ol {
  padding-left: 1.3rem;
  margin: 0.75rem 0;
}

.prose li {
  margin-bottom: 0.45rem;
}

.prose li::marker {
  color: var(--accent);
}

/* "Last updated" line under a policy h1 */
.policy-meta {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 2rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}

/* Contact details block at the foot of a policy */
.contact-block {
  margin: 1rem 0 0;
  padding: 1.25rem 1.35rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--green);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.contact-block dt {
  font-weight: 700;
  color: var(--heading);
  margin-top: 0.6rem;
}

.contact-block dt:first-child {
  margin-top: 0;
}

.contact-block dd {
  margin: 0.15rem 0 0;
}

/* Testimonials */
.testimonials {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.25rem;
  list-style: none;
  padding: 0;
  margin: 2rem 0 0;
  align-items: start;
}

.testimonial {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.5rem;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.testimonial:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}

.testimonial blockquote {
  margin: 0;
  font-size: 1.02rem;
}

.testimonial blockquote p {
  margin: 0;
}

/* Decorative opening quote mark, hidden from assistive tech */
.testimonial blockquote::before {
  content: "\201C";
  display: block;
  font-size: 2.75rem;
  line-height: 0.8;
  color: var(--green);
  margin-bottom: 0.35rem;
}

.testimonial .rating {
  display: block;
  margin-top: 1rem;
  color: var(--eyebrow);
  font-size: 0.9rem;
  letter-spacing: 0.12em;
}

@media (max-width: 720px) {
  .testimonials {
    grid-template-columns: 1fr;
  }
}

/* Booking routes on /appointments */
.booking-routes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  list-style: none;
  padding: 0;
  margin: 1.75rem 0 0;
  align-items: start;
}

.booking-routes li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 4px solid var(--purple);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.5rem;
}

.booking-routes h3 {
  margin: 0 0 0.4rem;
  font-size: 1.05rem;
}

.booking-routes p {
  margin: 0 0 0.35rem;
  color: var(--muted);
  font-size: 0.95rem;
}

.booking-routes a {
  font-weight: 600;
  word-break: break-word;
}

/* ---------------------------------------------------------------------------
   Footer

   Three columns on desktop, one column below 760px. Brand and statutory detail,
   then navigation, then contact.

   Footer links carry no resting underline. This is standalone navigation in a
   landmark of its own, not a link buried in a run of body text, so WCAG 1.4.1
   does not require a non-colour cue at rest: there is no surrounding sentence to
   distinguish them from. They underline on hover and on keyboard focus, and the
   site-wide 2px focus ring still applies. Do not remove that ring.
   --------------------------------------------------------------------------- */
.site-footer {
  /* Brand sweep painted as a 4px band on top of the surface colour */
  background:
    var(--brand-sweep) top center / 100% 4px no-repeat,
    var(--surface);
  border-top: none;
  margin-top: 3rem;
  padding: 3rem 1.5rem 1.5rem;
  color: var(--muted);
  font-size: 0.9rem;
}

/* Four columns: brand, Explore, Policies, Contact. The Policies column arrived
   when the seven policy documents each got a page of their own, which is more
   links than the one-line legal row at the very bottom could carry. */
.footer-grid {
  max-width: var(--max-width);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.5fr 0.9fr 1.1fr 1.2fr;
  gap: 2.5rem 2.5rem;
  align-items: start;
}

/* Between the desktop four-up and the single mobile column, two columns keeps
   the link lists from squeezing to two words a line. */
@media (max-width: 1020px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Everything except the call to action, which keeps the button colours. The
   :not() matters: .site-footer a is more specific than .btn and would otherwise
   repaint the button's text in the accent colour. */
.site-footer a:not(.btn) {
  color: var(--accent);
  text-decoration: none;
  /* The email address is one long unbreakable token; without this it pushes a
     380px viewport sideways. */
  overflow-wrap: anywhere;
}

.site-footer a:not(.btn):hover,
.site-footer a:not(.btn):focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.footer-mark {
  display: block;
  width: 42px;
  height: auto;
  margin-bottom: 1rem;
}

.footer-heading {
  margin: 0 0 0.85rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--heading);
}

.footer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
}

/* Booking is an action, not a destination in a list, so it is separated from the
   links and carries the button treatment used everywhere else on the site. */
.footer-cta {
  margin-top: 1.5rem;
}

/* Company number, registered office and ICO reference. Small print by
   convention, but it still has to clear 4.5:1 like any other body text.
   --muted measures 6.5:1 on --surface in light and 6.8:1 in dark, so it
   passes at this size. Don't drop the opacity to make it recede. */
.footer-legal {
  max-width: 34rem;
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--muted);
}

/* Bottom bar, quieter than the rest of the footer: smaller, and the legal links
   step down from accent purple to --muted. That is 6.5:1 on the light surface
   and 6.8:1 on the dark one, so quieter without dropping below AA. Hover and
   focus lift them back to the accent colour. */
.footer-bottom {
  max-width: var(--max-width);
  margin: 2.5rem auto 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.4rem 1.5rem;
  font-size: 0.8rem;
}

.footer-bottom p {
  margin: 0;
}

.site-footer .footer-bottom a {
  color: var(--muted);
}

.site-footer .footer-bottom a:hover,
.site-footer .footer-bottom a:focus-visible {
  color: var(--accent);
}

@media (max-width: 760px) {
  .footer-grid {
    grid-template-columns: 1fr;
    gap: 2rem;
  }

  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }
}

/* ---------------------------------------------------------------------------
   Brand swirl watermark

   The mark from the primary logo, traced to assets/swirl.svg and used as a
   section watermark. One per page at most.

   It is a CSS background rather than an <img> on purpose: it carries no
   information, so it should not appear in the accessibility tree, and it must
   not appear in /admin as something swappable for a photograph.

   Opacity is low enough that text sitting over it still clears WCAG AA. Measured
   worst cases, text over the darkest point of the mark:
     light theme  body #3b3b3b 10.2:1, muted #5d5566 6.5:1
     dark theme   body #ece7f0 13.9:1, muted #a9a0b3 6.8:1
   Raising these opacities without re-measuring would eat that margin.
   --------------------------------------------------------------------------- */
.section-swirl {
  position: relative;
  /* Own stacking context, so the -1 below sits behind this section's content
     but still in front of the body wash rather than disappearing under it. */
  isolation: isolate;
}

.section-swirl::before {
  content: "";
  position: absolute;
  /* Pinned inside the padding box. Bleeding it off the edge would widen the
     document and put a horizontal scrollbar on narrow screens. */
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: min(26rem, 46%);
  aspect-ratio: 149.93 / 200;
  background: url("assets/swirl.svg") no-repeat center / contain;
  opacity: 0.06;
  z-index: -1;
  pointer-events: none;
}

:root[data-theme="dark"] .section-swirl::before {
  /* The green half of the mark is far brighter than the dark ground, so the
     same opacity reads considerably stronger here. */
  opacity: 0.05;
}

/* ---------------------------------------------------------------------------
   Breathing room in long-form copy

   Typographic, not decorative: existing tokens only, no new colours.
   --------------------------------------------------------------------------- */

/* Lifts one sentence out of a long run of paragraphs. */
.pull-quote {
  position: relative;
  margin: 2.75rem 0;
  padding-left: 1.75rem;
  font-size: clamp(1.15rem, 1.05rem + 0.5vw, 1.4rem);
  line-height: 1.45;
  font-weight: 500;
  color: var(--heading);
  text-wrap: balance;
}

.pull-quote::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.15em;
  bottom: 0.15em;
  width: 4px;
  border-radius: 2px;
  background: var(--brand-sweep);
}

/* A single figure pulled out of the copy. Not a chart, just a full stop. */
.stat-callout {
  display: grid;
  gap: 0.3rem;
  margin: 2.5rem 0;
  padding: 1.5rem 1.65rem;
  background: var(--surface);
  border: 1px solid var(--border);
  /* Green is a UI colour here, not text. 3.95:1 on white, 4.69:1 on the dark
     ground, both clear of the 3:1 threshold for a component edge. */
  border-left: 5px solid var(--green);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  box-shadow: var(--shadow-sm);
}

.stat-callout .figure {
  font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--heading);
}

.stat-callout .figure-label {
  color: var(--muted);
  font-size: 0.98rem;
}

/* Bordered aside for a point that deserves separating from the flow. */
.callout {
  margin: 2.5rem 0;
  padding: 1.35rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* Size is left alone, so a heading reads the same inside a callout as it does
   anywhere else on the page. */
.callout h3 {
  margin: 0 0 0.5rem;
}

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

/* ---------------------------------------------------------------------------
   Common questions

   Plain headings and paragraphs, deliberately not an accordion. Each question
   is also marked up as FAQPage structured data, and marked-up content has to be
   visible on the page, so nothing here may depend on JavaScript to be readable.
   See CLAUDE.md RULE 1 and RULE 2.

   Colours are the ordinary heading and body tokens, so both themes hold the
   same contrast the rest of the page does. Nothing here introduces a new one.
   --------------------------------------------------------------------------- */
.faq {
  margin-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.faq-item {
  padding: 1.4rem 0;
  border-bottom: 1px solid var(--border);
}

.faq-item h3 {
  margin: 0 0 0.5rem;
  font-size: 1.15rem;
}

.faq-item p {
  margin: 0;
  max-width: 46rem;
  text-wrap: pretty;
}

/* A citation under a single answer, where that answer states a dated regulatory
   requirement. The link is a sibling of the tagged answer rather than a child of
   it, because a save through /admin replaces a tagged element's contents with
   plain text and would delete the URL. That is the same split the sources list
   on the insights article uses, and the reason this needs its own rule at all:
   .faq-item p zeroes the margin, so a second paragraph would otherwise sit
   flush against the answer above it. The selector is qualified for the same
   reason: .faq-item p is the more specific of the two and would win. See
   docs/insights.md. */
.faq-item .faq-source {
  margin-top: 0.6rem;
  font-size: 0.9rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* Sits below the questions on the pages whose answers state law, mirroring the
   disclaimer that closes each legal reality box. */
.faq-note {
  margin-top: 1.5rem;
  font-style: italic;
  color: var(--muted);
}

/* ---------------------------------------------------------------------------
   Photography slots

   Tagged and sized for photographs that have not been taken yet. Each one shows
   assets/photo-placeholder.svg until a real file is uploaded through /admin.

   The box is held open by aspect-ratio rather than by the img width and height
   attributes, because api/_lib/admin.js strips those attributes when it swaps
   in a new picture. Sizing the slot in CSS means the first upload changes the
   picture and nothing else on the page moves.
   --------------------------------------------------------------------------- */
.photo-slot {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  background: var(--surface);
}

/* 16:9, matching the supplied photographs, so object-fit: cover has nothing to
   crop. The earlier 16:7 banner shape was chosen when the slot held a flat
   placeholder; against real photographs it removed 11% off the top and bottom,
   which cut through the head of the subject in the contractors shot. */
.photo-slot-wide {
  aspect-ratio: 16 / 9;
}

.photo-slot-card {
  aspect-ratio: 4 / 3;
}

/* Full-width band directly beneath a hero. Shallower than a normal section,
   because it holds one picture rather than a block of copy. */
.photo-band {
  padding-block: 2.5rem;
}

.photo-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1.25rem;
  margin: 2.5rem 0;
}

@media (max-width: 720px) {
  .photo-band {
    padding-block: 1.75rem;
  }
}

/* ---------------------------------------------------------------------------
   Insights

   The article section. Two layouts: the index, which is a stack of linked
   cards, and the article itself, which is a single column of flat headings and
   paragraphs.

   Everything an article is made of is a separate editable element, so the
   markup is deliberately shallow: no wrapper carries meaning, and no paragraph
   contains a link. See docs/insights.md for why, and CLAUDE.md RULE 1 for what
   depends on it. Nothing here needs JavaScript to be readable.
   --------------------------------------------------------------------------- */
.insight-list {
  display: grid;
  gap: 1.25rem;
  margin-top: 2.5rem;
}

/* The whole card is the link, matching .audience-card on the homepage, so the
   title text sits inside the anchor where it counts for search, and the click
   target is the card rather than one line of it. */
.insight-card {
  display: block;
  padding: 1.6rem 1.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--purple-light);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    border-color var(--dur-fast) ease;
}

.insight-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-left-color: var(--purple);
}

.insight-card:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.insight-card h2 {
  margin: 0 0 0.5rem;
  font-size: 1.3rem;
  color: var(--heading);
}

.insight-card p {
  margin: 0;
  color: var(--text);
  max-width: 46rem;
  text-wrap: pretty;
}

/* Publication date. Sits above the title so the list reads as dated content,
   and is muted because the title is what a reader is scanning for. */
.insight-date {
  display: block;
  margin-bottom: 0.4rem;
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.insight-card .go {
  display: inline-block;
  margin-top: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  transition: transform var(--dur) var(--ease);
}

.insight-card:hover .go,
.insight-card:focus-visible .go {
  transform: translateX(4px);
}

/* The article itself. A narrower measure than a marketing section, for the
   same reason as .prose: this is read line by line, not scanned. */
.article {
  max-width: 44rem;
}

.article h1 {
  font-size: clamp(1.9rem, 1.2rem + 2.6vw, 2.7rem);
  margin: 0 0 1.1rem;
}

/* The line naming who the article is for. Larger than body text, because a
   reader decides from this sentence alone whether to keep going. */
.article-standfirst {
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.2rem);
  color: var(--muted);
  margin: 0 0 1.5rem;
  text-wrap: pretty;
}

.article-meta {
  margin: 0 0 2.5rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
  color: var(--muted);
}

/* Section headings within an article. Smaller than a marketing section h2,
   and given back the top margin that .section h2 removes, because these sit
   partway down a page rather than at the top of a band. */
.article h2 {
  font-size: 1.5rem;
  margin: 3.25rem 0 1rem;
}

/* Each sub-question. Spaced generously above so the question reads as the
   start of a new answer rather than a continuation of the previous one. */
.article h3 {
  font-size: 1.25rem;
  margin: 2.75rem 0 0.75rem;
}

.article p {
  max-width: 44rem;
  text-wrap: pretty;
}

/* The answer directly under the h1, which has to stand alone when quoted. */
.article-answer {
  font-size: 1.08rem;
  padding: 1.35rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--green);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  margin: 0 0 2rem;
}

/* Sources. The reference name is an editable element and the link beside it is
   not, so a save can never rewrite a citation URL. That split is what the
   layout here is arranged around: name on its own line, link beneath it. */
.sources {
  margin: 1.5rem 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--border);
}

.sources li {
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--border);
}

.sources cite {
  display: block;
  font-style: normal;
  font-weight: 600;
  color: var(--text);
}

.sources .source-note {
  display: block;
  margin: 0.2rem 0 0.35rem;
  color: var(--muted);
  font-size: 0.95rem;
}

/* Long legislation URLs, kept from forcing the page wider than the viewport. */
.sources a {
  font-size: 0.9rem;
  overflow-wrap: anywhere;
}

/* Closing block. The prose is editable and the links beneath it are not, the
   same split as the sources list and for the same reason. */
.article-next {
  margin: 2.5rem 0 0;
  padding: 1.35rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.article-next > :first-child { margin-top: 0; }

.article-next ul {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
}

.article-next a {
  font-weight: 600;
}

/* The date an article was reviewed, and the not-legal-advice line, both of
   which matter more on a page stating law than they do elsewhere. */
.article-disclaimer {
  margin-top: 2.5rem;
  font-style: italic;
  color: var(--muted);
}

@media (max-width: 720px) {
  .insight-card {
    padding: 1.35rem 1.25rem;
  }
}

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

   Gated behind .js, which an inline snippet in each <head> adds before first
   paint. Without JavaScript the rule below never matches, so every element
   renders in its normal visible state. Nothing here carries content.
   See CLAUDE.md RULE 1.
   --------------------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}

.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* ---------------------------------------------------------------------------
   Reduced motion

   WCAG 2.1 AA. Everything above is decoration, so this switches all of it off
   rather than merely shortening it: no transforms, no fades, no smooth scroll.
   Revealed elements are forced visible in case the observer never fires.
   --------------------------------------------------------------------------- */
@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;
  }

  .js [data-reveal] {
    opacity: 1;
    transform: none;
  }

  .js .hero > * {
    animation: none;
  }

  .rule {
    transform: none;
  }

  /* Hover lifts and slides removed outright */
  .tier:hover,
  .process li:hover,
  .solutions li:hover,
  .values li:hover,
  .partner:hover,
  .level-block:hover,
  .audience-card:hover,
  .audience-card:focus-visible,
  .insight-card:hover,
  .insight-card:focus-visible,
  .btn:hover,
  .btn:focus-visible,
  .brand:hover,
  .theme-toggle:hover {
    transform: none;
  }

  .audience-card:hover .go,
  .audience-card:focus-visible .go,
  .insight-card:hover .go,
  .insight-card:focus-visible .go,
  .partner:hover .logo-panel img,
  .process li:hover::before {
    transform: none;
  }

  /* Accent bars stay in their resting state rather than animating open */
  .audience-card::before,
  .solutions li::before {
    transition: none;
  }
}
