/* =========================================================================
   Affordable Web Design Cardiff
   Single stylesheet. Every value below traces to the brand guidelines v1.0.
   Order: tokens, reset, typography, layout, components, sections, motion.
   ========================================================================= */

/* -------------------------------------------------------------------------
   TOKENS · colour
   Five values, no more. Neutrals are derived from navy by opacity, never
   from a new hue. Guidelines p07.
   ------------------------------------------------------------------------- */
:root {
  --navy: #000044;
  --teal: #17B3B0;
  --teal-dark: #02837F;
  --sky: #EDF6FB;
  --white: #FFFFFF;

  /* navy derivations, p07 */
  --navy-raised: #0A0A5C;          /* raised cards on navy */
  --navy-glow: #000A5C;            /* hero radial glow terminus */

  /* derived neutrals, p07 */
  --text-body: rgba(0, 0, 68, 0.82);
  /* 0.58, not 0.55: at 0.55 muted text measured 4.42:1 on the sky ground and
     4.53:1 on white — one side of AA, one side of failing, from the same
     token. 0.58 clears 4.5 on both (4.93 and 5.06). Still navy at an opacity,
     so the five-value rule holds. */
  --text-muted: rgba(0, 0, 68, 0.58);
  --hairline: rgba(0, 0, 68, 0.08);
  --hairline-on-navy: rgba(255, 255, 255, 0.12);
  --panel-on-navy: rgba(255, 255, 255, 0.08);
  --focus-ring: rgba(23, 179, 176, 0.4);

  /* text on navy bands */
  --text-body-on-navy: rgba(255, 255, 255, 0.82);
  --text-muted-on-navy: rgba(255, 255, 255, 0.55);

  /* -----------------------------------------------------------------------
     TOKENS · type
     Montserrat structural, Work Sans reading and UI, Caveat annotation only.
     Desktop values at 1440px clamping to their mobile end at 390px. p09-p10.
     ----------------------------------------------------------------------- */
  --font-display: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Work Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-hand: "Caveat", ui-rounded, cursive;

  --size-h1: clamp(2.5rem, 1.5714rem + 3.8095vw, 5rem);        /* 40 → 80 */
  --size-h2: clamp(2rem, 1.5357rem + 1.9048vw, 3.25rem);       /* 32 → 52 */
  --size-h3: clamp(1.5rem, 1.3143rem + 0.7619vw, 2rem);        /* 24 → 32 */
  --size-h4: clamp(1.25rem, 1.1571rem + 0.381vw, 1.5rem);      /* 20 → 24 */
  --size-stat: clamp(3.5rem, 2.5714rem + 3.8095vw, 6rem);      /* 56 → 96 */
  --size-body-lg: clamp(1.0625rem, 1.0393rem + 0.0952vw, 1.125rem);
  --size-body: 1rem;
  --size-small: 0.875rem;
  --size-eyebrow: 0.75rem;
  --size-micro: 0.6875rem;
  --size-annotation: clamp(1.5rem, 1.3143rem + 0.7619vw, 2rem);

  /* -----------------------------------------------------------------------
     TOKENS · space, grid, radii  p11
     Scale: 4 8 12 16 24 32 48 64 96. Nothing sits between the steps.
     ----------------------------------------------------------------------- */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px;

  --container: 1240px;
  --col-gap: 24px;
  --gutter: 24px;                  /* 24 / 40 / 64 by breakpoint */
  --section-pad: 64px;             /* 64 / 88 / 120 by breakpoint */

  --r-input: 12px;                 /* inputs, small chips */
  --r-card: 16px;                  /* cards */
  --r-panel: 24px;                 /* large feature panels */
  --r-pill: 999px;                 /* pills and buttons */
  --target: 44px;                  /* minimum interactive target, everywhere */
  /* Fluid so it tracks the logo's own clamp() instead of forcing the fixed
     200px desktop height onto a phone, which left a huge empty header band
     above a tiny hamburger icon. */
  --header-h: clamp(64px, 14vw, 200px);  /* header row; the hero clears this. Grows with the logo. */

  /* -----------------------------------------------------------------------
     TOKENS · shadow
     Three steps only, always navy-tinted. No hard drops, no glows. p12
     ----------------------------------------------------------------------- */
  --shadow-1: 0 1px 2px rgba(0, 0, 68, 0.06), 0 1px 3px rgba(0, 0, 68, 0.04);
  --shadow-2: 0 4px 12px rgba(0, 0, 68, 0.08), 0 2px 4px rgba(0, 0, 68, 0.05);
  --shadow-3: 0 12px 32px rgba(0, 0, 68, 0.14), 0 4px 10px rgba(0, 0, 68, 0.07);

  /* TOKENS · motion  p14 */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --t-button: 160ms;
  --t-card: 200ms;
  --t-logo: 200ms;
  --t-accordion: 240ms;
  --t-entrance: 500ms;
}

@media (min-width: 768px)  { :root { --gutter: 40px; --section-pad: 88px; } }
@media (min-width: 1100px) { :root { --gutter: 64px; --section-pad: 120px; } }

/* -------------------------------------------------------------------------
   RESET
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* clip, not hidden: it contains the 720px comparison table without turning the
   root into a scroll container, so anchor jumps and the fixed header are
   untouched. body keeps overflow-x: hidden as the fallback for older engines.
   Without this the table pushed the document 301px wide whenever the entrance
   transform was absent — which is every visit with JavaScript off. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  overflow-x: clip;
}

body {
  font-family: var(--font-body);
  font-size: var(--size-body);
  line-height: 1.6;
  color: var(--text-body);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, picture, svg { display: block; max-width: 100%; }
img { height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }
a { color: inherit; text-decoration: none; }

/* Focus: 3px teal ring at 40%, 2px offset, on every interactive element. p14 */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--s-1);
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: 200;
  background: var(--navy); color: var(--white);
  padding: var(--s-3) var(--s-5); border-radius: var(--r-pill);
  transition: top var(--t-button) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

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

/* -------------------------------------------------------------------------
   TYPOGRAPHY
   Montserrat never sets a paragraph. Work Sans never sets a display heading.
   Sentence case throughout; uppercase only for eyebrows, badges and
   comparison column labels. p09-p10
   ------------------------------------------------------------------------- */
h1, h2, h3, h4, .stat__value {
  font-family: var(--font-display);
  color: var(--navy);
}

/* balance is right for a display heading that gets two or three long lines.
   In a 232px card column it fights the width and strands words on their own
   line ("Designers and developers / in the / same room"). Card-scale headings
   take pretty instead, which only protects the last line from an orphan. */
h1, h2 { text-wrap: balance; }
h3, h4 { text-wrap: pretty; }

h1, .h1 {
  font-size: var(--size-h1); font-weight: 800;
  letter-spacing: -0.03em; line-height: 1.02;
}
h2, .h2 {
  font-size: var(--size-h2); font-weight: 700;
  letter-spacing: -0.02em; line-height: 1.08;
}
h3, .h3 {
  font-size: var(--size-h3); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.2;
}
h4, .h4 {
  font-size: var(--size-h4); font-weight: 600;
  letter-spacing: 0; line-height: 1.3;
}

/* Exactly one word or short phrase per headline carries the accent. p15 */
/* The accent word takes its teal from the ground, not from its size. Bright
   teal on white measured 2.59:1 and on sky 2.36:1 — under the 3:1 large-text
   floor, on the most emphasised type in the system. teal-dark on the light
   grounds is 4.61:1 and 4.21:1, and bright teal stays on navy at 7.49:1.
   Two existing brand values, swapped by context. No sixth colour. */
