/* =========================================================================
   SUPREME CLEANS — site.css  v1.0
   Tokens and components implement docs/ARCH-DESIGN.md exactly.
   Do not add colours. A new colour is a tint of one of the five.
   ========================================================================= */

/* ---------- 1. TOKENS --------------------------------------------------- */
:root {
  /* the five — Supreme Cleans demo palette (2026-10-15). Contrast pairs are
     measured in .claude/cloud-notes.md; re-measure before changing any value. */
  --primary:        #0B3B3C;   /* Supreme Pine  */
  --secondary:      #062627;   /* Deep Lagoon   */
  --accent:         #F4845F;   /* Supreme Coral */
  --surface:        #F5F7F4;   /* Sea Salt      */
  --text:           #0D1C1C;   /* Kelp          */

  /* derived tints */
  --primary-dark:   #082D2E;
  --primary-tint:   #E2EFEC;
  --surface-2:      #E8EFEB;
  --surface-3:      #E2EFEC;
  --line:           #D5DFDA;   /* decorative hairlines on sea salt (not UI) */
  --line-strong:    #C2D0CA;   /* table rules, dividers (decoration only)   */
  /* Borders that IDENTIFY a control must clear 3:1 per WCAG SC 1.4.11.
     Measured: #6E837E is 4.03:1 on white and 3.74:1 on sea salt. */
  --border-input:   #6E837E;
  --text-muted:     #4A5E5B;   /* 6.41:1 on sea salt, 5.91:1 on surface-2 */
  --accent-ink:     #A3401F;   /* coral for TEXT on light: 5.89:1 on sea salt */
  --accent-soft:    #F8A487;
  --on-dark:        #E4F0ED;
  --on-dark-muted:  #B5CAC5;   /* 7.17:1 on pine, 9.30:1 on lagoon */
  --white:          #FFFFFF;

  /* semantic */
  --ok: #2E7D5B;  --ok-tint: #E4F0EA;
  --warn: #8A5A00; --warn-tint: #F7EEDC;
  --danger: #B3261E; --danger-tint: #FBE9E7;

  /* gradients — 135deg, 2 stops */
  --grad-cta:  linear-gradient(135deg, #0B3B3C 0%, #062627 100%);
  --grad-hero: linear-gradient(135deg, rgba(6,38,39,.92) 0%, rgba(11,59,60,.72) 100%);
  --grad-gold: linear-gradient(135deg, #F8A487 0%, #F4845F 100%);
  /* Lit card edge. Both are tints of primary and accent — no new colours. */
  --grad-edge:      linear-gradient(135deg, rgba(11,59,60,.50) 0%, rgba(244,132,95,.90) 100%);
  --grad-edge-dark: linear-gradient(135deg, rgba(244,132,95,.85) 0%, rgba(228,240,237,.30) 100%);

  /* type */
  --font-display: 'Fraunces','Iowan Old Style','Palatino Linotype',Georgia,serif;
  --font-body:    'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
  --fs-h1:    clamp(2.5rem, 1.55rem + 3.9vw, 4.5rem);
  --fs-h2:    clamp(2rem, 1.55rem + 1.9vw, 3rem);
  --fs-h3:    clamp(1.375rem, 1.24rem + 0.55vw, 1.75rem);
  --fs-h4:    clamp(1.125rem, 1.07rem + 0.24vw, 1.3125rem);
  --fs-lead:  clamp(1.0625rem, 1rem + 0.38vw, 1.25rem);
  --fs-body:  clamp(1rem, 0.975rem + 0.11vw, 1.0625rem);
  --fs-small: 1rem;        /* was 15px — card and secondary body copy is body copy */
  --fs-meta:  0.875rem;    /* 14px: the hard floor, secondary meta lines only      */
  --fs-label: 0.875rem;    /* 14px: uppercase labels and badges only               */
  --fs-price: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
  --lh-tight: 1.14;
  --lh-body:  1.7;
  --ls-head:  -0.02em;
  --ls-caps:  0.12em;

  /* spacing — 8px grid */
  --s-1:8px; --s-2:16px; --s-3:24px; --s-4:32px;
  --s-5:48px; --s-6:64px; --s-7:80px; --s-8:96px; --s-9:128px;
  --section-y:       clamp(3rem, 1.4rem + 6.4vw, 6rem);
  --section-y-large: clamp(4rem, 2rem + 8vw, 7rem);
  --gutter:          clamp(1.5rem, 0.9rem + 2.4vw, 2.5rem);
  --container:        1200px;
  --container-narrow: 760px;
  --card-pad:        clamp(1.5rem, 1.2rem + 1vw, 2rem);

  /* radii */
  --r-btn:4px; --r-sm:6px; --r-md:10px; --r-lg:16px; --r-pill:999px;

  /* shadows */
  --sh-sm:   0 1px 2px rgba(13,28,28,.06);
  --sh-md:   0 6px 20px -6px rgba(13,28,28,.18);
  --sh-lg:   0 18px 44px -12px rgba(13,28,28,.28);
  --sh-nav:  0 2px 14px rgba(13,28,28,.10);
  --sh-gold: 0 8px 24px -8px rgba(244,132,95,.55);

  /* motion */
  --t-fast:120ms; --t-base:220ms; --t-slow:420ms;
  --ease-out:    cubic-bezier(.22,.61,.36,1);
  --ease-in-out: cubic-bezier(.65,0,.35,1);
  --ease-pop:    cubic-bezier(.34,1.32,.64,1);

  /* chrome */
  --nav-h:76px; --nav-h-scroll:68px; --pricebar-h:88px; --wa-size:60px;

  /* z ladder */
  --z-base:1; --z-raised:10; --z-sticky:60; --z-nav:100;
  --z-wa:120; --z-overlay:200; --z-modal:300; --z-toast:400;
}

/* ---------- 2. RESET / BASE -------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
/* `hidden` is a UA rule at the lowest possible specificity, so ANY class that
   sets `display` beats it. `.card { display: flex }` did exactly that and left
   the "Application received" panel on screen for every visitor to
   /become-a-pro who had not applied yet. Measured on live 2026-08-29: 343px
   tall and fully visible. Anything toggled with the hidden attribute depends
   on this rule. */
[hidden] { display: none !important; }
* { margin: 0; }
/* The nav is position:fixed, so without scroll-padding every in-page anchor
   (#apply, the FAQ deep links) lands with its heading under the nav bar. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + var(--s-3)); }
body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: var(--primary); text-decoration-thickness: 1px; text-underline-offset: 3px; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { padding: 0; list-style: none; }

h1,h2,h3 { font-family: var(--font-display); font-weight: 700; letter-spacing: var(--ls-head); }
h1 { font-size: var(--fs-h1); line-height: 1.06; }
h2 { font-size: var(--fs-h2); line-height: var(--lh-tight); }
h3 { font-size: var(--fs-h3); line-height: 1.22; font-weight: 600; }
h4 { font-family: var(--font-body); font-size: var(--fs-h4); font-weight: 700; line-height: 1.3; letter-spacing: -0.01em; }
p  { max-width: 65ch; }

.tnum, .price, [data-count], .stat__num, table { font-feature-settings: 'tnum' 1; font-variant-numeric: tabular-nums; }

/* ---------- 3. FOCUS ---------------------------------------------------- */
/* One focus-ring definition. Pine by default because coral is only 2.35:1 on
   sea salt and fails SC 1.4.11 as a focus indicator; pine is 11.43:1. Coral is
   reinstated on dark surfaces, where it is 6.32:1 and pine would vanish.
   (The .btn--gold / --grad-gold names predate the Supreme palette: they mean
   "the accent", which is now coral.) */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible {
  outline: 3px solid var(--primary); outline-offset: 2px; border-radius: var(--r-btn);
}
.nav :focus-visible, .navmenu :focus-visible, .hero :focus-visible,
.pagehero :focus-visible, .ctaband :focus-visible, .footer :focus-visible,
.section--dark :focus-visible, .trustbar :focus-visible, .wa:focus-visible {
  outline-color: var(--accent);
}
.btn--gold:focus-visible, [data-on-gold]:focus-visible { outline-color: var(--primary); }
.skip {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-toast);
  background: var(--accent); color: var(--text); padding: var(--s-2) var(--s-3); font-weight: 700;
}
.skip:focus { left: var(--s-2); top: var(--s-2); }

/* ---------- 4. LAYOUT --------------------------------------------------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.container--narrow { max-width: var(--container-narrow); }
.section { padding-block: var(--section-y); }
.section--tint { background: var(--surface-2); }
.section--dark { background: var(--grad-cta); color: var(--on-dark); }
.section--dark h2, .section--dark h3 { color: var(--white); }
.section--dark p { color: var(--on-dark); }

.section-head { max-width: 720px; margin-inline: auto; text-align: center; margin-bottom: var(--s-6); }
.section-head p { margin-inline: auto; }
.section-head--left { margin-inline: 0; text-align: left; }
.eyebrow {
  display: inline-block; font-size: var(--fs-label); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--ls-caps); color: var(--accent-ink);
  margin-bottom: var(--s-1);
}
.section--dark .eyebrow { color: var(--accent); }
.section-head h2 { margin-bottom: var(--s-2); }
.lead { font-size: var(--fs-lead); color: var(--text-muted); }
.section--dark .lead { color: var(--on-dark-muted); }

/* Every track below is minmax(0,1fr), never a bare 1fr. A bare `1fr` is
   `minmax(auto,1fr)`, and that `auto` minimum is the ITEM'S MIN-CONTENT — a
   grid track physically refuses to shrink below it. Measured on live at 360px:
   the service card's nowrap footer (price + "Open the calculator") has a
   377px min-content, so the 312px track resolved to 377px and pushed
   document.scrollWidth to 401 on the homepage and /services; the contact
   form's <select> ("Office or commercial cleaning") did the same at 423px and
   took /contact to 447. `body { overflow-x: hidden }` hid the scrollbar but
   not the damage — the right-hand side of every card was simply cut off.
   minmax(0,1fr) is the fix everywhere a card, form or table lives in a track. */
.grid { display: grid; gap: var(--s-3); }
.grid--2 { grid-template-columns: minmax(0,1fr); }
.grid--3 { grid-template-columns: minmax(0,1fr); }
.grid--4 { grid-template-columns: minmax(0,1fr); }
.grid--services { grid-template-columns: minmax(0,1fr); }
@media (min-width: 640px) {
  .grid--2 { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .grid--3 { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .grid--4 { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .grid--services { grid-template-columns: repeat(2,minmax(0,1fr)); }
}
@media (min-width: 1024px) {
  .grid { gap: var(--s-4); }
  .grid--3 { grid-template-columns: repeat(3,minmax(0,1fr)); }
  .grid--4 { grid-template-columns: repeat(4,minmax(0,1fr)); }
  .grid--services { grid-template-columns: repeat(3,minmax(0,1fr)); gap: var(--s-4); }
}

/* ---------- 5. BUTTONS -------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-1);
  min-height: 48px; padding: .85rem 2rem; border: 0; border-radius: var(--r-btn);
  font-family: var(--font-body); font-size: 1rem; font-weight: 700; line-height: 1;
  text-decoration: none; cursor: pointer; text-align: center;
  transition: background-color var(--t-base) var(--ease-out),
              color var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: scale(.97); }
.btn__arrow { transition: transform var(--t-base) var(--ease-out); }
.btn:hover .btn__arrow { transform: translateX(4px); }

.btn--primary { background: var(--primary); color: var(--white); }
.btn--primary:hover { background: var(--primary-dark); box-shadow: var(--sh-md); }
.btn--gold { background: var(--accent); color: var(--text); }
.btn--gold:hover { background: var(--accent-soft); box-shadow: var(--sh-gold); }
.btn--ghost { background: transparent; color: var(--white); border: 2px solid rgba(255,255,255,.7); }
.btn--ghost:hover { background: rgba(255,255,255,.12); border-color: var(--white); }
.btn--outline { background: transparent; color: var(--primary); border: 2px solid var(--primary); }
.btn--outline:hover { background: var(--primary); color: var(--white); }
.btn--sm { min-height: 44px; padding: .6rem 1.25rem; font-size: var(--fs-small); }
.btn--block { width: 100%; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .5; pointer-events: none; }
@media (max-width: 639px) { .btn--stack { width: 100%; } }

/* ---------- 6. NAV ------------------------------------------------------ */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-nav);
  height: var(--nav-h); background: var(--primary);
  border-bottom: 1px solid rgba(244,132,95,.22);
  transition: height var(--t-base) var(--ease-out),
              background-color var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.nav--scrolled { height: var(--nav-h-scroll); background: var(--secondary); box-shadow: var(--sh-nav); }
.nav__inner { display: flex; align-items: center; gap: var(--s-3); height: 100%; }
.nav__logo {
  /* The logo is the home link and gets tapped; it measured 37px tall. */
  display: inline-flex; align-items: center; gap: 10px; margin-right: auto; min-height: 44px;
  color: var(--white); text-decoration: none; font-family: var(--font-display);
  font-weight: 700; font-size: 1.35rem; letter-spacing: -0.02em;
  transition: transform var(--t-base) var(--ease-out);
}
.nav--scrolled .nav__logo { transform: scale(.92); }
.nav__logo svg { width: 30px; height: 30px; flex: 0 0 auto; }
.nav__logo em { font-style: normal; color: var(--accent); }
.nav__links { display: none; align-items: center; gap: var(--s-3); }
.nav__links a {
  position: relative; color: var(--on-dark); text-decoration: none;
  font-size: var(--fs-small); font-weight: 600; padding: 6px 2px;
  transition: color var(--t-fast) var(--ease-out);
}
.nav__links a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--accent); transform: scaleX(0); transition: transform var(--t-base) var(--ease-out);
}
.nav__links a:hover, .nav__links a[aria-current="page"] { color: var(--white); }
.nav__links a:hover::after, .nav__links a[aria-current="page"]::after { transform: scaleX(1); }
.nav__cta { display: none; }
.nav__burger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; background: transparent; border: 0; cursor: pointer;
  color: var(--white); border-radius: var(--r-btn); margin-left: auto;
}
.nav__burger span { display: block; width: 22px; height: 2px; background: currentColor; position: relative; }
.nav__burger span::before, .nav__burger span::after {
  content: ''; position: absolute; left: 0; width: 22px; height: 2px; background: currentColor;
  transition: transform var(--t-base) var(--ease-out), opacity var(--t-fast);
}
.nav__burger span::before { top: -7px; } .nav__burger span::after { top: 7px; }
body.menu-open .nav__burger span { background: transparent; }
body.menu-open .nav__burger span::before { transform: translateY(7px) rotate(45deg); }
body.menu-open .nav__burger span::after  { transform: translateY(-7px) rotate(-45deg); }
/* Demo badge. Supreme Cleans is fictional; this pill sits beside the logo in
   the fixed nav so it is on screen on every page at every scroll position.
   Non-interactive on purpose (a span), so it needs no 44px touch target. */
