/* ==========================================================================
   REPULSE UK — SECTIONS
   ========================================================================== */

/* --------------------------------------------------------------------------
   HEADER
   The header starts transparent over the dark video hero and turns solid on
   scroll, so the video reads full-bleed rather than boxed in under a bar.
   There is no utility bar above it: it repeated the nav and cost 40px of
   every first viewport.
   -------------------------------------------------------------------------- */
.header {
  position: sticky; top: 0; z-index: var(--z-header);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--d-base) var(--e-both), border-color var(--d-base) var(--e-both);
}
.repulse-global-header-shell { position: sticky; top: 0; z-index: var(--z-header); }
.repulse-global-header-shell .header { position: relative; }
.repulse-global-header-shell .elementor-widget-container,
.repulse-global-header-shell .elementor-element { margin: 0; }
.drawer__actions { margin-top: 2rem; display: grid; gap: .75rem; }

/* Astra's blue square duplicates the branded scroll progress indicator. */
#ast-scroll-top { display: none !important; }

/* Elementor can replace widget markup after the reveal observer has run.
   Keep every section visible in the editor canvas so blocks never look blank. */
body.elementor-editor-active .reveal,
body.elementor-editor-preview .reveal,
.elementor-editor-active .reveal,
.elementor-editor-preview .reveal {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
  will-change: auto !important;
}
.header__inner { display: flex; align-items: center; gap: clamp(1rem, 0.5rem + 1vw, 1.5rem); min-height: 74px; }

/* Solid once scrolled, or immediately on pages without a dark hero */
.header.is-stuck,
body:not(.has-dark-hero) .header {
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom-color: var(--line);
}

.logo { display: flex; align-items: center; flex: 0 0 auto; min-height: 48px; }
.logo__img { height: 32px; width: auto; transition: opacity var(--d-base) var(--e-both); }
@media (min-width: 640px) { .logo__img { height: 36px; } }
/* Over the dark hero the logo shows its light knockout version */
.logo__img--light { display: none; }
body.has-dark-hero .header:not(.is-stuck) .logo__img--dark { display: none; }
body.has-dark-hero .header:not(.is-stuck) .logo__img--light { display: block; }

/* ---- Nav ---- */
.nav { display: none; margin-left: auto; }
@media (min-width: 1120px) { .nav { display: block; } }
.nav__list { display: flex; align-items: center; gap: 2px; }

.nav__link {
  position: relative; display: flex; align-items: center; gap: 4px;
  min-height: 44px; padding-block: var(--sp-2);
  padding-inline: clamp(0.5rem, 0.1rem + 0.5vw, 0.75rem);
  font-size: var(--fs-nav); font-weight: 600;
  color: var(--t-strong); white-space: nowrap;
  border-radius: var(--r-ctl);
  transition: color var(--d-fast) var(--e-both);
}
.nav__link:hover, .nav__link[aria-current='page'] { color: var(--violet); }
body.has-dark-hero .header:not(.is-stuck) .nav__link { color: rgba(255,255,255,.86); }
body.has-dark-hero .header:not(.is-stuck) .nav__link:hover,
body.has-dark-hero .header:not(.is-stuck) .nav__link[aria-current='page'] { color: #fff; }
.nav__chev { width: 13px; height: 13px; transition: transform var(--d-fast) var(--e-both); }
.nav__item.is-open .nav__chev { transform: rotate(180deg); }

/* ---- Mega menu ---- */
.nav__item { position: relative; }
.mega {
  position: absolute; top: calc(100% + 8px); left: 50%;
  width: min(840px, 92vw); padding: var(--sp-6);
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--r-card); box-shadow: var(--sh-2);
  opacity: 0; visibility: hidden; transform: translate(-50%, -8px);
  transition: opacity var(--d-base) var(--e-out), transform var(--d-base) var(--e-out), visibility var(--d-base);
  z-index: var(--z-dropdown);
}
.nav__item.is-open .mega { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.mega__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px var(--sp-4); }
.mega__link {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: 46px; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-ctl); color: var(--t-strong); font-size: var(--fs-sm); font-weight: 500;
  transition: background var(--d-fast) var(--e-both), color var(--d-fast) var(--e-both);
}
.mega__link:hover { background: var(--wash); color: var(--violet); }
.mega__link svg { width: 18px; height: 18px; stroke-width: 1.6; color: var(--violet); }
.mega__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  margin-top: var(--sp-5); padding-top: var(--sp-5);
  border-top: 1px solid var(--line); font-size: var(--fs-sm); color: var(--t-muted);
}

/* ---- Header actions ---- */
.header__actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
@media (min-width: 1120px) { .header__actions { margin-left: var(--sp-4); } }