.accent { color: var(--teal-dark); }
.on-navy .accent { color: var(--teal); }

.lede {
  font-size: var(--size-body-lg); line-height: 1.65;
  max-width: 62ch;                       /* 62 to 68 character measure. p10 */
}
p { max-width: 68ch; }

.small { font-size: var(--size-small); line-height: 1.55; }

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; line-height: 1.4;
  text-transform: uppercase;
  color: var(--teal-dark);               /* teal text ≤16px uses teal-dark. p08 */
  display: block;
}
.on-navy .eyebrow { color: var(--teal); }

.micro {
  font-size: var(--size-micro); font-weight: 600;
  letter-spacing: 0.1em; line-height: 1.4;
  text-transform: uppercase;
}

/* Caveat: lower-case fragments only, three to five per page. p09/p15 */
.annotation {
  font-family: var(--font-hand);
  font-size: var(--size-annotation); font-weight: 400;
  line-height: 1.2; letter-spacing: 0;
  color: var(--teal-dark);
  text-transform: lowercase;
}
.on-navy .annotation { color: var(--teal); }

/* -------------------------------------------------------------------------
   LAYOUT
   One container, one grid, one spacing scale. p11
   ------------------------------------------------------------------------- */
.container {
  width: 100%; max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

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

/* Section rhythm: white, sky, white, navy, white, sky, navy. Two identical
   backgrounds never sit next to each other; sections separate by background
   change and whitespace, never by rules. p08 */
.section--white { background: var(--white); }
.section--sky   { background: var(--sky); }
.section--navy  { background: var(--navy); }
.section--teal  { background: var(--teal); }   /* closing CTA band only. p08 */

/* Light type on a dark ground blooms optically: the same setting reads tighter
   and heavier than it does on white. Compensate on all three axes rather than
   one — a little more leading, a little more tracking, one step of weight on
   the smaller roles. Values stay inside the documented world. */
.on-navy { color: var(--text-body-on-navy); }
/* Not .annotation: Caveat is set at 1.2 deliberately, and this rule outranks
   it on specificity, which stretched the handwritten notes to 1.68. */
.on-navy p:not(.annotation), .on-navy li { line-height: 1.68; letter-spacing: 0.005em; }
.on-navy .small, .on-navy .stat__label, .on-navy .waitlist__note { font-weight: 500; }
.on-navy h1, .on-navy h2, .on-navy h3, .on-navy h4, .on-navy .stat__value { color: var(--white); }
.on-navy .text-muted { color: var(--text-muted-on-navy); }

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

.text-muted { color: var(--text-muted); }

.grid { display: grid; gap: var(--col-gap); }
.section__head { max-width: 46rem; margin-bottom: var(--s-7); }
.section__head > * + * { margin-top: var(--s-4); }
.section__head--center { margin-inline: auto; text-align: center; }
.section__head--center .lede { margin-inline: auto; }

.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-5); }

/* -------------------------------------------------------------------------
   COMPONENTS · buttons  p12
   Primary hover shifts teal to teal-dark and lifts 2px; press drops the lift.
   Secondary on light fills navy and flips text to white; on navy it fills
   white and flips text to navy. Nothing scales down on press and nothing
   changes opacity as a hover state.
   ------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-height: var(--target); padding: var(--s-3) var(--s-5);
  border: 1px solid transparent; border-radius: var(--r-pill);
  font-family: var(--font-body); font-size: var(--size-body); font-weight: 600;
  line-height: 1; cursor: pointer; text-align: center;
  transition: background-color var(--t-button) var(--ease),
              color var(--t-button) var(--ease),
              border-color var(--t-button) var(--ease),
              transform var(--t-button) var(--ease);
}

.btn--primary { background: var(--teal); color: var(--navy); }
.btn--primary:hover { background: var(--teal-dark); color: var(--white); transform: translateY(-2px); }
.btn--primary:active { transform: translateY(0); }

.btn--secondary { background: transparent; color: var(--navy); border-color: var(--navy); }
.btn--secondary:hover { background: var(--navy); color: var(--white); transform: translateY(-2px); }
.btn--secondary:active { transform: translateY(0); }

.on-navy .btn--secondary { color: var(--white); border-color: var(--hairline-on-navy); }
.on-navy .btn--secondary:hover { background: var(--white); color: var(--navy); border-color: var(--white); }

/* On the teal band a teal button would vanish, so primary fills navy. */
.on-teal .btn--primary { background: var(--navy); color: var(--white); }
.on-teal .btn--primary:hover { background: var(--navy-raised); color: var(--white); }
.on-teal .btn--secondary { color: var(--navy); border-color: var(--navy); }
.on-teal .btn--secondary:hover { background: var(--navy); color: var(--white); }

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

/* Pending: the button says what it is doing and stops taking a second press.
   No spinner — the label is the status, which survives reduced motion. */
.btn[aria-busy="true"] { pointer-events: none; }
.btn[aria-busy="true"] .btn__label { opacity: 0.65; }
.btn:disabled { pointer-events: none; opacity: 0.55; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.btn-row--center { justify-content: center; }

/* COMPONENTS · pills and badges — teal is permitted here. p08 */
.pill {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--teal); color: var(--navy);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; line-height: 1.4;
}
.pill--quiet {
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--hairline);
}
.on-navy .pill--quiet { color: var(--text-muted-on-navy); border-color: var(--hairline-on-navy); }

/* Where a client portrait is unavailable, the business name sits in a teal
   chip rather than a placeholder avatar. p13 */
.chip {
  display: inline-flex; align-items: center; justify-content: center;
  padding: var(--s-2) var(--s-3); border-radius: var(--r-input);
  background: var(--teal); color: var(--navy);
  font-size: var(--size-small); font-weight: 600; line-height: 1.2;
}

/* -------------------------------------------------------------------------
   COMPONENTS · cards  p12
   Cards invert against their section: white on sky, sky on white,
   navy-raised on a navy band. Hover lifts 4px, deepens the shadow and
   tints the border teal.
   ------------------------------------------------------------------------- */
.card {
  border-radius: var(--r-card);
  padding: var(--s-6);
  border: 1px solid var(--hairline);
  background: var(--sky);
  box-shadow: var(--shadow-1);
  transition: transform var(--t-card) var(--ease),
              box-shadow var(--t-card) var(--ease),
              border-color var(--t-card) var(--ease);
}
.section--sky .card { background: var(--white); }
.section--navy .card,
.on-navy .card {
  background: var(--navy-raised);
  border-color: var(--hairline-on-navy);
  box-shadow: none;
  color: var(--text-body-on-navy);
}
.on-teal .card { background: var(--white); border-color: transparent; }

.card--interactive:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-3);
  border-color: var(--teal);
}
.on-navy .card--interactive:hover { box-shadow: none; border-color: var(--teal); }

.card--panel { border-radius: var(--r-panel); padding: var(--s-7); }
.card__icon { color: var(--teal); margin-bottom: var(--s-4); }
.card__title { margin-bottom: var(--s-3); }

/* COMPONENTS · icons — outline only, teal or navy, never filled. p13 */
.icon { width: 20px; height: 20px; flex: none; stroke-width: 1.75; }
.icon--tick { width: 18px; height: 18px; color: var(--teal); }
.icon--body { width: 16px; height: 16px; }
.icon--muted { color: rgba(0, 0, 68, 0.45); }
.on-navy .icon--muted { color: rgba(255, 255, 255, 0.45); }