.nav__logo { margin-right: 0; }
.demo-badge {
  margin-right: auto; flex: 0 0 auto;
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--text);
  font-size: var(--fs-label); font-weight: 700; line-height: 1.4;
  text-transform: uppercase; letter-spacing: var(--ls-caps);
}
@media (max-width: 419px) {
  /* "Supreme" is two letters longer than the old wordmark; at 360px the logo,
     badge and burger only fit with tighter gaps and a slightly smaller mark. */
  .nav__inner { gap: 12px; }
  .nav__logo { font-size: 1.2rem; gap: 8px; }
  .nav__logo svg { width: 26px; height: 26px; }
  .demo-badge { padding: 2px 7px; letter-spacing: .08em; }
}
@media (min-width: 1024px) {
  .nav__links { display: flex; }
  .nav__cta { display: inline-flex; }
  .nav__burger { display: none; }
}

.navmenu {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: var(--grad-cta); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--s-3);
  padding: var(--s-7) var(--gutter) var(--s-5);
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out), visibility var(--t-base);
  overflow-y: auto;
}
body.menu-open .navmenu { opacity: 1; visibility: visible; transform: none; }
body.menu-open { overflow: hidden; }
.navmenu a { color: var(--white); text-decoration: none; font-size: 1.35rem; font-weight: 600; padding: 10px 16px; }
.navmenu a:hover { color: var(--accent); }
.navmenu__close {
  position: absolute; top: 14px; right: var(--gutter);
  width: 44px; height: 44px; background: transparent; border: 0; color: var(--white); cursor: pointer; font-size: 1.6rem;
}
.navmenu__label {
  font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-caps);
  color: var(--accent); font-weight: 700; margin-top: var(--s-2);
}
@media (min-width: 1024px) { .navmenu { display: none; } }