.header__phone {
  display: none; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding-inline: var(--sp-2);
  font-size: var(--fs-nav); font-weight: 700; color: var(--t-strong); white-space: nowrap;
}
.header__phone svg { width: 1em; height: 1em; color: var(--violet); }
/* Restated for hover; see the a:hover note in tokens.css. */
.header__phone:hover, .header__phone:focus { color: var(--t-strong); }
body.has-dark-hero .header:not(.is-stuck) .header__phone { color: #fff; }
body.has-dark-hero .header:not(.is-stuck) .header__phone svg { color: var(--violet-bright); }
@media (min-width: 780px) { .header__phone { display: inline-flex; } }

.header__cta { display: none; font-size: var(--fs-nav); }
@media (min-width: 560px) { .header__cta { display: inline-flex; } }

/* ---- Burger ---- */
.burger {
  display: grid; place-items: center; width: 46px; height: 46px;
  padding: 0;
  background: transparent; border: 1px solid var(--line-2);
  border-radius: var(--r-ctl); cursor: pointer; color: var(--t-strong);
}
body.has-dark-hero .header:not(.is-stuck) .burger { border-color: rgba(255,255,255,.3); color: #fff; }
@media (min-width: 1120px) { .burger { display: none; } }
.burger__bars { position: relative; width: 18px; height: 12px; }
.burger__bars span {
  position: absolute; top: 5px; left: 0; width: 100%; height: 2px;
  background: currentColor; border-radius: 2px;
  transition: transform var(--d-base) var(--e-out), opacity var(--d-fast) var(--e-both);
}
.burger__bars span:nth-child(1) { transform: translateY(-5px); }
.burger__bars span:nth-child(3) { transform: translateY(5px); }
.burger[aria-expanded='true'] .burger__bars span:nth-child(1) { transform: translateY(0) rotate(45deg); }
.burger[aria-expanded='true'] .burger__bars span:nth-child(2) { opacity: 0; }
.burger[aria-expanded='true'] .burger__bars span:nth-child(3) { transform: translateY(0) rotate(-45deg); }

/* ---- Mobile drawer ---- */
.drawer {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--ink); color: #fff;
  display: flex; flex-direction: column;
  transform: translateX(100%); visibility: hidden;
  transition: transform var(--d-slow) var(--e-out), visibility var(--d-slow);
  overflow-y: auto; overscroll-behavior: contain;
}
.drawer.is-open { transform: none; visibility: visible; }
@media (min-width: 1120px) { .drawer { display: none; } }
.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 74px; padding-inline: var(--gutter);
  border-bottom: 1px solid rgba(255,255,255,.1);
  position: sticky; top: 0; background: var(--ink); z-index: 2;
}
.drawer__close {
  width: 46px; height: 46px; display: grid; place-items: center;
  padding: 0;
  background: transparent; border: 1px solid rgba(255,255,255,.24);
  border-radius: var(--r-ctl); color: #fff; cursor: pointer;
}
.drawer__close svg { width: 18px; height: 18px; }
.drawer__body { padding: var(--sp-6) var(--gutter) var(--sp-16); flex: 1; }
.drawer__link {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  min-height: 58px; padding: var(--sp-4) 0;
  border-bottom: 1px solid rgba(255,255,255,.1);
  font-size: var(--fs-xl); font-weight: 600; color: #fff;
}
.drawer__link svg { width: 17px; height: 17px; color: var(--violet-bright); }
.drawer__link:hover, .drawer__link:focus { color: #fff; }
.drawer__accordion > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  min-height: 58px; padding: var(--sp-4) 0;
  border-bottom: 1px solid rgba(255,255,255,.1);
  font-size: var(--fs-xl); font-weight: 600; color: #fff; cursor: pointer; list-style: none;
}
.drawer__accordion > summary::-webkit-details-marker { display: none; }
.drawer__accordion > summary svg { width: 17px; height: 17px; color: var(--violet-bright); transition: transform var(--d-base) var(--e-out); }
.drawer__accordion[open] > summary svg { transform: rotate(180deg); }
.drawer__sub { padding: var(--sp-2) 0 var(--sp-4); }
.drawer__sub a {
  display: flex; align-items: center; min-height: 48px;
  padding: var(--sp-2) var(--sp-4);
  color: var(--t-on-dark-body); font-size: var(--fs-sm);
  border-left: 1px solid rgba(255,255,255,.14);
}
.drawer__sub a:hover { color: #fff; border-left-color: var(--violet-bright); }

/* ==========================================================================
   HERO — full-bleed video, dark, unmistakable
   ========================================================================== */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--night);
  color: var(--t-on-dark-body);
  /* Pull up under the transparent header so the video runs to the very top */
  margin-top: -74px; padding-top: 74px;
  min-height: min(880px, 100svh);
  display: flex; align-items: center;
  padding-bottom: clamp(3rem, 2rem + 4vw, 5rem);
}

.hero__media { position: absolute; inset: 0; z-index: -3; overflow: hidden; background: var(--night); }
.hero__video {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 42%;
  /* Keep some of the footage's own contrast — this should read as film, not
     as a texture. Slight desaturation only, so brand violet stays dominant. */
  filter: grayscale(.18) contrast(1.04) brightness(.94);
  transform: scale(1.06);
  will-change: transform;
}

/* Shown until assets/video/hero-loop.mp4 exists */
.hero__fallback {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 90% at 78% 12%, rgba(147,0,211,.34) 0%, transparent 58%),
    radial-gradient(90% 80% at 8% 88%, rgba(69,20,101,.6) 0%, transparent 62%),
    linear-gradient(160deg, #170E24 0%, #0D0D14 48%, #08080D 100%);
}
.hero__fallback::after {
  content: ''; position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(147,0,211,.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(147,0,211,.10) 1px, transparent 1px);
  background-size: 66px 66px;
  mask-image: radial-gradient(72% 62% at 42% 44%, #000 0%, transparent 100%);
  -webkit-mask-image: radial-gradient(72% 62% at 42% 44%, #000 0%, transparent 100%);
}

/* Branded scrim: legibility on the left, footage still readable on the right */
.hero__scrim {
  position: absolute; inset: 0; z-index: -2;
  background:
    linear-gradient(94deg, rgba(8,8,13,.88) 0%, rgba(8,8,13,.72) 34%, rgba(8,8,13,.34) 62%, rgba(8,8,13,.18) 100%),
    linear-gradient(180deg, rgba(8,8,13,.55) 0%, transparent 26%, transparent 62%, rgba(8,8,13,.72) 100%),
    linear-gradient(128deg, rgba(147,0,211,.18) 0%, transparent 58%);
}
@media (max-width: 1059px) {
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(8,8,13,.78) 0%, rgba(8,8,13,.62) 45%, rgba(8,8,13,.86) 100%),
      linear-gradient(140deg, rgba(147,0,211,.17) 0%, transparent 62%);
  }
}

.hero__grid { position: relative; display: grid; gap: clamp(2.5rem, 1rem + 4vw, 9rem); width: 100%; align-items: center; }
@media (min-width: 1060px) { .hero__grid { grid-template-columns: minmax(0, 1.1fr) minmax(390px, .9fr); } }

/* The hero gets a wider frame than the rest of the page.
 *
 * Measured on a 2560px screen: the grid was 1920px wide with 320px of dead
 * space down each side, and the two columns sat 64px apart in the middle. So
 * a quarter of the screen carried nothing while the headline and the form
 * were crowded together -- the video ran full-bleed behind them, which made
 * the unused margin more obvious rather than less.
 *
 * The body container stays where it is: it holds prose and card rows, and
 * widening those makes them harder to read. The hero holds neither. It is a
 * headline and a form, both of which are happy further apart.
 *
 * max() rather than a plain value so this can only ever be wider, never
 * narrower, than the standard container at any width. */
@media (min-width: 1600px) {
  .hero__grid.container { max-width: max(var(--container), min(2900px, 88vw)); }
}

.hero__creds { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-6); }
/* One row or nothing. Below 560px a third badge wraps and eats the headline's
   position in the first viewport, so it waits its turn. */