/* COMPONENTS · tick list */
.ticks { display: grid; gap: var(--s-3); }
.ticks li { display: flex; gap: var(--s-3); align-items: flex-start; line-height: 1.5; }
.ticks li > svg { margin-top: 3px; }

/* COMPONENTS · stat  p10 stat numeral 96/800/-0.03em/0.95 */
.stat__value {
  font-size: var(--size-stat); font-weight: 800;
  letter-spacing: -0.03em; line-height: 0.95;
  color: var(--teal);
}
.stat__label {
  margin-top: var(--s-3);
  font-size: var(--size-small); line-height: 1.4;
  color: var(--text-muted-on-navy);
  max-width: 22ch;
}

/* COMPONENTS · form  p11 inputs 12px radius */
.field { display: grid; gap: var(--s-2); }
.field label { font-size: var(--size-small); font-weight: 500; }

.input {
  width: 100%; min-height: var(--target);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-input);
  border: 1px solid var(--hairline-on-navy);
  background: var(--panel-on-navy);
  color: var(--white);
  transition: border-color var(--t-button) var(--ease);
}
.input::placeholder { color: rgba(255, 255, 255, 0.45); }
.input:hover { border-color: rgba(255, 255, 255, 0.24); }

/* The system has no red, so the glyph carries the negative signal — teal alone
   also means "yes" on the tick lists and "act" on the buttons. */
.field__error {
  display: none;
  align-items: flex-start; gap: var(--s-2);
  font-size: var(--size-small); color: var(--teal);
}
.field__error .icon { flex: none; margin-top: 3px; }
.field[data-invalid] .input { border-color: var(--teal); border-width: 2px; }
.field[data-invalid] .field__error { display: flex; }

/* Submit-time failure. Same glyph, same reasoning, one step louder. */
.form-alert {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 2px solid var(--teal); border-radius: var(--r-input);
  background: var(--panel-on-navy);
  font-size: var(--size-small); color: var(--white);
}
.form-alert[hidden] { display: none; }
.form-alert .icon { flex: none; color: var(--teal); margin-top: 2px; }
.form-alert p { margin: 0; max-width: none; }

/* Long emails and business names must wrap rather than widen the card. */
.field, .waitlist, .form-alert { min-width: 0; }
.input, .field__error, .form-alert p { overflow-wrap: anywhere; }

/* Required marker. The asterisk is the visual cue; the `required` attribute
   carries the same fact to assistive tech, so the glyph is aria-hidden. */
.req { color: var(--teal); font-weight: 700; }
.waitlist__required {
  margin: 0; font-size: var(--size-small); color: var(--text-muted-on-navy);
}

/* Select. Native arrow stripped so the control matches the text inputs, with
   the chevron redrawn as a background image. Options are painted by the OS,
   which ignores the dark ground — so they get explicit colours or they render
   white-on-white on Windows. */
select.input {
  appearance: none; -webkit-appearance: none;
  padding-right: calc(var(--s-4) + 20px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-4) center;
  background-size: 16px 16px;
  cursor: pointer;
}
select.input option { color: var(--navy); background: var(--white); }
select.input:invalid, select.input option[value=""] { color: rgba(255, 255, 255, 0.45); }

/* Textarea. Vertical resize only, so a long enquiry cannot widen the card. */
textarea.input--area {
  min-height: 120px; padding-top: var(--s-3); padding-bottom: var(--s-3);
  resize: vertical; line-height: 1.5;
}

/* -------------------------------------------------------------------------
   HEADER
   Translucent over the hero with the dark lockup, then solidifies to white
   on scroll and cross-fades to the light lockup over 200ms. Transparency
   yes, blur no. p05 / p08
   ------------------------------------------------------------------------- */
.header {
  position: fixed; inset: 0 0 auto; z-index: 100;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color var(--t-logo) var(--ease),
              border-color var(--t-logo) var(--ease);
}
.header__inner {
  display: flex; align-items: center; gap: var(--s-4);
  min-height: var(--header-h);
}
/* The header runs wider than the page's own 1240px container, so the logo
   sits closer to the true edge and the nav capsule has room to stay on one
   line instead of wrapping each label onto its own. */
.header .container { max-width: 1600px; }
/* Over the white hero the bar is bare; past the fold it takes a white ground
   so the pills keep their contrast over the navy, sky and teal bands. */
.header.is-solid { background: var(--white); border-bottom-color: var(--hairline); }

/* One lockup: the ground behind it is light in both header states. */
/* Fixed 150px height overflowed the header on phones (the logo alone ran
   wider than a 390px viewport). Fluid between a mobile floor and the
   150px desktop value instead, so it never outgrows the header row. */
.logo { display: block; height: clamp(44px, 13vw, 150px); width: auto; flex: none; min-width: 0; }
.logo img { height: 100%; width: auto; max-width: 100%; }

/* The links ride inside a single navy capsule. */
.nav { margin-left: auto; }
.nav__list {
  display: flex; align-items: center; gap: var(--s-1);
  padding: var(--s-1);
  background: var(--navy);
  border-radius: var(--r-pill);
}
.nav__link {
  display: inline-flex; align-items: center;
  min-height: var(--target); padding-inline: var(--s-4);
  border-radius: var(--r-pill);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
  white-space: nowrap;
  transition: background-color var(--t-button) var(--ease),
              color var(--t-button) var(--ease);
}
.nav__link:hover { background: var(--white); color: var(--navy); }

/* The CTA is a second capsule: navy body, teal badge carrying the arrow. */
.btn--cta {
  flex: none; gap: var(--s-3);
  padding: var(--s-1) var(--s-1) var(--s-1) var(--s-5);
  background: var(--navy); color: var(--white);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.btn--cta:hover { background: var(--navy-raised); transform: translateY(-2px); }
.btn--cta:active { transform: translateY(0); }
.btn__badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--target); height: var(--target); flex: none;
  border-radius: 50%;
  background: var(--teal); color: var(--navy);
}
.btn__badge .icon { width: 18px; height: 18px; }

/* The in-nav CTA is the mobile one; the capsule CTA covers desktop. */
@media (min-width: 900px) { .nav__list .btn { display: none; } }

.nav-toggle {
  display: none; margin-left: auto;
  width: var(--target); height: var(--target); flex: none;
  align-items: center; justify-content: center;
  background: var(--navy); border: 0;
  border-radius: var(--r-pill); color: var(--white); cursor: pointer;
}

@media (max-width: 899px) {
  .nav-toggle { display: inline-flex; }
  .header__inner > .btn { display: none; }
  /* The capsule unpacks into a full-width navy drawer. */
  .nav {
    position: absolute; inset: 100% 0 auto; margin: 0;
    background: var(--navy);
    border-top: 1px solid var(--hairline-on-navy);
    padding: var(--s-4) var(--gutter) var(--s-6);
    display: none;
  }
  .nav.is-open { display: block; }
  .nav__list {
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 0; background: none; border-radius: 0;
  }
  .nav__link { min-height: 52px; font-size: var(--size-small); letter-spacing: 0.08em; }
  .nav .btn { margin-top: var(--s-4); width: 100%; }
}

/* -------------------------------------------------------------------------
   HERO
   ------------------------------------------------------------------------- */