/* ---------- 7. PROGRESS BAR --------------------------------------------- */
.progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: calc(var(--z-nav) + 1); pointer-events: none; }
.progress__fill { height: 100%; background: var(--grad-gold); transform: scaleX(0); transform-origin: left center; }

/* ---------- 8. HERO ----------------------------------------------------- */
/* The hero is built to land inside ONE viewport at 100% zoom, down to a
   768px-tall laptop. Every vertical value below is height-aware for that
   reason — see tools/layout.test.mjs "HERO FITS THE VIEWPORT". */
.hero {
  position: relative; display: flex; align-items: center;
  min-height: 100vh;
  padding-top: calc(var(--nav-h) + clamp(1rem, 3vh, 3rem));
  padding-bottom: clamp(2.75rem, 7.5vh, 5rem);
  overflow: hidden; color: var(--white);
}
@supports (min-height: 100svh) { .hero { min-height: 100svh; } }
.hero__bg { position: absolute; inset: -2%; z-index: 0; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; }
.hero__overlay { position: absolute; inset: 0; z-index: 1; background: var(--grad-hero); }
.hero__inner { position: relative; z-index: 2; width: 100%; }
.hero__content { max-width: 800px; }
/* Deliberately smaller than --fs-h1: the headline sets in two lines here,
   and a third line is what pushed the CTA row past the fold. */
.hero h1 { color: var(--white); font-size: clamp(2.25rem, 1.2rem + 3.2vw, 3.5rem); margin-bottom: var(--s-2); }
.hero__sub { font-size: var(--fs-lead); color: var(--on-dark); max-width: 62ch; margin-bottom: var(--s-3); }
.hero__trust {
  display: inline-flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
  background: rgba(255,255,255,.10); border: 1px solid rgba(244,132,95,.4);
  border-radius: var(--r-pill); padding: 7px 16px; margin-bottom: var(--s-2);
  font-size: var(--fs-small); color: var(--white);
}
.hero__trust strong { color: var(--accent); }
.hero__cta { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-3); }
.hero__micro { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-3); font-size: var(--fs-small); color: var(--on-dark-muted); }
.hero__micro li { display: flex; align-items: center; gap: 6px; }
.hero__micro li::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.word { display: inline-block; overflow: hidden; vertical-align: bottom; }
.word__i { display: inline-block; }

.scroll-hint {
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 2;
  display: none; flex-direction: column; align-items: center; gap: 8px;
  font-size: var(--fs-meta); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--on-dark-muted);
}
.scroll-hint__rail { width: 1px; height: 34px; background: rgba(228,240,237,.3); position: relative; overflow: hidden; }
.scroll-hint__rail .seg { position: absolute; top: -12px; left: 0; width: 1px; height: 12px; background: var(--accent); }
@media (min-width: 1024px) { .scroll-hint { display: flex; } }

/* suburb launcher — funnel entry, not a lead form */
.launcher {
  display: flex; flex-wrap: wrap; gap: var(--s-1); align-items: stretch;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.28);
  padding: 8px; border-radius: var(--r-md); max-width: 560px; margin-bottom: var(--s-2);
  backdrop-filter: blur(6px);
}
.launcher select {
  /* min-width:0 is load-bearing. A flex item defaults to min-width:auto, so
     the select refused to shrink below its widest <option> ("Stellenbosch
     Central, Stellenbosch") and wrapped the Get My Price button onto a second
     row — 58px of hero height spent on a layout accident. */
  flex: 1 1 120px; min-width: 0; min-height: 46px; border: 0; border-radius: var(--r-btn);
  padding: 0 12px; background: var(--white); color: var(--text); font-size: var(--fs-small);
}
/* Wrapping is decided on flex-basis, before any shrinking — so the basis
   above has to be small enough that select + button fit one line at 390px. */
.launcher .btn { flex: 0 0 auto; }