.hero__cred--extra { display: none; }
@media (min-width: 560px) { .hero__cred--extra { display: inline-flex; } }
.hero__title { font-size: var(--fs-display); color: #fff; margin-bottom: var(--sp-5); max-width: 22ch; text-wrap: balance; }
.hero__title-line { display: block; }
.hero__lead { font-size: var(--fs-lg); color: var(--t-on-dark-body); max-width: 50ch; line-height: 1.55; margin-bottom: var(--sp-8); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-8); }
.hero__trust { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6); font-size: var(--fs-sm); color: var(--t-on-dark-muted); }
.hero__trust > span { display: inline-flex; align-items: center; gap: var(--sp-2); }
.hero__trust svg { width: 16px; height: 16px; color: var(--violet-bright); stroke-width: 1.6; }

/* --------------------------------------------------------------------------
   SERVICE BEAM — several services, one contract
   -------------------------------------------------------------------------- */
.beam {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(1.5rem, 0.5rem + 4vw, 4.5rem);
  margin-top: clamp(2.5rem, 2rem + 2vw, 4rem);
  padding: clamp(1.75rem, 1rem + 2.5vw, 3rem) clamp(1rem, 0.5rem + 2vw, 2.5rem);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}

/* Sits under the nodes, never intercepts a pointer. */
/* The global `svg { width; height }` baseline that stops unsized sprite symbols
   rendering enormous also clamps THIS svg to icon size, and an over-constrained
   absolute box resolves in favour of width/height over inset. Say 100% here or
   the beams render into a 20px square. */
.beam__svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none; opacity: 0;
  transition: opacity 600ms var(--e-out);
}
[data-beam-state="ready"] .beam__svg { opacity: 1; }
/* --line-2 on --paper measures barely 1.2:1 — as a hairline it simply vanished.
   A violet-tinted wire reads as wiring without competing with the chips. */
.beam__track { stroke: rgba(147, 0, 211, 0.26); stroke-width: 2; stroke-linecap: round; }
.beam__glow  { stroke-width: 3; stroke-linecap: round; }

/* Chips hug the hub side of their column so the beams have a clean run and the
   diagram reads inward rather than as two stretched stacks. */
.beam__col { display: grid; gap: var(--sp-4); margin: 0; padding: 0; list-style: none; }
.beam__col--left  { justify-items: end; }
.beam__col--right { justify-items: start; }

.beam__node {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: #fff;
  border: 1px solid var(--line-2);
  border-radius: var(--r-ctl);
  box-shadow: var(--sh-1);
  font-size: var(--fs-sm); font-weight: 600; color: var(--t-strong);
  white-space: nowrap;
}
.beam__node-icon { display: grid; place-items: center; width: 20px; height: 20px; flex: 0 0 auto; color: var(--violet); }
.beam__node-icon svg { width: 18px; height: 18px; stroke-width: 1.7; }

.beam__centre { position: relative; z-index: 1; display: grid; justify-items: center; gap: var(--sp-3); text-align: center; }
.beam__hub {
  display: grid; place-items: center;
  width: 84px; height: 84px;
  background: var(--grad-violet);
  border-radius: var(--r-card);
  box-shadow: 0 10px 30px -12px rgba(147, 0, 211, 0.6);
}
/* The real Repulse monogram, knocked out of the plate.

   Masked rather than drawn: assets/logos/repulse-mark.png IS the brand mark,
   so there is one copy of it on disk and no hand-traced approximation to
   drift out of date. `contain` plus centre keeps it optically centred in the
   plate whatever the mark's own aspect ratio is — the previous version was an
   icon-grid glyph that sat high and left inside the square. */
.beam__hub-mark {
  display: block;
  width: 42px; height: 42px;
  background: #fff;
  -webkit-mask: url('../logos/repulse-mark.png') center / contain no-repeat;
          mask: url('../logos/repulse-mark.png') center / contain no-repeat;
}
.beam__hub-label { font-size: var(--fs-sm); font-weight: 700; color: var(--t-strong); line-height: 1.4; }
.beam__hub-label span { font-weight: 500; color: var(--t-muted); }

/* Below the two-column threshold the beams stop being legible as a diagram, so
   the whole thing becomes an honest stacked list and the SVG is dropped. */
@media (max-width: 719px) {
  .beam { grid-template-columns: 1fr; gap: var(--sp-4); }
  .beam__svg { display: none; }
  .beam__col { gap: var(--sp-3); }
  .beam__col--left, .beam__col--right { justify-items: stretch; }
  .beam__node { white-space: normal; }
  .beam__centre { order: -1; }
  .beam__hub { width: 66px; height: 66px; }
  .beam__hub-mark { width: 34px; height: 34px; }
}

/* --------------------------------------------------------------------------
   COVERAGE BAND — the UK dot map and the two real bases
   -------------------------------------------------------------------------- */
/* Two columns from 980px up: the argument on the left, the country on the
   right. Below that it stacks in reading order — copy, proof, action, map —
   which is the order somebody scrolling a phone actually wants them in. */
.coverage-band__split {
  display: grid; gap: clamp(2.25rem, 1rem + 4vw, 4rem);
  grid-template-columns: 1fr;
  align-items: center;
  margin-bottom: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
@media (min-width: 980px) {
  .coverage-band__split { grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr); }
}
.coverage-band__intro .sec-head__title { max-width: 18ch; margin-bottom: var(--sp-5); }
.coverage-band__intro .sec-head__text  { max-width: 54ch; margin-bottom: 0; }

/* Four answers to the four questions asked before the price one. Two up from
   520px; single column below, where two would set four words a line. */
.coverage-band__proof {
  display: grid; gap: var(--sp-5) var(--sp-6);
  grid-template-columns: 1fr;
  margin: clamp(1.75rem, 1rem + 2vw, 2.5rem) 0 0; padding: 0; list-style: none;
}
@media (min-width: 520px) { .coverage-band__proof { grid-template-columns: 1fr 1fr; } }