.hero {
  padding-top: calc(var(--header-h) + var(--section-pad) + var(--s-7));
  padding-bottom: var(--section-pad);
  position: relative; overflow: hidden;
}
.hero__inner { position: relative; z-index: 2; text-align: center; }
.hero__inner > * + * { margin-top: var(--s-5); }
.hero .lede { margin-inline: auto; }
/* 14ch broke "A website that works" across two lines, so the headline ran to
   three against a documented two-line desktop maximum. 21ch holds each of the
   two authored spans on its own line, which is the break the markup intends. */
.hero h1 { max-width: 21ch; margin-inline: auto; }

.hero__trust {
  display: flex; flex-wrap: wrap; justify-content: center;
  align-items: center; gap: var(--s-3) var(--s-5);
  margin-top: var(--s-7);
  font-size: var(--size-small); color: var(--text-body);
}
.hero__trust li { display: flex; align-items: center; gap: var(--s-2); }

/* Floating client-work cards. Absolute at desktop, a plain grid below. */
.hero__art { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-4); margin-top: var(--s-7); }
.hero__art figure { margin: 0; }
.hero__art img {
  width: 100%; border-radius: var(--r-card);
  box-shadow: var(--shadow-2);
  border: 1px solid var(--hairline);
}
.hero__art figcaption {
  margin-top: var(--s-2);
  font-size: var(--size-micro); line-height: 1.35;
  color: var(--text-muted);
  text-align: center;
  text-wrap: balance;
}
/* Cropped to a common ratio below desktop so six tall shots do not push the
   rest of the page down a screen and a half. */
@media (max-width: 1099px) {
  .hero__art { padding-inline: var(--gutter); }
  .hero__art img { aspect-ratio: 4 / 3; object-fit: cover; }
  .hero__art .a5, .hero__art .a6 { display: none; }
}

@media (min-width: 1100px) {
  .hero { padding-bottom: calc(var(--section-pad) + var(--s-9)); }
  .hero__art {
    /* Inset from the viewport edges, not flush to them: the frames are
       positioned against this box, so a1/a4 sat hard against the screen edge
       when it was inset:0. */
    position: absolute; inset: 0 100px; z-index: 1;
    display: block; margin: 0; pointer-events: none;
  }
  /* The percentage widths were tuned before the captions existed. Past ~1440
     the frames keep growing (width-driven height) while the tops stay
     proportional, so each caption slid under the next frame in its column —
     three were covered at 1920. Capping the width stops the frames growing
     past their tuned size, and the proportional tops then give more clearance,
     not less. */
  .hero__art figure { position: absolute; width: var(--w); max-width: var(--w-max); }
  .hero__art img { box-shadow: var(--shadow-3); }
  .hero__art .a1 { --w: 20%; --w-max: 288px; top: calc(12% + 30px); left: 0; }
  .hero__art .a2 { --w: 13%; --w-max: 187px; top: 40%; left: 4%; }
  /* 72%: at 70% this frame landed on a2's caption at 1440. Same reason as a6. */
  .hero__art .a3 { --w: 17%; --w-max: 245px; top: 72%; left: 12%; }
  .hero__art .a4 { --w: 21%; --w-max: 302px; top: calc(10% + 30px); right: 0; }
  .hero__art .a5 { --w: 13%; --w-max: 187px; top: 44%; right: 5%; }
  /* 76%, not 72%: a6 and a5 overlap by ~72px horizontally, and at 72% a6's
     image landed exactly on a5's caption and painted over the first word of
     "Easy World Clock". The captions are content now, so they get the room. */
  .hero__art .a6 { --w: 18%; --w-max: 259px; top: 76%; right: 13%; }
  .hero__inner { padding-inline: 12%; }
  /* a3 and a6 flank the hero at 70%+, leaving a clear channel between their
     inner edges (29% and 69%). The strip is the lowest content and the only
     line long enough to reach them, so cap it to that channel; it wraps
     instead of running behind the artwork. */
  .hero__trust { max-width: 440px; margin-inline: auto; }
}

.hero__note {
  position: absolute; z-index: 3; display: none;
  transform: rotate(-6deg);
}
/* At left:26% the note sat against the tablet shot and read as its caption.
   It belongs to the price line, so it now hangs under the trust strip in the
   centre channel. Centred by transform rather than a fixed offset: the flanking
   images sit at 29% and 69%, and a hard margin put the rotated box across the
   left one at exactly 1100px. */
@media (min-width: 1100px) {
  .hero__note {
    display: block;
    bottom: 8%; left: 50%;
    max-width: 34%; text-align: center;
    transform: translateX(-50%) rotate(-6deg);
  }
}

/* -------------------------------------------------------------------------
   SPLIT — heading one side, content the other
   ------------------------------------------------------------------------- */
.split { display: grid; gap: var(--s-7); align-items: start; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--s-8); align-items: center; }
  .split--wide-right { grid-template-columns: 5fr 7fr; }
  .split--wide-left  { grid-template-columns: 7fr 5fr; }
  .split--top { align-items: start; }
}

/* A heading column half the height of the grid beside it left ~470px of dead
   white. Narrowing it and letting it travel with the scroll turns that gap
   into the section's own margin rather than a hole. Only at desktop, where
   there is a second column for it to travel against. */
@media (min-width: 1100px) {
  .split__sticky {
    position: sticky;
    top: calc(var(--header-h) + var(--s-7));
  }
}

/* CHECKLIST card — the pain points, struck through as they are dealt with */
.checklist { display: grid; gap: var(--s-3); }
.checklist li {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-input);
  background: var(--panel-on-navy);
  border: 1px solid var(--hairline-on-navy);
  font-size: var(--size-small);
}
.checklist li.is-struck { color: var(--text-muted-on-navy); }
.checklist li.is-struck span { text-decoration: line-through; }

/* -------------------------------------------------------------------------
   ONE-GRID — the four "one" promises plus the mark tile
   ------------------------------------------------------------------------- */
.one-grid { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, 1fr); }
@media (max-width: 599px) { .one-grid { grid-template-columns: 1fr; } }

.one-card { display: flex; flex-direction: column; gap: var(--s-3); min-height: 168px; }
/* The label used to be set at h4 weight in teal, so it outranked the heading
   underneath it — the card's own hierarchy read backwards. Dropped to label
   scale: it classifies the card, the h4 still leads it. */
.one-card__num {
  font-family: var(--font-body);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--teal-dark);
}
/* No margin-top: auto — pinning the paragraph to the bottom of an equal-height
   card tore it away from the heading it belongs to and made every card the
   same monotonous full-bleed block. The grid still equalises the heights. */
.one-card p { font-size: var(--size-small); }

/* The mark tile closes the grid rather than sitting as an orphan beside a gap. */
/* Doubles as the grid's closing CTA: the mark plus "Get started" in one
   clickable pill, rather than a static logo tile. */
.one-card--mark {
  background: var(--navy); border-color: var(--navy);
  grid-column: 1 / -1; min-height: 0;
  flex-direction: row; align-items: center; justify-content: center; gap: var(--s-4);
  padding-block: var(--s-5);
  transition: background-color var(--t-button) var(--ease), transform var(--t-button) var(--ease);
}
.one-card--mark:hover { background: var(--navy-raised); transform: translateY(-2px); }
.one-card--mark img { width: 44px; height: auto; flex: none; }
.one-card--mark span {
  font-family: var(--font-body); font-size: var(--size-eyebrow); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--white);
}

/* -------------------------------------------------------------------------
   SPEC STRIP — what the plan covers, six across
   ------------------------------------------------------------------------- */