@media (max-width: 639px) {
  /* .scroll-hint is display:none below 1024px, so the bottom padding no longer
     has to reserve clearance for it. */
  .hero { padding-bottom: var(--s-4); }
  /* As a flex row the stars became their own line and the pill turned into a
     lopsided 3-line blob. Flowing it as text keeps the stars inline with the
     copy; the shape stops pretending to be a pill once it wraps. */
  .hero__trust { display: block; font-size: var(--fs-meta); padding: 7px 14px; border-radius: var(--r-lg); }
  .hero__trust .stars { display: inline-flex; vertical-align: -2px; margin-right: 6px; }
  .hero__trust .stars svg { width: 13px; height: 13px; }
  .launcher .btn { padding-inline: .85rem; font-size: var(--fs-meta); }
  .launcher select { padding: 0 8px; font-size: var(--fs-meta); }
  /* A centred bullet against a two-line item floats away from its first line. */
  .hero__micro li { align-items: flex-start; }
  .hero__micro li::before { margin-top: .5em; }
  /* Two columns beats four stacked rows: same four proofs, half the height. */
  .hero__micro { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-1) var(--s-2); font-size: var(--fs-meta); align-items: start; }
}

/* ---------- 9. PAGE HERO (inner pages) ---------------------------------- */
.pagehero {
  position: relative; padding-top: calc(var(--nav-h) + var(--s-6)); padding-bottom: var(--s-6);
  overflow: hidden; color: var(--white);
}
.pagehero__bg { position: absolute; inset: 0; z-index: 0; }
.pagehero__bg img { width: 100%; height: 100%; object-fit: cover; }
.pagehero::after { content: ''; position: absolute; inset: 0; z-index: 1; background: var(--grad-hero); }
.pagehero .container { position: relative; z-index: 2; }
.pagehero h1 { color: var(--white); max-width: 20ch; margin-bottom: var(--s-2); }
.pagehero p { color: var(--on-dark); font-size: var(--fs-lead); }
.crumbs { font-size: var(--fs-small); color: var(--on-dark-muted); margin-bottom: var(--s-2); }
.crumbs a { color: var(--on-dark-muted); text-decoration: none; }
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs span { margin-inline: 6px; }

/* ---------- 10. TRUST BAR ----------------------------------------------- */
.trustbar { background: var(--secondary); color: var(--on-dark); padding-block: var(--s-3); }
.trustbar ul { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--s-2) var(--s-3); }
@media (min-width: 900px) { .trustbar ul { grid-template-columns: repeat(4,minmax(0,1fr)); } }
.trustbar li { display: flex; align-items: center; gap: 10px; font-size: var(--fs-small); }
.trustbar svg { width: 22px; height: 22px; color: var(--accent); flex: 0 0 auto; }
.trustbar strong { color: var(--white); }

/* ---------- 11. CARDS --------------------------------------------------- */
/* Edge-lit surface. White on the page surface has almost no edge of its
   own, so the card is defined by a 1px rule that IGNITES on hover: ::before is
   masked down to a 1px ring and fades a primary→accent gradient in over the
   static border. The ring is masked, not background-clipped, so any rule that
   re-declares `background` on a card (.card--tail does) keeps its edge.
   Per the design standard: border at rest, shadow only on hover — never both. */
.card {
  position: relative;
  /* A grid item defaults to min-width:auto and so refuses to shrink below its
     own min-content, overflowing the track even after the track itself became
     minmax(0,1fr). That is how the /contact form stayed 423px wide inside a
     312px column at 360px — its <select> min-content is the widest option,
     "Office or commercial cleaning". Same trap as .launcher select in the
     hero and .tile in the pickers. */
  min-width: 0;
  display: flex; flex-direction: column; height: 100%;
  background: linear-gradient(180deg, #FFFFFF 0%, #FAFCFB 100%);
  border: 1px solid rgba(11,59,60,.16); border-radius: var(--r-lg);
  padding: var(--card-pad); text-decoration: none; color: inherit;
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base) var(--ease-out);
}
.card::before {
  content: ''; position: absolute; inset: -1px; border-radius: var(--r-lg);
  padding: 1px; background: var(--grad-edge); pointer-events: none;
  /* Masked to the 1px band only: paint the whole box, punch out the content
     box, keep the difference. No z-index — the ring sits over the border, and
     --card-pad guarantees no text is ever within 1px of it. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0; transition: opacity var(--t-base) var(--ease-out);
}
a.card:hover, .card--hover:hover {
  transform: translateY(-4px);
  box-shadow: 0 18px 38px -18px rgba(11,59,60,.34), 0 2px 6px rgba(11,59,60,.05);
}
a.card:hover::before, .card--hover:hover::before { opacity: 1; }

/* An open ring, not a filled chip — the chip was the most dated thing on the
   card. It fills with pine and the glyph goes coral on hover. */
/* A pine jewel with a coral glyph, not a pale lavender chip. The open ring I
   tried first read as washed-out and gave the card no anchor at rest; this is
   the brand mark in miniature, and it flips to solid coral on hover. */
.card__icon {
  width: 52px; height: 52px; border-radius: var(--r-md);
  background: var(--grad-cta); border: 1px solid rgba(244,132,95,.30);
  color: var(--accent); display: grid; place-items: center; margin-bottom: var(--s-3);
  transition: background-color var(--t-base) var(--ease-out), color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.card__icon svg { width: 26px; height: 26px; }
a.card:hover .card__icon {
  background: var(--grad-gold); border-color: var(--accent-soft);
  color: var(--secondary); transform: translateY(-2px);
  box-shadow: 0 6px 18px -6px rgba(244,132,95,.65);
}
.card h3, .card h4 { margin-bottom: var(--s-1); }
/* Service-card title: sized so every service name in the catalogue fits on ONE
   line at the 3-column desktop width. Do not raise this without re-checking the
   longest name ("Move In / Move Out"). */
.card__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.25rem, 1.12rem + 0.4vw, 1.4rem);
  line-height: 1.2; letter-spacing: -0.02em; margin-bottom: var(--s-1);
  text-wrap: balance;
  transition: color var(--t-base) var(--ease-out);
}
a.card:hover .card__title { color: var(--primary); }
.card p { color: var(--text-muted); font-size: var(--fs-small); margin-bottom: var(--s-3); text-wrap: pretty; }
/* Cards without a footer (the how-it-works steps) must not carry that margin. */
.card p:last-child { margin-bottom: 0; }
/* Catalogue index. Reads the grid as a system rather than eight loose boxes,
   and it costs nothing — the top-right of every card was empty. */
.card__index {
  position: absolute; top: var(--card-pad); right: var(--card-pad);
  font-size: var(--fs-meta); font-weight: 700; font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-caps); color: var(--primary); opacity: .22;
  transition: opacity var(--t-base) var(--ease-out), color var(--t-base) var(--ease-out);
}
a.card:hover .card__index { color: var(--accent-ink); opacity: .85; }
/* Footer sits on two clean lines: price row, then the link. Everything inside a
   row is nowrap — the old space-between layout wrapped "See details" onto two
   lines and split "R335 FROM · 3H MIN" across three.
   The full-width grey rule read like a document; it is now a faint track with a
   lit accent segment that extends on hover — an instrument reading, not a divider. */