.coverage-band__proof-item {
  display: grid; gap: var(--sp-2);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--night-line);
}
.coverage-band__proof-icon { display: block; color: var(--violet-bright); }
.coverage-band__proof-icon svg { width: 20px; height: 20px; stroke-width: 1.7; }
.coverage-band__proof-title { font-size: var(--fs-base); font-weight: 700; color: #fff; }
.coverage-band__proof-text  { font-size: var(--fs-sm); color: var(--t-on-dark-muted); line-height: 1.55; }

.coverage-band__actions {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
  margin-top: clamp(1.75rem, 1rem + 2vw, 2.5rem);
}

.coverage-band__map { min-width: 0; }

/* --------------------------------------------------------------------------
   THE MAP

   The stage is deliberately much wider than the country. Everything outside
   the coastline is working space: it is where the leader lines run and where
   the city names live, which is the whole reason this reads as a briefing
   graphic rather than a decorative blob. js/uk-map.js measures .ukmap and
   fits the landmass to the height, capping its width so those margins
   survive. Change the aspect ratios here and the map re-fits itself.
   -------------------------------------------------------------------------- */
.ukmap {
  position: relative;
  width: 100%;
  aspect-ratio: 6 / 5;
  max-height: 82vh;
  /* Reclaim the container gutter on small screens — the labels need every
     pixel of horizontal room they can get, and the map is centred anyway. */
  margin-inline: calc(var(--gutter) * -1);
  width: calc(100% + var(--gutter) * 2);
}
@media (min-width: 560px)  { .ukmap { margin-inline: 0; width: 100%; aspect-ratio: 29 / 20; max-width: 640px; } }

/* From the two-column threshold the map has a column of its own rather than
   the whole container. A portrait stage suits it: the country is 1.6x taller
   than it is wide, so height is what it wants, and the leftover width is
   exactly the margin the leader lines and their chips run in. */
@media (min-width: 980px) {
  .ukmap {
    /* Slightly portrait: the country is 1.3x taller than it is wide, so the
       stage gives it height and spends the leftover width on the margin the
       leader lines and their chips run in. */
    aspect-ratio: 10 / 11;
    max-width: none;
    max-height: min(88vh, 860px);
    margin-inline: auto;
    /* Reclaim the container gutter on the outboard side so the map can run
       wider than its column without pushing the copy around. */
    width: calc(100% + var(--gutter));
    margin-right: calc(var(--gutter) * -1);
  }
}

.ukmap__drift, .ukmap__drift-in { position: absolute; inset: 0; }
.ukmap__drift {
  opacity: 0;
  transition: opacity 900ms var(--e-out);
}
[data-ukmap-state="ready"] .ukmap__drift { opacity: 1; }

.ukmap__canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.ukmap__labels { position: absolute; inset: 0; pointer-events: none; }

/* Two nested drifts on near-co-prime periods, so the composition never
   settles into an obvious loop. Translate only: a scale would resample the
   canvas texture every frame and soften every dot on it. */
@media (prefers-reduced-motion: no-preference) {
  .ukmap__drift    { animation: ukmap-drift-y 19s ease-in-out infinite; }
  .ukmap__drift-in { animation: ukmap-drift-x 27s ease-in-out infinite; }
}
@keyframes ukmap-drift-y {
  0%, 100% { transform: translate3d(0, -5px, 0); }
  50%      { transform: translate3d(0,  5px, 0); }
}
@keyframes ukmap-drift-x {
  0%, 100% { transform: translate3d(-4px, 0, 0); }
  50%      { transform: translate3d( 4px, 0, 0); }
}

.ukmap__label {
  position: absolute; top: 0; left: 0;
  font-size: clamp(0.5625rem, 0.48rem + 0.28vw, 0.8125rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  white-space: nowrap;
  color: #fff;
  background: rgba(180, 75, 238, 0.14);
  border: 1px solid rgba(180, 75, 238, 0.38);
  border-radius: 999px;
  padding: 0.28em 0.62em;
  opacity: 0;
  /* `translate` composes with the `transform` that the script writes for
     centring, so the slide and the anchor never fight over one property. */
  translate: 0 0;
  transition: opacity 420ms var(--e-out), translate 420ms var(--e-out);
}
.ukmap__label[data-side="r"] { translate: -7px 0; }
.ukmap__label[data-side="l"] { translate:  7px 0; }
.ukmap__label.is-on { opacity: 1; translate: 0 0; }

/* The two bases are permanent, sit inside the coastline rather than out in
   the margin, and carry no chip — they are landmarks, not announcements. */
.ukmap__label--base {
  opacity: 1;
  padding: 0; background: none; border: 0;
  font-weight: 700;
  font-size: clamp(0.5625rem, 0.5rem + 0.3vw, 0.875rem);
  color: #fff;
  text-shadow: 0 1px 8px rgba(13, 13, 20, 0.95), 0 0 2px rgba(13, 13, 20, 0.9);
}

/* On a phone the chip's own padding and border are the difference between a
   readable map and a postage stamp: they cost ~24px of the margin the leader
   line has to live in, twice over. Below 560px the name is set as plain text
   on a shadow instead, which buys the country another 15% of width. */
@media (max-width: 559px) {
  .ukmap__label {
    background: none; border: 0; padding: 0;
    text-shadow: 0 1px 6px rgba(13, 13, 20, 0.95), 0 0 3px rgba(13, 13, 20, 0.9);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ukmap__drift, .ukmap__drift-in { animation: none; }
  .ukmap__drift { transition: none; }
  .ukmap__label { transition: none; }
}

/* --------------------------------------------------------------------------
   COVERAGE BAND FOOT
   -------------------------------------------------------------------------- */
.coverage-band__foot {
  margin-top: clamp(1.5rem, 1rem + 2vw, 2.75rem);
  padding-top: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  border-top: 1px solid var(--night-line);
}

.coverage-band__cities-label {
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--tr-kicker);
  color: var(--violet-bright);
  margin-bottom: var(--sp-3);
}
.coverage-band__cities {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-3); padding: 0; list-style: none;
}
.coverage-band__cities li {
  font-size: var(--fs-sm); color: var(--t-on-dark-body);
  padding-right: var(--sp-3);
  border-right: 1px solid var(--night-line);
}
.coverage-band__cities li:last-child { border-right: 0; padding-right: 0; }
.coverage-band__note { font-size: var(--fs-xs); color: var(--t-on-dark-muted); margin: 0; }

@media (max-width: 979px) {
  .coverage-band__intro .sec-head__title { max-width: 22ch; }
}

/* --------------------------------------------------------------------------
   TYPEWRITER
   -------------------------------------------------------------------------- */
.typer { display: flex; align-items: baseline; justify-content: flex-start; color: var(--violet-bright); white-space: nowrap; }
.typer__word { display: inline-block; }
.typer__caret {
  display: inline-block; width: 3px; height: .74em; margin-left: 4px;
  background: var(--violet-bright); transform: translateY(.02em);
}
/* Blinks only while a completed word is held — a 2.1s window, so it never
   runs past the 5s threshold in WCAG 2.2.2 (Pause, Stop, Hide). */
.typer.is-holding .typer__caret { animation: caret-blink 1.06s step-end 2; }
@keyframes caret-blink { 0%,100% { opacity: 1; } 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .typer__caret { display: none; } }

/* --------------------------------------------------------------------------
   HERO QUOTE CARD — solid white panel over the footage
   -------------------------------------------------------------------------- */
/* The hero quote card is hidden on phones. On a narrow screen it pushed the
   headline, the lead and both hero CTAs off the first viewport, so the page
   opened on a form instead of on the pitch. The same form is one tap away
   from the hero's own "Get my free quote" button, which opens the wizard. */
@media (max-width: 899px) {
  .hero .quote-card { display: none; }
}

.quote-card {
  position: relative;
  padding: clamp(1.5rem, 1rem + 1.6vw, 2.25rem);
  background: #fff;
  border-radius: var(--r-card);
  box-shadow: var(--sh-3);
}
.quote-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--grad-violet); border-radius: var(--r-card) var(--r-card) 0 0; }
.quote-card__head { margin-bottom: var(--sp-5); }
.quote-card__title { font-size: var(--fs-h3); margin-bottom: var(--sp-2); }
.quote-card__sub { font-size: var(--fs-sm); color: var(--t-muted); line-height: 1.5; }
.quote-card__row { display: grid; gap: var(--sp-4); }
@media (min-width: 480px) { .quote-card__row--2 { grid-template-columns: 1fr 1fr; } }
.quote-card__foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-4); font-size: var(--fs-xs); color: var(--t-muted); }
.quote-card__foot svg { width: 14px; height: 14px; color: var(--signal); }
.quote-card__switch {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; min-height: 46px; margin-top: var(--sp-4); padding: var(--sp-4) 0 0;
  border: 0; border-top: 1px solid var(--line); background: none;
  color: var(--violet); font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
.quote-card__switch:hover { color: var(--violet-bright); }
.quote-card__switch svg { width: 14px; height: 14px; }

/* ==========================================================================
   ACCREDITATION RAIL
   ========================================================================== */
.accred-band {
  --band-bg: #fff;
  padding-block: var(--sp-8); background: var(--band-bg);
  border-bottom: 1px solid var(--line); overflow: hidden;
  /* Keep the rail's per-frame work inside this box. Without it the browser is
     free to consider layout and paint effects outside the band on every frame
     of a permanently running animation. */
  contain: layout paint;
}
.marquee { position: relative; display: flex; overflow: hidden; }

/* The soft edges used to be a mask-image on this element.
 *
 * A mask applies to the element's composited result, so with a child moving on
 * every single frame the browser had to re-apply a gradient mask across the
 * full width of the rail, forever. At 1280px that is cheap enough to miss. At
 * 2560px it is the difference between smooth and visibly stuttering, which is
 * why the rail felt worse on a large monitor -- the same reason the gap only
 * showed up there.
 *
 * These are two static overlays instead. They are painted once, never change,
 * and sit above the moving tracks rather than filtering them. Absolutely
 * positioned so they are not flex items, and pointer-events:none so they
 * cannot intercept a hover meant to pause the rail. */
.marquee::before,
.marquee::after {
  content: '';
  position: absolute; top: 0; bottom: 0; z-index: 2;
  width: clamp(2rem, 6vw, 7rem);
  pointer-events: none;
}
.marquee::before { left: 0;  background: linear-gradient(90deg,  var(--band-bg, #fff), transparent); }
.marquee::after  { right: 0; background: linear-gradient(270deg, var(--band-bg, #fff), transparent); }
/* Why min-width: 100%.
 *
 * The keyframe moves each track left by translateX(-100%), which is 100% of
 * THAT TRACK'S OWN WIDTH. Two tracks sit side by side, so when track one has
 * travelled its own width, track two has taken its place and the loop repeats.
 *
 * That only reads as seamless while a single track is at least as wide as the
 * screen. If the screen is wider, the moment before the loop restarts there is
 * nothing to the right of track two -- and the empty strip grows as the rail
 * moves. It was invisible on a laptop and obvious on a large monitor, because
 * the fault is the ratio between one track's width and the viewport.
 *
 * min-width: 100% makes every track at least as wide as the rail itself, at
 * any screen size, so the next copy always arrives before the last one leaves.
 * space-around then spreads the logos into whatever slack that creates, and
 * the half-gap of padding on each end means the join between two tracks is
 * exactly one gap wide, like every other gap. */
.marquee__track {
  --marquee-gap: clamp(2.5rem, 1rem + 5vw, 4.5rem);
  display: flex; flex: 0 0 auto; align-items: center;
  justify-content: space-around;
  min-width: 100%;
  gap: var(--marquee-gap);
  padding-inline: calc(var(--marquee-gap) / 2);
  animation: marquee-scroll var(--marquee-duration, 32s) linear infinite;
  will-change: transform;
  backface-visibility: hidden;
}
.marquee:hover .marquee__track, .marquee:focus-within .marquee__track { animation-play-state: paused; }
@keyframes marquee-scroll { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee::before, .marquee::after { display: none; }
  .marquee__track { animation: none; }
}
.accred { display: flex; align-items: center; flex: 0 0 auto; }
.accred__img { height: 52px; width: auto; opacity: .9; transition: opacity var(--d-base) var(--e-both); }
.accred:hover .accred__img { opacity: 1; }

/* ==========================================================================
   TESTIMONIALS — three columns drifting upward

   The whole effect is one transform and one mask. Each track holds its column
   twice and animates to translateY(-50%), which lands the duplicate exactly
   where the original started, so the loop has no seam to hide. The frame is
   masked top and bottom, so a card dissolves as it leaves and resolves as it
   arrives rather than being clipped by an edge.

   Slow on purpose: 78-96 seconds a lap. This is ambient proof, not a carousel
   somebody is meant to keep up with.
   ========================================================================== */
.testimonials { overflow: hidden; }

.tstream {
  display: grid; gap: var(--sp-5);
  grid-template-columns: 1fr;
  /* The frame. Tall enough that two or three cards are always in shot, short
     enough that it never becomes the page. */
  height: clamp(30rem, 26rem + 18vw, 44rem);
  /* Dissolve at both ends. Repeated unprefixed for Safari. */
  mask-image: linear-gradient(180deg, transparent 0%, #000 13%, #000 87%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 13%, #000 87%, transparent 100%);
}
@media (min-width: 640px)  { .tstream { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .tstream { grid-template-columns: repeat(3, 1fr); gap: var(--sp-6); } }

/* A phone gets one column, a tablet two. The hidden columns leave the
   accessibility tree with their frames — the quotes in them are duplicates of
   a type, not unique information a reader is missing out on. */
.tstream__col { position: relative; overflow: hidden; min-width: 0; }
.tstream__col:nth-child(2) { display: none; }
.tstream__col:nth-child(3) { display: none; }
@media (min-width: 640px)  { .tstream__col:nth-child(2) { display: block; } }
@media (min-width: 1000px) { .tstream__col:nth-child(3) { display: block; } }

.tstream__track {
  display: flex; flex-direction: column; gap: var(--sp-4);
  /* The trailing gap is what makes -50% land on the seam instead of half a
     gap short of it. Without it the loop visibly hitches once a lap. */
  padding-bottom: var(--sp-4);
  will-change: transform;
  animation: tstream-up var(--tstream-duration, 84s) linear infinite;
}
.tstream__loop { display: flex; flex-direction: column; gap: var(--sp-4); }
/* The duplicate has to sit in the same flow as the originals or the two halves
   are not the same height and the seam drifts. */
.tstream__loop { margin-bottom: 0; }

@keyframes tstream-up {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -50%, 0); }
}

.tstream:hover .tstream__track,
.tstream:focus-within .tstream__track { animation-play-state: paused; }

/* No motion: the frame becomes an ordinary, scrollable, honest list. */
@media (prefers-reduced-motion: reduce) {
  .tstream {
    height: auto;
    mask-image: none; -webkit-mask-image: none;
  }
  .tstream__col { overflow: visible; }
  .tstream__track { animation: none; padding-bottom: 0; }
  .tstream__loop { display: none; }
}

.tcard {
  margin: 0;
  padding: var(--sp-6);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.tcard__quote {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-base);
  line-height: 1.62;
  color: var(--t-body);
  text-wrap: pretty;
}
/* Literal curly quotes rather than C escapes: the escape has to be
   terminated, and the terminator is invisible in a stylesheet. */
.tcard__quote::before { content: '“'; }
.tcard__quote::after  { content: '”'; }
.tcard__by { display: grid; gap: 2px; padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.tcard__name { font-size: var(--fs-sm); font-weight: 700; color: var(--t-strong); }
.tcard__role { font-size: var(--fs-xs); color: var(--t-muted); }

/* ==========================================================================
   STATS BAND (inside .section--night)
   ========================================================================== */
.stats { display: grid; gap: var(--sp-8); }
@media (min-width: 640px)  { .stats { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .stats { grid-template-columns: repeat(4, 1fr); gap: var(--sp-6); } }
.stats .stat { padding-top: var(--sp-5); border-top: 2px solid rgba(255,255,255,.18); }
.stats .stat:first-child { border-top-color: var(--violet-bright); }

/* ==========================================================================
   SERVICES
   ========================================================================== */
.services-grid { display: grid; gap: var(--sp-4); }
@media (min-width: 620px)  { .services-grid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-5); } }
@media (min-width: 1000px) { .services-grid { grid-template-columns: repeat(3, 1fr); } }

/* Compact list for the remaining services — avoids 14 identical cards */
.service-list { display: grid; gap: 0; border-top: 1px solid var(--line); }
@media (min-width: 700px)  { .service-list { grid-template-columns: repeat(2, 1fr); column-gap: var(--sp-8); } }
@media (min-width: 1040px) { .service-list { grid-template-columns: repeat(3, 1fr); } }
.service-list__item {
  display: flex; align-items: center; gap: var(--sp-4);
  min-height: 68px; padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--line);
  color: var(--t-strong); font-size: var(--fs-sm); font-weight: 600;
  transition: color var(--d-fast) var(--e-both);
}
.service-list__thumb {
  flex: 0 0 auto; display: block;
  width: 46px; height: 46px; overflow: hidden;
  border-radius: var(--r-ctl); background: var(--paper-2);
}
.service-list__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--d-slow) var(--e-out); }
.service-list__item:hover .service-list__thumb img { transform: scale(1.08); }
.service-list__name { min-width: 0; }
.service-list__item .arr { margin-left: auto; width: 15px; height: 15px; color: var(--t-faint); transition: transform var(--d-fast) var(--e-out), color var(--d-fast) var(--e-both); }
.service-list__item:hover { color: var(--violet); }
.service-list__item:hover .arr { transform: translateX(3px); color: var(--violet); }

/* ==========================================================================
   SECTORS — tabbed, with an image panel
   ========================================================================== */
.sector-panel { display: grid; gap: var(--sp-6); align-items: stretch; }
@media (min-width: 900px) { .sector-panel { grid-template-columns: 1fr 1fr; gap: var(--sp-8); } }
.sector-panel__media { position: relative; overflow: hidden; border-radius: var(--r-card); min-height: 280px; background: var(--paper-2); }
.sector-panel__media img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.sector-panel__body { display: flex; flex-direction: column; justify-content: center; }
.sector-panel__title { font-size: var(--fs-h2); margin-bottom: var(--sp-4); }
.sector-panel__text { font-size: var(--fs-lg); color: var(--t-muted); line-height: 1.55; margin-bottom: var(--sp-6); }
.sector-panel__points { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-8); }
.sector-panel__points li { display: flex; gap: var(--sp-3); align-items: flex-start; font-size: var(--fs-sm); color: var(--t-body); }
.sector-panel__points svg { width: 18px; height: 18px; color: var(--violet); margin-top: 3px; stroke-width: 1.8; }

/* ==========================================================================
   PROCESS
   ========================================================================== */
.process { display: grid; gap: var(--sp-6); }
@media (min-width: 700px)  { .process { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .process { grid-template-columns: repeat(4, 1fr); } }
.step { padding-top: var(--sp-5); border-top: 2px solid rgba(255,255,255,.18); }
.step:first-child { border-top-color: var(--violet-bright); }
.step__num { display: block; font-size: var(--fs-sm); font-weight: 700; color: var(--violet-bright); margin-bottom: var(--sp-4); letter-spacing: var(--tr-label); }
.step__title { font-size: var(--fs-h4); color: #fff; margin-bottom: var(--sp-3); }
.step__text { font-size: var(--fs-sm); color: var(--t-on-dark-muted); line-height: 1.6; }

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq { max-width: 820px; border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__item > summary {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
  min-height: 58px; padding: var(--sp-5) 0; cursor: pointer; list-style: none;
  font-size: var(--fs-h4); font-weight: 600; color: var(--t-strong);
  transition: color var(--d-fast) var(--e-both);
}
.faq__item > summary::-webkit-details-marker { display: none; }
.faq__item > summary:hover { color: var(--violet); }
.faq__sign { position: relative; flex: 0 0 auto; width: 20px; height: 20px; margin-top: 6px; }
.faq__sign::before, .faq__sign::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  background: var(--violet); border-radius: 2px;
  transform: translate(-50%, -50%);
  transition: transform var(--d-base) var(--e-out), opacity var(--d-base) var(--e-both);
}
.faq__sign::before { width: 14px; height: 2px; }
.faq__sign::after  { width: 2px; height: 14px; }
.faq__item[open] .faq__sign::after { transform: translate(-50%,-50%) rotate(90deg); opacity: 0; }
.faq__answer { padding-bottom: var(--sp-6); color: var(--t-body); line-height: var(--lh-body); max-width: 68ch; }

/* ==========================================================================
   CTA BAND — full-bleed violet gradient close
   ========================================================================== */
.cta-band { position: relative; overflow: hidden; padding-block: clamp(3.5rem, 2rem + 5vw, 5.5rem); background: var(--grad-violet); color: rgba(255,255,255,.9); }
.cta-band::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(80% 120% at 88% 10%, rgba(255,255,255,.16) 0%, transparent 60%);
  pointer-events: none;
}
.cta-band__inner { position: relative; z-index: 1; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-8); }
.cta-band__title { font-size: var(--fs-h2); color: #fff; max-width: 17ch; margin-bottom: var(--sp-4); }
.cta-band__text { color: rgba(255,255,255,.88); max-width: 46ch; font-size: var(--fs-lg); line-height: 1.5; }
.cta-band__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.cta-band .btn--ghost { --bd: rgba(255,255,255,.5); --fg: #fff; }
.cta-band .btn--ghost:hover { --bd: #fff; --bg: rgba(255,255,255,.12); }

/* ==========================================================================
   POSTS
   ========================================================================== */
.posts { display: grid; gap: var(--sp-5); }
@media (min-width: 700px)  { .posts { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .posts { grid-template-columns: repeat(3, 1fr); } }
.post__meta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-4); font-size: var(--fs-xs); color: var(--t-muted); }
.post__cat { color: var(--violet); font-weight: 700; letter-spacing: var(--tr-label); text-transform: uppercase; }
.post__title { font-size: var(--fs-h4); margin-bottom: var(--sp-3); line-height: 1.25; }
.post__excerpt { font-size: var(--fs-sm); color: var(--t-muted); line-height: 1.6; flex: 1; }

/* ==========================================================================
   CAREERS
   ========================================================================== */
.careers-head__grid {
  display: grid; gap: clamp(2rem, 1rem + 4vw, 3.5rem);
  grid-template-columns: 1fr; align-items: center;
}
@media (min-width: 980px) {
  .careers-head__grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
}
.careers-head .page-head__title { max-width: 20ch; }
.careers-head__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-8); }
.careers-head__points {
  display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6);
  margin: var(--sp-8) 0 0; padding: 0; list-style: none;
  font-size: var(--fs-sm); color: var(--t-on-dark-muted);
}
.careers-head__points li { display: inline-flex; align-items: center; gap: var(--sp-2); }
.careers-head__points svg { width: 16px; height: 16px; color: var(--violet-bright); stroke-width: 2; flex: 0 0 auto; }

/* The picture is a supporting card, not a hero plate — the headline is still
   the loudest thing in the band. */
.careers-head__media {
  position: relative; overflow: hidden;
  border-radius: var(--r-card);
  aspect-ratio: 16 / 10;
  border: 1px solid rgba(255,255,255,.1);
}
.careers-head__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 979px) { .careers-head__media { aspect-ratio: 16 / 7; } }

/* ---- Open roles ---- */
.roles { display: grid; gap: var(--sp-4); }
@media (min-width: 760px)  { .roles { grid-template-columns: repeat(2, 1fr); gap: var(--sp-5); } }

.role {
  display: grid; gap: var(--sp-4);
  grid-template-columns: auto 1fr;
  align-content: start;
  padding: var(--sp-6);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  transition: border-color var(--d-base) var(--e-both), box-shadow var(--d-base) var(--e-both);
}
.role:hover { border-color: var(--line-2); box-shadow: var(--sh-2); }
.role__icon { display: grid; place-items: center; width: 42px; height: 42px; border-radius: var(--r-ctl); background: var(--wash); color: var(--violet); }
.role__icon svg { width: 22px; height: 22px; stroke-width: 1.6; }
.role__body { min-width: 0; }
.role__title { font-size: var(--fs-h4); margin-bottom: var(--sp-2); }
.role__note  { font-size: var(--fs-sm); color: var(--t-muted); line-height: 1.6; }

.role__meta {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  margin: 0; padding: var(--sp-4) 0 0; list-style: none;
  border-top: 1px solid var(--line);
  font-size: var(--fs-xs); color: var(--t-muted);
}
.role__meta li { display: inline-flex; align-items: center; gap: var(--sp-2); }
.role__meta svg { width: 14px; height: 14px; color: var(--violet); stroke-width: 1.8; flex: 0 0 auto; }
.role__cta { grid-column: 1 / -1; justify-self: start; }

/* The "no licence yet" note. Deliberately the loudest block on the page after
   the H1: it is the sentence that unlocks the largest pool of applicants. */
.role-note {
  display: grid; gap: var(--sp-4);
  grid-template-columns: auto 1fr;
  align-items: start;
  margin-top: clamp(1.75rem, 1rem + 2vw, 2.5rem);
  padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  background: var(--grad-wash);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.role-note__icon { display: grid; place-items: center; width: 40px; height: 40px; color: var(--violet); }
.role-note__icon svg { width: 28px; height: 28px; stroke-width: 1.5; }
.role-note__title { font-size: var(--fs-h4); margin-bottom: var(--sp-2); }
.role-note__text  { color: var(--t-body); font-size: var(--fs-sm); line-height: 1.65; max-width: 72ch; }
@media (max-width: 559px) {
  .role-note { grid-template-columns: 1fr; }
}

/* ==========================================================================
   PAGE HEADER (interior)
   ========================================================================== */
.page-head {
  position: relative; overflow: hidden;
  padding-block: clamp(3rem, 2rem + 4vw, 5rem);
  background: var(--grad-night); color: var(--t-on-dark-body);
  margin-top: -74px; padding-top: calc(74px + clamp(3rem, 2rem + 4vw, 5rem));
}
.page-head::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(70% 100% at 88% 0%, rgba(147,0,211,.28) 0%, transparent 62%);
  pointer-events: none;
}
.page-head .container { position: relative; z-index: 1; }
.page-head__title { color: #fff; margin-bottom: var(--sp-5); max-width: 18ch; }
.page-head__lead { font-size: var(--fs-lg); color: var(--t-on-dark-body); max-width: 58ch; line-height: 1.55; }
.breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-6); font-size: var(--fs-xs); color: var(--t-on-dark-muted); }
.breadcrumb a { color: var(--t-on-dark-muted); transition: color var(--d-fast) var(--e-both); }
.breadcrumb a:hover { color: #fff; }
.breadcrumb svg { width: 12px; height: 12px; opacity: .6; }
.breadcrumb [aria-current] { color: #fff; }

/* ==========================================================================
   PROSE + ASIDE
   ========================================================================== */
.prose { max-width: 66ch; }
.prose > * + * { margin-top: var(--sp-5); }
.prose h2 { font-size: var(--fs-h2); margin-top: var(--sp-12); }
.prose h3 { font-size: var(--fs-h3); margin-top: var(--sp-10); }
.prose p { color: var(--t-body); line-height: var(--lh-body); }
.prose ul { display: grid; gap: var(--sp-3); }
.prose ul li { position: relative; padding-left: var(--sp-6); color: var(--t-body); line-height: var(--lh-body); }
.prose ul li::before { content: ''; position: absolute; left: 0; top: .68em; width: 11px; height: 2px; background: var(--violet); border-radius: 2px; }
.prose a { color: var(--violet); text-decoration: underline; text-underline-offset: 3px; }

.with-aside { display: grid; gap: clamp(2.5rem, 1.5rem + 4vw, 4rem); align-items: start; }
@media (min-width: 1040px) { .with-aside { grid-template-columns: minmax(0, 1fr) 380px; } }
.aside-sticky { position: sticky; top: 96px; }

/* ==========================================================================
   FOOTER — dark, closes the page with weight
   ========================================================================== */
.footer { background: var(--ink); color: var(--t-on-dark-body); padding-block: clamp(3rem, 2rem + 3vw, 4.5rem) var(--sp-8); }
.footer__grid { display: grid; gap: var(--sp-10); }
.footer__badges { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.footer__cta { margin-top: var(--sp-6); }
@media (min-width: 720px)  { .footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1060px) { .footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1.2fr; } }
.footer__about { max-width: 38ch; }
.footer__tagline { color: var(--t-on-dark-muted); font-size: var(--fs-sm); line-height: 1.6; margin-block: var(--sp-5); }
.footer__title { font-size: var(--fs-xs); font-weight: 700; letter-spacing: var(--tr-label); text-transform: uppercase; color: #fff; margin-bottom: var(--sp-4); }
.footer__list { display: grid; }
.footer__list a {
  display: flex; align-items: center; min-height: 44px;
  font-size: var(--fs-sm); color: var(--t-on-dark-muted);
  transition: color var(--d-fast) var(--e-both), transform var(--d-fast) var(--e-out);
}
.footer__list a:hover { color: #fff; transform: translateX(4px); }
.footer__contact { display: grid; gap: var(--sp-4); font-size: var(--fs-sm); color: var(--t-on-dark-muted); }
.footer__contact-item { display: flex; gap: var(--sp-3); align-items: flex-start; }
.footer__contact-item svg { width: 17px; height: 17px; color: var(--violet-bright); stroke-width: 1.6; margin-top: 3px; }
.footer__contact a { color: #fff; display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.footer__contact a:hover { color: var(--violet-bright); }
.footer__bottom {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-4);
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem); padding-top: var(--sp-6);
  border-top: 1px solid rgba(255,255,255,.1);
  font-size: var(--fs-xs); color: var(--t-on-dark-muted);
}
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5); }
.footer__legal a { display: inline-flex; align-items: center; min-height: 44px; min-width: 44px; justify-content: center; }
.footer__legal a:hover { color: #fff; }

/* ==========================================================================
   STICKY MOBILE BAR
   ========================================================================== */
.mobile-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky);
  display: grid; grid-template-columns: 1fr 1fr;
  background: var(--ink);
  padding-bottom: env(safe-area-inset-bottom, 0);
  transform: translateY(110%);
  transition: transform var(--d-slow) var(--e-out);
}