.spec {
  display: grid; grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--hairline);
  margin-top: var(--s-7);
}
@media (min-width: 700px)  { .spec { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .spec { grid-template-columns: repeat(6, 1fr); } }

.spec__item {
  padding: var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
  display: grid; gap: var(--s-3); justify-items: start;
}
.spec__item:nth-child(2n) { border-right: 0; }
@media (min-width: 700px)  { .spec__item { border-right: 1px solid var(--hairline); }
                             .spec__item:nth-child(2n) { border-right: 1px solid var(--hairline); }
                             .spec__item:nth-child(3n) { border-right: 0; } }
@media (min-width: 1000px) { .spec__item:nth-child(3n) { border-right: 1px solid var(--hairline); }
                             .spec__item:nth-child(6n) { border-right: 0; } }
.spec__item .icon { color: var(--teal-dark); }
.spec__item strong { font-size: var(--size-small); font-weight: 600; color: var(--navy); }
.spec__item span { font-size: var(--size-small); color: var(--text-muted); }

/* SPEC STRIP · motion
   The strip is the contents of the offer, so it rules itself up like a spec
   sheet being drawn: the top line runs left to right, each vertical divider
   drops in behind its column, and the six entries land in reading order.
   The lines are the idea — the content only follows them.

   Gated on .js, like every other entrance here: without it the strip is
   simply present, borders and all. The real borders stay as that fallback;
   the animated rules are pseudo-elements laid over them. */
.js .spec { position: relative; }
.js .spec::before {
  content: ""; position: absolute; inset: 0 0 auto;
  height: 1px; background: var(--hairline);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 600ms var(--ease);
}
.js .spec.is-in::before { transform: scaleX(1); }
.js .spec.is-in { border-top-color: transparent; }

.js .spec__item { position: relative; }
/* The divider belongs to the column it opens, so it drops from the top rule. */
.js .spec__item::after {
  content: ""; position: absolute; inset: 0 -1px 0 auto;
  width: 1px; background: var(--hairline);
  transform: scaleY(0); transform-origin: center top;
  transition: transform 400ms var(--ease);
  transition-delay: calc(160ms + var(--i, 0) * 60ms);
}
.js .spec.is-in .spec__item::after { transform: scaleY(1); }
.js .spec.is-in .spec__item { border-right-color: transparent; }
/* The last item in each row has no divider, at any breakpoint. */
.js .spec__item:nth-child(2n)::after { content: none; }
@media (min-width: 700px) {
  .js .spec__item:nth-child(2n)::after { content: ""; }
  .js .spec__item:nth-child(3n)::after { content: none; }
}
@media (min-width: 1000px) {
  .js .spec__item:nth-child(3n)::after { content: ""; }
  .js .spec__item:nth-child(6n)::after { content: none; }
}

/* Content follows its own line in, 8px rather than the section's 24px: these
   are small cells and a long throw would read as a second section entrance. */
.js .spec__item > * {
  opacity: 0; transform: translateY(8px);
  transition: opacity 400ms var(--ease), transform 400ms var(--ease);
  transition-delay: calc(220ms + var(--i, 0) * 60ms);
}
.js .spec.is-in .spec__item > * { opacity: 1; transform: none; }

.js .spec__item:nth-child(1) { --i: 0; }
.js .spec__item:nth-child(2) { --i: 1; }
.js .spec__item:nth-child(3) { --i: 2; }
.js .spec__item:nth-child(4) { --i: 3; }
.js .spec__item:nth-child(5) { --i: 4; }
.js .spec__item:nth-child(6) { --i: 5; }

/* Nothing here carries meaning that the static strip does not. */
@media (prefers-reduced-motion: reduce) {
  .js .spec::before,
  .js .spec__item::after { transform: none; transition: none; }
  .js .spec__item > * { opacity: 1; transform: none; transition: none; }
}

/* FEATURE GRID */
.features { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, 1fr); }
@media (max-width: 599px)  { .features { grid-template-columns: 1fr; } }
@media (min-width: 1000px) { .features { grid-template-columns: repeat(3, 1fr); } }

/* Doubled class: this has to outrank the .section--sky .card inversion above. */
.card.feature--invert {
  background: var(--navy); border-color: var(--navy); color: var(--text-body-on-navy);
}
.card.feature--invert h3, .card.feature--invert h4 { color: var(--white); }
.card.feature--invert .card__icon { color: var(--teal); }

/* -------------------------------------------------------------------------
   MARQUEE — continuous, paused on hover, 40s. p14
   ------------------------------------------------------------------------- */
.marquee {
  background: var(--navy); overflow: hidden;
  padding-block: var(--s-4);
  border-block: 1px solid var(--hairline-on-navy);
}
.marquee__track {
  display: flex; width: max-content;
  animation: marquee 40s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__group {
  display: flex; align-items: center; gap: var(--s-6);
  padding-right: var(--s-6); flex: none;
}
.marquee__item {
  display: flex; align-items: center; gap: var(--s-4);
  color: var(--white);
  font-size: var(--size-micro); font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap;
}
.marquee__item::after { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--teal); }

@keyframes marquee { to { transform: translateX(-50%); } }

/* -------------------------------------------------------------------------
   PLANS — three-column pricing cards, section #plan. Professional sits
   elevated and dark as the recommended tier, matching the familiar
   three-tier SaaS pricing layout. p12
   ------------------------------------------------------------------------- */
.plans { display: grid; gap: var(--s-6); margin-top: var(--s-8); align-items: stretch; }
@media (min-width: 900px) {
  .plans { grid-template-columns: repeat(3, 1fr); align-items: center; gap: var(--s-5); }
}

.plan-card {
  display: flex; flex-direction: column; gap: var(--s-5);
  text-align: left;
}
.plan-card__eyebrow {
  font-family: var(--font-body);
  font-size: var(--size-eyebrow); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--teal-dark);
}
.plan-card__tagline { font-size: var(--size-small); color: var(--text-muted); }

.plan-card__price { display: flex; align-items: baseline; gap: var(--s-2); font-family: var(--font-display); }
.plan-card__price-value { font-size: var(--size-stat); font-weight: 800; letter-spacing: -0.03em; line-height: 0.95; color: var(--navy); }
.plan-card__price-suffix { font-family: var(--font-body); font-size: var(--size-body-lg); font-weight: 500; color: var(--text-muted); }

.plan-card .ticks { border-top: 1px solid var(--hairline); padding-top: var(--s-5); }

/* Professional: dark, elevated, badged. Reuses the navy-card and pill
   components rather than inventing a sixth colour.
   Doubled class: .section--sky .card (0,2,0) otherwise outranks the single
   .plan-card--featured class and wins the background back to white. */
.card.plan-card--featured {
  background: var(--navy); border-color: var(--navy);
  box-shadow: var(--shadow-3);
  position: relative;
  padding-top: calc(var(--s-6) + var(--s-4));
}
.plan-card--featured .plan-card__eyebrow { color: var(--teal); }
.plan-card--featured .plan-card__tagline { color: var(--text-muted-on-navy); }
.plan-card--featured .plan-card__price-value { color: var(--white); }
.plan-card--featured .plan-card__price-suffix { color: var(--text-muted-on-navy); }
.plan-card--featured .ticks { border-top-color: var(--hairline-on-navy); color: var(--text-body-on-navy); }

@media (min-width: 900px) { .plan-card--featured { transform: translateY(-16px); } }

.plan-card__badge {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
}

/* -------------------------------------------------------------------------
   PRICING
   ------------------------------------------------------------------------- */