.card__foot {
  margin-top: auto;            /* pushes the footer to the bottom of an equal-height card */
  padding-top: var(--s-3);
  border-top: 1px solid rgba(11,59,60,.10);
  position: relative;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-2); flex-wrap: nowrap;
}
.card__foot::before {
  content: ''; position: absolute; top: -1px; left: 0; height: 2px; width: 32px;
  border-radius: 2px; background: var(--grad-gold);
  transition: width var(--t-base) var(--ease-out);
}
a.card:hover .card__foot::before { width: 76px; }
/* Tabular figures so prices sit in a column down the grid instead of jittering. */
.card__price {
  font-weight: 700; color: var(--primary); white-space: nowrap; font-size: 1.1875rem;
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.card__price i {
  font-style: normal; font-weight: 600; font-size: var(--fs-meta);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted);
  margin-right: 5px;
}
.card__price small {
  font-weight: 500; font-size: var(--fs-meta); color: var(--text-muted);
  text-transform: none; letter-spacing: 0; margin-left: 2px;
}
.card__link {
  color: var(--primary); font-weight: 700; font-size: var(--fs-small);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; flex: 0 0 auto;
}
/* minmax(0,1fr) stops the TRACK being forced open, but the footer's two nowrap
   halves ("from R95/hr" + "Open the calculator") still need somewhere to go
   once the card is narrower than their sum — otherwise they just overflow the
   card instead of the page. Wrapping to two rows is the only option that keeps
   both the price and the action visible; below 1024px there is no third card
   in the row to stay level with, so nothing is lost. Desktop is untouched,
   which is what keeps the layout test's shared-footer-baseline group green. */
@media (max-width: 1023px) {
  .card__foot { flex-wrap: wrap; row-gap: 10px; }
}
a.card:hover .card__link { color: var(--primary-dark); }
.card__media { margin: calc(var(--card-pad) * -1) calc(var(--card-pad) * -1) var(--s-3); border-radius: var(--r-md) var(--r-md) 0 0; overflow: hidden; height: 200px; }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 400ms var(--ease-out); }
a.card:hover .card__media img { transform: scale(1.05); }

/* Tail card that completes the 3x3 service grid. */
.card--tail {
  background: var(--grad-cta); border-color: rgba(244,132,95,.28); color: var(--on-dark);
  justify-content: center;
}
.card--tail::before { background: var(--grad-edge-dark); }
.card--tail h3 { color: var(--white); }
.card--tail p { color: var(--on-dark-muted); }
.card--tail .card__icon {
  background: rgba(244,132,95,.14); border-color: rgba(244,132,95,.42); color: var(--accent);
}
a.card--tail:hover .card__icon { background: var(--accent); border-color: var(--accent); color: var(--secondary); }
a.card--tail:hover .card__title { color: var(--white); }
.card--tail .card__foot { border-top-color: rgba(228,240,237,.18); }
.card--tail .card__link { color: var(--accent); }
.card--tail .card__price { color: var(--white); }
.card--tail .card__price i,
.card--tail .card__price small { color: var(--on-dark-muted); }
a.card--tail:hover .card__link { color: var(--accent-soft); }

/* ---------- 12. RAIL (pinned horizontal, desktop) ------------------------ */
[data-rail] { position: relative; }
/* The pinned desktop rail needs a full-bleed container so the track can scroll
   past the viewport edge, and it re-adds the gutter on .rail__track itself.
   Below 1024px there is no pin: the track is a plain vertical stack, so the
   container has to carry the page gutter like every other section. This used
   to be an inline `style="max-width:none;padding:0"` in tools/pages.mjs, which
   applied at EVERY width — so on a phone the whole "How it works" section (the
   eyebrow, the H2, the lead, the Start My Booking button and all four step
   cards) sat hard against both screen edges with zero padding. Measured on
   live at 360/390/430px: every one of those elements had left = 0. */
[data-rail] > .container { max-width: none; padding-inline: var(--gutter); }
@media (min-width: 1024px) { [data-rail] > .container { padding-inline: 0; } }
.rail__track { display: flex; gap: var(--s-4); }
.rail__panel { flex: 0 0 300px; }
@media (min-width: 1024px) {
  /* The 100vh height IS the section's spacing while pinned. Leaving the normal
     section padding on top of it added ~192px of dead space below the cards and
     pushed the panels off centre. */
  [data-rail] {
    /* Capped below 100vh: a full-height pin around 300px-tall cards leaves the
       content marooned in the middle of the screen. This keeps the takeover
       feeling deliberate while the panels still fill most of it. */
    height: min(100vh, 780px); min-height: 560px;
    overflow: hidden; display: flex; align-items: center; padding-block: 0;
  }
  /* Start the first panel on the same vertical line as every other section's
     content, instead of hard against the viewport edge. */
  .rail__track {
    padding-inline: max(var(--gutter), calc((100vw - var(--container)) / 2));
    will-change: transform;
  }
  .rail__panel { flex: 0 0 360px; }
  /* Give the step cards real presence so they occupy the pinned viewport
     instead of floating in it. */
  .rail__panel > .card { min-height: 340px; padding: var(--s-4); }
  .rail__panel .step__n { font-size: 3rem; margin-bottom: var(--s-2); }
  .rail__panel .card h3 { font-size: 1.5rem; margin-bottom: var(--s-2); }
  .rail__panel:last-child { padding-right: var(--gutter); }
  /* The lead panel carries the section heading and needs more room than a card. */
  .rail__panel--lead { flex: 0 0 clamp(360px, 32vw, 440px); }
}
@media (max-width: 1023px) {
  .rail__track { flex-direction: column; }
  .rail__panel { flex: 1 1 auto; }
}

/* ---------- 13. STEPS --------------------------------------------------- */
.steps { display: grid; gap: var(--s-4); position: relative; }
@media (min-width: 900px) { .steps { grid-template-columns: repeat(3,minmax(0,1fr)); gap: var(--s-5); } }
.step__n {
  font-family: var(--font-display); font-size: 2.5rem; font-weight: 700;
  color: var(--accent); line-height: 1; margin-bottom: var(--s-1);
}
.step h3 { margin-bottom: var(--s-1); }
.step p { color: var(--text-muted); }
.steps__line { display: none; }
@media (min-width: 900px) {
  .steps__line { display: block; position: absolute; top: 22px; left: 8%; right: 8%; height: 2px; background: var(--line); z-index: 0; }
  .steps__line i { display: block; height: 100%; background: var(--accent); transform: scaleX(0); transform-origin: left center; }
  .step { position: relative; z-index: 1; }
}

/* ---------- 14. STATS --------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-4); }
.stat__rule { display: block; width: 40px; height: 3px; background: var(--accent); margin-bottom: var(--s-2); transform-origin: left center; }
.stat__num { font-family: var(--font-display); font-size: clamp(2.25rem,1.6rem + 2.6vw,3.5rem); font-weight: 700; line-height: 1; color: var(--white); }
.stat__label { font-size: var(--fs-small); color: var(--on-dark-muted); margin-top: 6px; }

/* ---------- 15. REVIEWS ------------------------------------------------- */
.review { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--card-pad); height: 100%; display: flex; flex-direction: column; }
.stars { display: inline-flex; gap: 2px; color: var(--accent); margin-bottom: var(--s-2); }
.stars svg { width: 18px; height: 18px; }
.review__text { font-style: italic; color: var(--text); margin-bottom: var(--s-3); }
.review__who { margin-top: auto; display: flex; align-items: center; gap: 12px; }
.review__avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--primary); color: var(--white); display: grid; place-items: center; font-weight: 700; flex: 0 0 auto; }
.review__meta { font-size: var(--fs-small); line-height: 1.4; }
.review__meta strong { display: block; }
.review__meta span { color: var(--text-muted); }