/* Fill whatever the browser leaves underneath.
 *
 * A phone browser hides its address bar as you scroll down and brings it back
 * as you scroll up. A position:fixed element is placed against the LAYOUT
 * viewport, which does not resize when that chrome collapses -- so on the way
 * down the visible area grows beneath the bar and a strip of page content
 * appears below it. Scrolling up puts the chrome back and the strip closes,
 * which is why it looks like it only misbehaves in one direction.
 *
 * There is no way to make a fixed element track the visual viewport in CSS
 * alone, and doing it in JavaScript means running code on every scroll frame
 * of a page that already has a sticky header and a canvas map.
 *
 * So the gap is not prevented, it is made invisible: a strip of the same
 * colour hangs below the bar and fills whatever opens up. Absolutely
 * positioned, so it is not a grid item and cannot disturb the two buttons, and
 * pointer-events:none so it can never swallow a tap meant for the page. */
.mobile-bar::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 180px;
  background: var(--ink);
  pointer-events: none;
}
.mobile-bar.is-visible { transform: none; }
@media (min-width: 900px) { .mobile-bar { display: none; } }
.mobile-bar__btn {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: 58px; background: transparent; color: #fff;
  font-size: var(--fs-sm); font-weight: 600; border: 0;
  border-right: 1px solid rgba(255,255,255,.14);
  cursor: pointer; touch-action: manipulation;
}
.mobile-bar__btn svg { width: 17px; height: 17px; }

/* ---------------------------------------------------------------------------
   The bottom-right corner

   Tawk.to's chat launcher and Google's reCAPTCHA badge both park bottom-right,
   where the mobile call bar already is, so the bubble can cover the "Free
   quote" button.

   There WAS a rule here lifting them clear. It has been removed deliberately:
   it worked by overriding another vendor's inline styles, which means reaching
   into markup we do not own and cannot see change. When Tawk alter their DOM
   the selector stops matching, silently, and the overlap returns with nothing
   to indicate why.

   The correct home for this is the vertical offset in the Tawk.to dashboard --
   a setting on their side, applied by them, that survives their updates. See
   the handover notes.
   -------------------------------------------------------------------------- */
.mobile-bar__btn:hover, .mobile-bar__btn:focus { color: #fff; }
.mobile-bar__btn--primary { background: var(--violet); border-right: 0; }
@media (max-width: 899px) { .footer { padding-bottom: calc(var(--sp-8) + 58px + env(safe-area-inset-bottom, 0px)); } }