.pricing { display: grid; gap: var(--s-5); align-items: start; }
@media (min-width: 900px)  { .pricing { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .pricing { grid-template-columns: 1.05fr 1fr 1fr; } }

/* Columns two and three carry two cards each against the tall plan card. */
.pricing__stack { display: grid; gap: var(--s-5); align-content: start; }

.price-card {
  background: var(--navy); border-color: var(--navy);
  color: var(--text-body-on-navy);
  display: flex; flex-direction: column; gap: var(--s-5);
}
.price-card h3 { color: var(--white); }
.price-card__head { display: grid; justify-items: start; gap: var(--s-4); }

.price {
  display: flex; align-items: baseline; gap: var(--s-2);
  font-family: var(--font-display); color: var(--white);
}
.price__value { font-size: var(--size-stat); font-weight: 800; letter-spacing: -0.03em; line-height: 0.95; }
.price__suffix { font-family: var(--font-body); font-size: var(--size-body-lg); font-weight: 500; color: var(--text-muted-on-navy); }
.price__terms { font-size: var(--size-small); color: var(--text-muted-on-navy); }

.price-card .ticks { font-size: var(--size-small); }
.price-card__included { border-top: 1px solid var(--hairline-on-navy); padding-top: var(--s-5); }
.price-card__label {
  font-family: var(--font-body);
  font-size: var(--size-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-muted-on-navy);
  margin-bottom: var(--s-4);
}

/* The add-on card. It used to hold a full teal ground, which made a £25 extra
   the loudest object in a section headed "One number. That's it." — the eye
   found the second price before the first. It now inverts against the white
   section like every other card, and carries its teal in the price badge
   alone, which p08 permits and which reads as an add-on rather than a rival. */
.care-card {
  position: relative;
  display: grid; justify-items: start; gap: var(--s-4);
}
.care-card__lede { font-weight: 500; }
.care-card__terms { font-size: var(--size-small); color: var(--text-muted); }
.care-card__badge {
  position: absolute; top: 0; right: var(--s-6);
  transform: translateY(-50%);
  display: inline-flex; align-items: center;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--teal); color: var(--navy);
  font-family: var(--font-display);
  font-size: var(--size-small); font-weight: 700;
  box-shadow: var(--shadow-2);
}

.note-card { display: grid; justify-items: start; gap: var(--s-4); }
.note-card__badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--target); height: var(--target);
  border-radius: 50%;
  background: var(--teal); color: var(--navy);
}

.price-note { position: relative; }
.price-note .annotation { display: block; transform: rotate(-3deg); }

/* -------------------------------------------------------------------------
   COMPARISON TABLE
   Uppercase column labels. Circles 14px inside 24px. p10 / p13
   ------------------------------------------------------------------------- */
/* max-width and the explicit overflow-y pair keep the 720px table contained on
   their own. Previously only the entrance transform established the containing
   block, so with JavaScript off the table pushed the document 301px wide. */
.compare-wrap {
  overflow-x: auto; overflow-y: hidden;
  max-width: 100%; min-width: 0;
  margin-top: var(--s-7);
}
.compare {
  width: 100%; min-width: 720px;
  border-collapse: collapse;
  text-align: left;
}
.compare th, .compare td {
  padding: var(--s-4) var(--s-4);
  border-bottom: 1px solid var(--hairline);
  vertical-align: middle;
}
.compare thead th {
  font-family: var(--font-body);
  font-size: var(--size-micro); font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-muted); white-space: nowrap;
}
.compare tbody th {
  font-family: var(--font-body); font-weight: 500;
  font-size: var(--size-small); color: var(--navy);
}
.compare td { text-align: center; width: 15%; }
/* The us column inverts against its section, like every other surface here.
   The section is white since the reband, so the highlight is sky. */
.compare .col-us { background: var(--sky); }
.compare thead .col-us { color: var(--teal-dark); }
.compare tbody tr:last-child th, .compare tbody tr:last-child td { border-bottom: 0; }

.mark-yes, .mark-no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
}
.mark-yes { color: var(--teal-dark); }
.mark-no { color: rgba(0, 0, 68, 0.45); }
.mark-yes svg, .mark-no svg { width: 14px; height: 14px; }

.compare-cta { display: grid; gap: var(--s-4); justify-items: center; margin-top: var(--s-7); text-align: center; }

/* Below 900px the five columns become five labelled lines inside one card per
   claim. Same markup, same table semantics for assistive tech — the matrix
   simply stops being a matrix at a width that cannot hold one. A 720px
   scroller showed two of five columns with no sign the other three existed,
   and the only visible competitor was the one that looks cheapest. */
@media (max-width: 899px) {
  .compare-wrap { overflow-x: visible; }
  .compare { min-width: 0; }
  .compare thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .compare, .compare tbody, .compare th, .compare td { display: block; }
  /* flex column, not block: it is what lets col-us reorder to the top. */
  .compare tr { display: flex; flex-direction: column; }

  .compare tbody tr {
    padding: var(--s-5);
    margin-bottom: var(--s-4);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    background: var(--sky);
  }
  .compare tbody tr:last-child { margin-bottom: 0; }

  .compare tbody th {
    order: -2;
    padding: 0 0 var(--s-4);
    margin-bottom: var(--s-3);
    border-bottom: 1px solid var(--hairline);
    font-size: var(--size-body-lg); font-weight: 600;
  }

  .compare td {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s-4);
    width: auto; text-align: left;
    padding: var(--s-2) 0; border-bottom: 0;
  }
  .compare td::before {
    content: attr(data-label);
    font-size: var(--size-small); color: var(--text-muted);
  }
  /* Us first and unmistakable: the one row on a sky ground, in navy, at weight. */
  .compare td.col-us {
    order: -1;
    margin: 0 calc(var(--s-5) * -1) var(--s-3);
    padding: var(--s-3) var(--s-5);
    border-radius: var(--r-input);
    background: var(--white);
  }
  .compare td.col-us::before { color: var(--navy); font-weight: 600; }
  .compare tbody tr:last-child td { border-bottom: 0; }
}

/* -------------------------------------------------------------------------
   WORK MOSAIC — real client work and the businesses behind it
   ------------------------------------------------------------------------- */
.mosaic {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 700px)  { .mosaic { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .mosaic { grid-template-columns: repeat(4, 1fr); } }

.mosaic figure { margin: 0; position: relative; }
.mosaic img {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: var(--r-card);
  border: 1px solid var(--hairline-on-navy);
}
.mosaic .tall { grid-row: span 2; }
.mosaic .tall img { aspect-ratio: 3 / 4.6; }
.mosaic .wide { grid-column: span 2; }
.mosaic figure:not(.tall) img { aspect-ratio: 4 / 3; }
.mosaic .wide img { aspect-ratio: 16 / 9; }

.mosaic .stat {
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--s-5);
  border-radius: var(--r-card);
  background: var(--navy-raised);
  border: 1px solid var(--hairline-on-navy);
  min-height: 100%;
}
/* No teal ground here either. Two teal tiles scattered through the work grid
   shouted louder than the client sites they sat among, and put a third and
   fourth teal background on a page whose rule allows one. The figures keep
   their teal numeral on navy-raised, which is 7.49:1 and plenty. */

@media (max-width: 699px) { .mosaic .tall { grid-row: span 1; } .mosaic .tall img { aspect-ratio: 4 / 3; } }

/* At 390px a two-up mosaic gave 163px tiles — too small to read a website in a
   section headed "real sites for real businesses". Each shot takes the full
   column; the stat tiles keep pairing up, so the section grows in height
   without turning five short claims into five full-width slabs. */