/* ---------- 16. SQUEEGEE (signature) ------------------------------------ */
.squeegee { position: relative; border-radius: var(--r-lg); overflow: hidden; aspect-ratio: 16/9; background: var(--surface-2); }
.squeegee img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.squeegee__after { clip-path: inset(0 0 0 0); }
.squeegee__edge { position: absolute; top: 0; bottom: 0; left: 0; width: 3px; background: var(--grad-gold); filter: blur(1px); box-shadow: 0 0 18px 3px rgba(244,132,95,.6); display: none; }
.squeegee__tag { position: absolute; bottom: 12px; z-index: 3; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-caps); font-weight: 700; background: rgba(6,38,39,.82); color: var(--white); padding: 6px 12px; border-radius: var(--r-pill); }
.squeegee__tag--before { left: 12px; } .squeegee__tag--after { right: 12px; color: var(--accent); }
@media (min-width: 1024px) { .squeegee__edge { display: block; } }

/* ---------- 17. POLISH (signature) -------------------------------------- */
.polish { position: relative; display: inline-block; overflow: hidden; }
.polish__gloss {
  position: absolute; inset: -20% -10%; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(255,255,255,.85) 50%, transparent 62%);
  transform: translateX(-130%); mix-blend-mode: overlay;
}

/* ---------- 18. ACCORDION ----------------------------------------------- */
.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  background: none; border: 0; padding: var(--s-3) 0; text-align: left; cursor: pointer;
  font-family: var(--font-body); font-size: var(--fs-h4); font-weight: 700; color: var(--text);
}
.acc__btn:hover { color: var(--primary); }
.acc__icon { position: relative; width: 22px; height: 22px; flex: 0 0 auto; color: var(--accent-ink); }
.acc__icon .h, .acc__icon .v { position: absolute; background: currentColor; }
.acc__icon .h { top: 10px; left: 0; width: 22px; height: 2px; }
.acc__icon .v { top: 0; left: 10px; width: 2px; height: 22px; }
.acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--t-slow) var(--ease-in-out); }
.acc__panel > div { overflow: hidden; padding-bottom: 0; }
.acc__item.is-open .acc__panel { grid-template-rows: 1fr; }
.acc__item.is-open .acc__panel > div { padding-bottom: var(--s-3); }
.acc__icon .v { transition: transform var(--t-base) var(--ease-out), opacity var(--t-fast); }
.acc__item.is-open .acc__icon .v { transform: rotate(90deg); opacity: 0; }
.acc__panel p { color: var(--text-muted); }

/* ---------- 19. FORMS --------------------------------------------------- */
.field { margin-bottom: var(--s-3); }
.field > label, .field__legend { display: block; font-size: var(--fs-small); font-weight: 600; margin-bottom: 6px; }
.field .hint { display: block; font-size: var(--fs-small); color: var(--text-muted); margin-top: 6px; }
.input, .select, .textarea {
  width: 100%; min-height: 48px; padding: 12px 14px;
  background: var(--white); border: 1px solid var(--border-input); border-radius: var(--r-sm);
  color: var(--text); transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.textarea { min-height: 120px; resize: vertical; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--primary); }
.input:focus, .select:focus, .textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(11,59,60,.14); }
.field.is-error .input, .field.is-error .select, .field.is-error .textarea { border-color: var(--danger); background: var(--danger-tint); }
.field .error { display: none; font-size: var(--fs-small); color: var(--danger); font-weight: 600; margin-top: 6px; }
.field.is-error .error { display: block; }
fieldset { border: 0; padding: 0; }

.check { display: flex; align-items: flex-start; gap: 12px; cursor: pointer; font-size: var(--fs-small); }
.check input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.check__box {
  width: 24px; height: 24px; flex: 0 0 auto; border: 2px solid var(--border-input); border-radius: var(--r-btn);
  background: var(--white); display: grid; place-items: center; transition: all var(--t-fast);
}
.check__box svg { width: 15px; height: 15px; color: var(--text); transform: scale(0); transition: transform var(--t-base) var(--ease-pop); }
.check input:checked + .check__box { background: var(--accent); border-color: var(--accent); }
.check input:checked + .check__box svg { transform: scale(1); }
.check input:focus-visible + .check__box { outline: 3px solid var(--primary); outline-offset: 2px; }

/* option tiles (services, extras, frequency) */
.tiles { display: grid; gap: var(--s-2); grid-template-columns: minmax(0,1fr); }
@media (min-width: 560px) { .tiles { grid-template-columns: repeat(2,minmax(0,1fr)); } }
@media (min-width: 900px) { .tiles--3 { grid-template-columns: repeat(3,minmax(0,1fr)); } .tiles--4 { grid-template-columns: repeat(4,minmax(0,1fr)); } }
.tile { position: relative; display: block; cursor: pointer; height: 100%; min-width: 0; }
.tile input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.tile__body {
  display: flex; flex-direction: column; justify-content: flex-start;
  height: 100%; min-height: 72px;
  background: var(--white); border: 2px solid var(--border-input); border-radius: var(--r-md);
  padding: var(--s-3); transition: border-color var(--t-base) var(--ease-out),
    background-color var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}
/* One-line tiles (the Pro funnel's experience bands) do not need the 72px
   body a three-line service tile does — six of them stacked at full height is
   620px of scrolling for a single question on a phone. */
.tiles--compact { grid-template-columns: repeat(2,minmax(0,1fr)); }
.tiles--compact .tile__body { min-height: 0; padding: var(--s-2) var(--s-3); justify-content: center; }
.tile:hover .tile__body { border-color: var(--primary); transform: translateY(-2px); }
.tile input:checked + .tile__body { border-color: var(--primary); background: var(--primary-tint); box-shadow: inset 0 0 0 1px var(--primary); }
.tile input:focus-visible + .tile__body { outline: 3px solid var(--accent); outline-offset: 2px; }
.tile__title { font-weight: 700; display: flex; align-items: center; justify-content: space-between; gap: var(--s-1); line-height: 1.3; }
.tile__meta { font-size: var(--fs-small); color: var(--text-muted); margin-top: 4px; }
.tile__tick { width: 22px; height: 22px; border-radius: 50%; background: var(--primary); color: var(--white); display: none; place-items: center; flex: 0 0 auto; }
.tile input:checked + .tile__body .tile__tick { display: grid; }

/* stepper */
.stepper { display: inline-flex; align-items: center; gap: var(--s-1); }
.stepper button {
  width: 44px; height: 44px; border-radius: var(--r-sm); border: 2px solid var(--border-input);
  background: var(--white); cursor: pointer; font-size: 1.25rem; font-weight: 700; color: var(--primary);
  display: grid; place-items: center; transition: all var(--t-fast);
}
.stepper button:hover:not([disabled]) { border-color: var(--primary); background: var(--primary-tint); }
.stepper button[disabled] { opacity: .4; cursor: not-allowed; }
.stepper output { min-width: 72px; text-align: center; font-weight: 700; font-size: 1.25rem; }