@media (max-width: 599px) {
  .mosaic figure { grid-column: 1 / -1; }
  .mosaic figure img, .mosaic .tall img { aspect-ratio: 16 / 10; }
  .mosaic .stat { min-height: 0; padding: var(--s-4); }
  .mosaic .stat .stat__value { font-size: clamp(2.5rem, 12vw, 3.5rem); }
}

/* CLIENT ROW */
.client-row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; margin-top: var(--s-5); }

/* -------------------------------------------------------------------------
   TESTIMONIALS
   ------------------------------------------------------------------------- */
.quotes { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, 1fr); }
@media (max-width: 599px)  { .quotes { grid-template-columns: 1fr; } }
@media (min-width: 1000px) { .quotes { grid-template-columns: repeat(4, 1fr); } }

.quote { display: flex; flex-direction: column; gap: var(--s-5); }
.quote blockquote { font-size: var(--size-body); line-height: 1.6; }
.quote__mark { color: var(--teal); font-family: var(--font-display); font-size: var(--size-h3); line-height: 1; }
.quote__by { display: flex; align-items: center; gap: var(--s-3); margin-top: auto; }
.quote__by img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; flex: none; }
.quote__name { font-size: var(--size-small); font-weight: 600; color: var(--white); }
.quote__role { font-size: var(--size-small); color: var(--text-muted-on-navy); }

/* -------------------------------------------------------------------------
   FAQ ACCORDION — height and opacity, 240ms. p14
   ------------------------------------------------------------------------- */
.faq { display: grid; gap: var(--s-3); margin-top: var(--s-7); }

.faq__item {
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  overflow: hidden;
  transition: border-color var(--t-card) var(--ease);
}
.faq__item[open] { border-color: var(--teal); }

.faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  width: 100%; min-height: var(--target);
  padding: var(--s-5);
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-body); font-size: var(--size-body-lg); font-weight: 500;
  color: var(--navy); text-align: left;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__icon { flex: none; color: var(--teal-dark); transition: transform var(--t-accordion) var(--ease); }
.faq__item[open] .faq__icon { transform: rotate(45deg); }

.faq__a { padding: 0 var(--s-5) var(--s-5); }
.faq__a p { font-size: var(--size-body); color: var(--text-body); }

/* -------------------------------------------------------------------------
   CLOSING CTA — the one teal background in the system. p08
   ------------------------------------------------------------------------- */
.cta-band { text-align: center; }
.cta-band .lede { margin-inline: auto; }
.cta-band .annotation { color: var(--navy); }

/* Doubled class: this has to outrank the .on-teal .card inversion above. */
.card.waitlist {
  background: var(--navy); color: var(--text-body-on-navy);
  border-radius: var(--r-panel); padding: var(--s-7);
  margin: var(--s-7) auto 0; max-width: 620px; text-align: left;
  border: 0;
}
.waitlist h3 { color: var(--white); }
/* Rhythm on direct children only, so the paired fields inside a row stay level. */
.waitlist__form > * + * { margin-top: var(--s-4); }
.waitlist__form > h3 + * { margin-top: var(--s-5); }
.waitlist .btn { margin-top: var(--s-5); }
.waitlist__row { display: grid; gap: var(--s-4); }
@media (min-width: 600px) { .waitlist__row { grid-template-columns: 1fr 1fr; } }
.waitlist .btn--primary { background: var(--teal); color: var(--navy); }
.waitlist .btn--primary:hover { background: var(--teal-dark); color: var(--white); }
.waitlist__note { margin-top: var(--s-4); font-size: var(--size-small); color: var(--text-muted-on-navy); }
.waitlist__done { display: none; }
.waitlist.is-sent .waitlist__form { display: none; }
.waitlist.is-sent .waitlist__done { display: block; }

/* -------------------------------------------------------------------------
   PORTFOLIO SCROLLER — third-party embed, restyled into this system
   The script injects its own <style> at runtime, so it lands after this file:
   equal specificity would lose. Scoping from the section id outranks it
   (2,1,0 against 1,1,0) without reaching for !important.
   ------------------------------------------------------------------------- */
/* A logo strip is a trust band, not a full section: at the standard rhythm it
   spent 240px of padding on 112px of content. Its own tighter scale, still on
   the 4/8/…/96 steps, and the heading sits closer to the row it introduces. */
#portfolio { padding-block: var(--s-6); }
#portfolio .section__head { margin-bottom: var(--s-5); }
@media (min-width: 768px)  { #portfolio { padding-block: var(--s-7); } }
@media (min-width: 1100px) { #portfolio { padding-block: var(--s-8); } }

#portfolio #pmw-portfolio-scroller { font-family: var(--font-body); }

/* The vendor's mask fades the track at 8% and 92%. Widened to the gutter so
   the fade lines up with where the page's own content starts and stops. */
#portfolio #pmw-portfolio-scroller .pmw-s-wrap {
  padding-block: var(--s-5);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

/* 40s matches the spec-strip marquee already on the page, so the two run at
   the same speed rather than at two rates the eye has to reconcile. */
#portfolio #pmw-portfolio-scroller .pmw-s-track { gap: var(--s-8); }

#portfolio #pmw-portfolio-scroller .pmw-s-item { height: 64px; color: var(--text-body); }
#portfolio #pmw-portfolio-scroller .pmw-s-item img { max-height: 52px; max-width: 168px; }

/* Logos arrive in whatever colour their owner uses. Holding them at a slight
   remove keeps the row reading as one band and stops a bright logo pulling
   rank over the headline; full colour returns on hover. */
#portfolio #pmw-portfolio-scroller .pmw-s-item {
  opacity: 0.72;
  transition: opacity var(--t-card) var(--ease);
}
#portfolio #pmw-portfolio-scroller .pmw-s-item:hover,
#portfolio #pmw-portfolio-scroller .pmw-s-item:focus-visible { opacity: 1; }

/* The name fallback for a client with no logo file. */
#portfolio #pmw-portfolio-scroller .pmw-s-fallback {
  font-family: var(--font-display);
  font-size: var(--size-h4); font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--navy);
  padding-inline: var(--s-3);
}

#portfolio #pmw-portfolio-scroller .pmw-s-msg {
  padding: var(--s-6);
  font-size: var(--size-small); color: var(--text-muted);
}
#portfolio #pmw-portfolio-scroller .pmw-s-skel {
  background: var(--hairline);
  border-radius: var(--r-input);
}

#portfolio #pmw-portfolio-scroller a:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* The vendor pauses on hover but only stops the marquee under reduced motion.
   The skeleton pulse keeps running, so stop that here too. */
@media (prefers-reduced-motion: reduce) {
  #portfolio #pmw-portfolio-scroller .pmw-s-skel { animation: none; }
  #portfolio #pmw-portfolio-scroller .pmw-s-item { transition: none; }
}

/* -------------------------------------------------------------------------
   STICKY CTA — mobile only, thumb zone, never over the waitlist
   ------------------------------------------------------------------------- */
.sticky-cta { display: none; }

@media (max-width: 899px) {
  .sticky-cta {
    position: fixed; z-index: 90;
    inset: auto 0 0;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s-4);
    padding: var(--s-3) var(--gutter);
    padding-bottom: max(var(--s-3), env(safe-area-inset-bottom));
    background: var(--navy);
    border-top: 1px solid var(--hairline-on-navy);
    transform: translateY(100%);
    transition: transform var(--t-card) var(--ease);
  }
  .sticky-cta[hidden] { display: none; }
  .sticky-cta.is-in { transform: none; }

  .sticky-cta__price {
    margin: 0; max-width: none;
    font-size: var(--size-small); line-height: 1.25;
    color: var(--text-body-on-navy);
  }
  .sticky-cta__price strong {
    font-family: var(--font-display); font-size: var(--size-h4); font-weight: 800;
    letter-spacing: -0.02em; color: var(--white);
    margin-right: var(--s-1);
  }
  .sticky-cta__terms { display: block; font-size: var(--size-micro); color: var(--text-muted-on-navy); }
  .sticky-cta .btn { flex: none; }

  /* The bar sits over the last of the page, so give the footer room to clear. */
  body.has-sticky-cta .footer { padding-bottom: calc(var(--s-9) + var(--target)); }
}

@media (prefers-reduced-motion: reduce) {
  .sticky-cta { transition: none; }
}

/* -------------------------------------------------------------------------
   CONSENT NOTICE — fixed bottom, every viewport. Sits above the sticky CTA
   (z-index 90) and the header (100): a consent gate outranks both while it
   is up, and script.js keeps the sticky CTA from showing underneath it.
   Named "site-consent", not "cookie-banner": ad-block "Annoyances" filter
   lists auto-hide any id/class containing "cookie" on every site, working
   or not, which was silently killing this one a beat after it painted.
   ------------------------------------------------------------------------- */
.site-consent {
  position: fixed; z-index: 150; inset: auto 0 0;
  background: var(--navy);
  border-top: 1px solid var(--hairline-on-navy);
  padding: var(--s-5) var(--gutter);
  padding-bottom: max(var(--s-5), env(safe-area-inset-bottom));
  transform: translateY(100%);
  transition: transform var(--t-card) var(--ease);
}
.site-consent[hidden] { display: none; }
.site-consent.is-in { transform: none; }

.site-consent__inner {
  max-width: var(--container); margin-inline: auto;
  display: grid; gap: var(--s-4);
}
@media (min-width: 700px) {
  .site-consent__inner { grid-template-columns: 1fr auto; align-items: center; gap: var(--s-6); }
}

.site-consent__text p { font-size: var(--size-small); color: var(--text-body-on-navy); max-width: 62ch; }

.site-consent__details { margin-top: var(--s-3); }
.site-consent__details summary {
  display: inline-flex; align-items: center; gap: var(--s-2);
  cursor: pointer; list-style: none;
  font-size: var(--size-small); font-weight: 600; color: var(--teal);
}
.site-consent__details summary::-webkit-details-marker { display: none; }
.site-consent__details .faq__icon { color: var(--teal); transition: transform var(--t-accordion) var(--ease); }
.site-consent__details[open] .faq__icon { transform: rotate(45deg); }
.site-consent__details ul { margin-top: var(--s-3); display: grid; gap: var(--s-2); }
.site-consent__details li { font-size: var(--size-small); color: var(--text-muted-on-navy); }
.site-consent__details strong { color: var(--text-body-on-navy); }

.site-consent__actions { display: flex; gap: var(--s-3); flex-wrap: wrap; }
.site-consent .btn--secondary { color: var(--white); border-color: var(--hairline-on-navy); }
.site-consent .btn--secondary:hover { background: var(--white); color: var(--navy); border-color: var(--white); }

/* Unlike the sticky CTA this shows at every width, so the footer needs the
   same clearance on desktop too, not just inside the mobile breakpoint. */
body.has-site-consent .footer { padding-bottom: calc(var(--s-9) + var(--target)); }

@media (prefers-reduced-motion: reduce) {
  .site-consent { transition: none; }
  .site-consent__details .faq__icon { transition: none; }
}

/* -------------------------------------------------------------------------
   FOOTER
   Lockup 200px. Ghosted wordmark at 15 to 20% white. p05 / p08
   ------------------------------------------------------------------------- */
.footer { background: var(--navy); color: var(--text-body-on-navy); position: relative; overflow: hidden; }
.footer__inner { padding-block: var(--section-pad) var(--s-7); position: relative; z-index: 2; }

.footer__top { display: grid; gap: var(--s-7); }
@media (min-width: 900px) { .footer__top { grid-template-columns: 1fr auto; gap: var(--s-9); } }

.footer .logo { width: 240px; height: auto; }
.footer .logo img { width: 100%; height: auto; }
.footer__blurb { max-width: 38ch; margin-block: var(--s-5); font-size: var(--size-small); }

.footer__links { display: grid; gap: var(--s-7); grid-template-columns: repeat(2, minmax(120px, 1fr)); }
@media (min-width: 600px) { .footer__links { grid-template-columns: repeat(3, minmax(120px, 1fr)); } }
.footer__links h2 {
  font-family: var(--font-body);
  font-size: var(--size-micro); font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-muted-on-navy); margin-bottom: var(--s-4);
}
.footer__links ul { display: grid; gap: var(--s-1); }
/* Footer micro-labels are deliberately lower-case. p10 */
.footer__links a {
  display: inline-flex; align-items: center; min-height: var(--target);
  font-size: var(--size-small); text-transform: lowercase;
  color: var(--text-body-on-navy);
  transition: color var(--t-button) var(--ease);
}
.footer__links a:hover { color: var(--teal); }

.footer__bar {
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5);
  align-items: center; justify-content: space-between;
  margin-top: var(--s-8); padding-top: var(--s-5);
  border-top: 1px solid var(--hairline-on-navy);
  font-size: var(--size-small); color: var(--text-muted-on-navy);
}
.footer__bar ul { display: flex; flex-wrap: wrap; gap: var(--s-5); align-items: center; }
.footer__bar a {
  display: inline-flex; align-items: center; min-height: var(--target);
  text-transform: lowercase;
}
.footer__bar a:hover { color: var(--teal); }

/* Was a raster wordmark; now live text so it always fits the footer width
   at any viewport instead of overflowing at fixed pixel dimensions. */
.footer__ghost {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 100%; max-width: 100vw;
  margin: 0; transform: translate(-50%, -50%);
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(2.5rem, 11vw, 8.5rem);
  line-height: 0.95; letter-spacing: -0.02em;
  text-transform: uppercase; text-align: center;
  color: var(--white);
  opacity: 0.17; pointer-events: none; user-select: none;
}

/* -------------------------------------------------------------------------
   MOTION
   Section entrance fades up 24px, staggered 80ms between siblings, fired
   once at 20% viewport entry, 500ms. The H1 is the only line-by-line mask
   reveal in the system. p14
   ------------------------------------------------------------------------- */
/* Gated on .js, which the inline script in <head> sets before first paint.
   The hidden state must never be the default: with JavaScript off, or the
   script request lost on a bad signal, an opacity:0 default would leave the
   whole page blank. Without .js the entrances simply do not run. */
.js [data-reveal] {
  opacity: 0; transform: translateY(24px);
  transition: opacity var(--t-entrance) var(--ease),
              transform var(--t-entrance) var(--ease);
  transition-delay: var(--delay, 0ms);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

.h1-reveal { display: block; }
.js .h1-reveal { overflow: hidden; }
.js .h1-reveal > span {
  display: block;
  transform: translateY(100%);
  transition: transform var(--t-entrance) var(--ease);
  transition-delay: var(--delay, 0ms);
}
.js .is-loaded .h1-reveal > span { transform: none; }

/* Nothing in the system depends on movement to be understood. p14 */
@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;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .h1-reveal > span { transform: none; }
  .btn:hover, .card--interactive:hover { transform: none; }
  .marquee__track { animation: none; }
}