/* ---------- 20. FUNNEL -------------------------------------------------- */
.funnel { padding-top: calc(var(--nav-h) + var(--s-4)); padding-bottom: var(--s-7); }
.funnel__grid { display: grid; gap: var(--s-4); align-items: start; }
@media (min-width: 1024px) { .funnel__grid { grid-template-columns: minmax(0,1fr) 340px; gap: var(--s-5); } }

.prog { margin-bottom: var(--s-4); }
.prog__rail { position: relative; height: 4px; background: var(--line); border-radius: var(--r-pill); overflow: hidden; }
.prog__fill { height: 100%; background: var(--grad-gold); transform: scaleX(0); transform-origin: left center; }
.prog__dots { display: flex; justify-content: space-between; margin-top: 10px; gap: 4px; }
.prog__dot { font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 700; background: none; border: 0; padding: 2px; cursor: default; }
.prog__dot.is-current { color: var(--primary); }
.prog__dot.is-done { color: var(--ok); cursor: pointer; }
@media (max-width: 767px) { .prog__dots { display: none; } }
.prog__count { font-size: var(--fs-small); color: var(--text-muted); margin-bottom: 6px; font-weight: 600; }

.stage { position: relative; }
.panel { display: none; }
.panel.is-active { display: block; }
.panel h2 { font-size: clamp(1.5rem,1.2rem + 1.2vw,2rem); margin-bottom: var(--s-1); }
.panel h2:focus { outline: none; }
.panel > .lead { margin-bottom: var(--s-3); }
.panel__nav { display: flex; gap: var(--s-2); margin-top: var(--s-4); flex-wrap: wrap; }
.panel__nav .btn { flex: 1 1 auto; }
@media (min-width: 560px) { .panel__nav .btn { flex: 0 0 auto; } }

.summary {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--card-pad);
}
@media (min-width: 1024px) { .summary { position: sticky; top: calc(var(--nav-h) + var(--s-2)); } }
.summary h3 { font-size: 1.125rem; font-family: var(--font-body); font-weight: 700; margin-bottom: var(--s-2); }
.summary__price { font-family: var(--font-body); font-size: var(--fs-price); font-weight: 700; color: var(--primary); display: inline-block; transform-origin: left center; }
.summary__rows { margin: var(--s-2) 0; font-size: var(--fs-small); }
.summary__row { display: flex; justify-content: space-between; gap: var(--s-2); padding: 5px 0; border-bottom: 1px dashed var(--line); }
/* Labels are always short ("Phone", "Total", "Based in"); values are not. Pin
   the label to one line so the wrapping happens in the value column, where
   there is room, instead of splitting "Based / in" down the left. */
.summary__row span:first-child { white-space: nowrap; }
.summary__row:last-child { border-bottom: 0; }
/* The value column used to be white-space: nowrap so a price never broke
   across two lines. It does not need to be: formatZAR already groups with a
   NON-BREAKING space (U+00A0), so "R2 093" is unbreakable on its own. The
   nowrap only ever applied to the PROSE values this component also carries —
   "Cape Town, Western Cape, South Africa" on /contact (310px wide, the last
   thing pushing that page to 422px at 360px), plus "Where"/"Your Pro" in the
   booking lookup and the once-off/weekly pair on /pricing. Wrapping is right
   for those and changes nothing for a price. */
.summary__row span:last-child { font-weight: 600; text-align: right; min-width: 0; overflow-wrap: anywhere; }
.summary__row--discount span:last-child { color: var(--ok); }
.summary__row--total { border-top: 2px solid var(--line-strong); border-bottom: 0; margin-top: 6px; padding-top: 10px; font-size: var(--fs-body); font-weight: 700; }
.summary__note { font-size: var(--fs-meta); color: var(--text-muted); margin-top: var(--s-2); }
.summary__empty { color: var(--text-muted); font-size: var(--fs-small); }

/* mobile sticky price bar */
.pricebar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky);
  background: var(--white); border-top: 1px solid var(--line-strong);
  padding: 10px var(--gutter) calc(10px + env(safe-area-inset-bottom));
  display: none; align-items: center; gap: var(--s-2); box-shadow: 0 -4px 18px rgba(13,28,28,.10);
}
.pricebar.is-on { display: flex; }
.pricebar__l { flex: 1 1 auto; line-height: 1.2; }
.pricebar__l small { display: block; font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: var(--ls-caps); color: var(--text-muted); font-weight: 700; }
.pricebar__p { font-size: 1.4rem; font-weight: 700; color: var(--primary); display: inline-block; transform-origin: left center; }
@media (min-width: 1024px) { .pricebar { display: none !important; } }
body.has-pricebar { padding-bottom: var(--pricebar-h); }
@media (min-width: 1024px) { body.has-pricebar { padding-bottom: 0; } }

/* ---------- 21. TABLES -------------------------------------------------- */
.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--white); }
table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
thead th {
  text-align: left; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-caps);
  color: var(--text-muted); padding: 14px 16px; border-bottom: 2px solid var(--line-strong); white-space: nowrap;
}
tbody td { padding: 14px 16px; border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:nth-child(even) { background: var(--surface); }
td.num, th.num { text-align: right; white-space: nowrap; }

/* ---------- 22. LISTS --------------------------------------------------- */
.ticks li, .crosses li { display: flex; gap: 12px; padding: 8px 0; align-items: flex-start; }
.ticks svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--ok); margin-top: 3px; }
/* Compact variant for the Become-a-Pro funnel rail. */
.ticks--tight li { padding: 5px 0; font-size: var(--fs-small); }
.ticks--tight svg { width: 17px; height: 17px; margin-top: 2px; }
/* Final-step read-back on the Pro funnel. */
.reviewlist { border-top: 1px solid var(--line-strong); }
.reviewlist > div { display: flex; justify-content: space-between; gap: var(--s-3); padding: 10px 0; border-bottom: 1px solid var(--line); }
.reviewlist dt { color: var(--text-muted); font-size: var(--fs-small); flex: 0 0 auto; }
.reviewlist dd { font-weight: 600; text-align: right; overflow-wrap: anywhere; }
.crosses svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--danger); margin-top: 3px; }
.split { display: grid; gap: var(--s-4); align-items: center; }
.split > * { min-width: 0; }
@media (min-width: 900px) { .split { grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: var(--s-6); } .split--wide { grid-template-columns: minmax(0,1.15fr) minmax(0,.85fr); } }
.split__text { max-width: 600px; }
.split__media { border-radius: var(--r-lg); overflow: hidden; position: relative; height: 480px; }
.split__media img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 899px) { .split__media { height: 300px; } }

/* ---------- 23. CTA BANNER + FOOTER ------------------------------------- */
.ctaband { background: var(--grad-cta); color: var(--on-dark); text-align: center; padding-block: var(--section-y); border-top: 1px solid rgba(244,132,95,.28); }
.ctaband h2 { color: var(--white); margin-bottom: var(--s-2); }
.ctaband p { color: var(--on-dark); margin-inline: auto; margin-bottom: var(--s-4); }
.ctaband__btns { display: flex; gap: var(--s-2); justify-content: center; flex-wrap: wrap; }

.footer { background: var(--secondary); color: var(--on-dark-muted); padding-block: var(--s-6) var(--s-4); font-size: var(--fs-small); }
.footer__grid { display: grid; gap: var(--s-4); grid-template-columns: minmax(0,1fr); }
@media (min-width: 640px) { .footer__grid { grid-template-columns: repeat(2,minmax(0,1fr)); } }
@media (min-width: 1024px) { .footer__grid { grid-template-columns: minmax(0,1.4fr) repeat(3,minmax(0,1fr)); gap: var(--s-5); } }
.footer h3 { font-family: var(--font-body); font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-caps); color: var(--accent); font-weight: 700; margin-bottom: var(--s-2); }
.footer a { color: var(--on-dark-muted); text-decoration: none; }
.footer a:hover { color: var(--white); text-decoration: underline; }
.footer li { padding: 4px 0; }
.footer__brand { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; color: var(--white); font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; margin-bottom: var(--s-2); text-decoration: none; }
.footer__brand em { font-style: normal; color: var(--accent); }
.footer__brand svg { width: 28px; height: 28px; }
.footer__bottom { border-top: 1px solid rgba(228,240,237,.14); margin-top: var(--s-5); padding-top: var(--s-3); display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-3); justify-content: space-between; font-size: var(--fs-meta); }
/* Demo notices on forms: Supreme Cleans is fictional and its inbox is shared. */
.form-demo { display: flex; gap: 10px; align-items: flex-start; max-width: none; margin: var(--s-2) 0; font-size: var(--fs-meta); color: var(--text-muted); }
.form-demo svg { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; color: var(--accent-ink); }
.footer__demo { margin-top: var(--s-2); max-width: none; font-size: var(--fs-meta); color: var(--on-dark-muted); }
.footer__demo a { color: var(--accent); }
/* Demo reset, styled as a link so it reads as part of the demo sentence. */
.footer__reset { display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px; background: none; border: 0;
  color: var(--accent); font: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.footer__reset[aria-disabled="true"] { opacity: .6; pointer-events: none; }
.footer__areas { color: var(--on-dark-muted); line-height: 1.9; }

/* ---------- 24. WHATSAPP FLOAT ------------------------------------------ */
.wa {
  position: fixed; right: 18px; bottom: 18px; z-index: var(--z-wa);
  width: var(--wa-size); height: var(--wa-size); border-radius: 50%;
  background: #25D366; color: #fff; display: grid; place-items: center;
  box-shadow: 0 6px 20px rgba(0,0,0,.28); text-decoration: none;
  transition: transform var(--t-base) var(--ease-out);
}
.wa:hover { transform: scale(1.07); }
.wa svg { width: 32px; height: 32px; }
.wa__ring { position: absolute; inset: 0; border-radius: 50%; border: 2px solid #25D366; opacity: 0; pointer-events: none; }
body.has-pricebar .wa { bottom: calc(var(--pricebar-h) + 12px); }
@media (min-width: 1024px) { body.has-pricebar .wa { bottom: 18px; } }

/* ---------- 25. TOASTS -------------------------------------------------- */
.toasts { position: fixed; right: 16px; bottom: 90px; z-index: var(--z-toast); display: flex; flex-direction: column; gap: 10px; max-width: min(360px, calc(100vw - 32px)); }
.toast { background: var(--secondary); color: var(--on-dark); border-left: 4px solid var(--accent); border-radius: var(--r-sm); padding: 14px 16px; box-shadow: var(--sh-lg); font-size: var(--fs-small); }
.toast--ok { border-left-color: var(--ok); }
.toast--error { border-left-color: var(--danger); }

/* ---------- 26. BADGES / PILLS ------------------------------------------ */
.pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: var(--ls-caps); padding: 5px 12px; border-radius: var(--r-pill); }
.pill--ok { background: var(--ok-tint); color: var(--ok); }
.pill--warn { background: var(--warn-tint); color: var(--warn); }
.pill--danger { background: var(--danger-tint); color: var(--danger); }
.pill--info { background: var(--primary-tint); color: var(--primary); }

.note {
  display: flex; gap: 12px; align-items: flex-start;
  background: var(--warn-tint); border-left: 4px solid var(--accent);
  border-radius: var(--r-sm); padding: var(--s-2) var(--s-3); font-size: var(--fs-small);
}
.note svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--accent-ink); margin-top: 2px; }
.note strong { color: var(--text); }
.note p { max-width: none; }

/* ---------- 27. UTILITIES ----------------------------------------------- */
/* <small> defaults to 0.8em, which resolves to 13.3px against --fs-body and
   put the "4 hr" notes in the /pricing comparison table under the 14px floor
   this scale already declares as its minimum. */
small { font-size: var(--fs-meta); }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.mt-1{margin-top:var(--s-1)} .mt-2{margin-top:var(--s-2)} .mt-3{margin-top:var(--s-3)} .mt-4{margin-top:var(--s-4)} .mt-5{margin-top:var(--s-5)}
.mb-2{margin-bottom:var(--s-2)} .mb-3{margin-bottom:var(--s-3)} .mb-4{margin-bottom:var(--s-4)}
.center{text-align:center} .muted{color:var(--text-muted)} .nowrap{white-space:nowrap}

/* ---------- 28. FAIL-OPEN REVEAL GATE ----------------------------------- */
/* Content is VISIBLE by default. Only .fx (added by motion.js AFTER GSAP is
   confirmed present) hides it. .fx-rescue un-hides everything and always wins. */
.fx [data-reveal]         { opacity: 0; will-change: transform, opacity; }
.fx [data-reveal="up"]    { transform: translate3d(0,28px,0); }
.fx [data-reveal="left"]  { transform: translate3d(-28px,0,0); }
.fx [data-reveal="right"] { transform: translate3d(28px,0,0); }
.fx [data-reveal="scale"] { transform: scale(.96); }
/* The lines rise into place while the heading fades in. The travel used to be
   110% — a full line-height — which was invisible only because the heading
   itself was stuck at opacity 0 and nobody ever saw the motion. With the
   heading actually revealing, a 110% slide runs the second line straight over
   the paragraph below it: the gap to the next element is 16px. A true mask is
   not available either — measured on every masked heading at 390 and 1440,
   the span boxes are taller than the h2's content box (scrollHeight >
   clientHeight), so `overflow: hidden` would shave the descenders off
   "publish" and "everyone". 0.3em travels less than that 16px gap at every
   size, so nothing can overlap and nothing is clipped. */
.fx [data-reveal="mask"] > span { display: block; transform: translate3d(0,0.3em,0); }

html.fx-rescue [data-reveal],
html.fx-rescue [data-reveal] > span {
  opacity: 1 !important; transform: none !important; visibility: visible !important;
}

/* ---------- 29. REDUCED MOTION ------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  [data-reveal], [data-reveal] > span { opacity: 1 !important; transform: none !important; }
}
