/* ==========================================================================
   1st4FencingUK — design system
   Identity taken from the company's existing logo: red #D73932 wordmark over a
   slate-blue #425979 strapline. Red is kept scarce (CTAs, rules, indices, the
   one red band) so it stays loud; slate carries structure; a family of warm
   off-whites separates sections without the page reading cream or grey.
   Type: Bricolage Grotesque (display) + Archivo (UI/body).
   ========================================================================== */
:root {
  /* --- Brand, straight from the logo ------------------------------------ */
  --brand: #D73932;          /* logo red — buttons, marks, rules */
  --brand-deep: #C4302A;     /* red band ground; 5.5:1 with white */
  --brand-strong: #B62E28;   /* red as text/links on white; 6.2:1 */
  --brand-light: #E9564D;    /* red on dark grounds */

  --slate: #425979;          /* logo strapline slate */
  --slate-deep: #1C2740;     /* supply-only section ground */
  --slate-darkest: #141C2E;  /* footer ground */

  /* --- Neutral grounds: subtle, still bright ---------------------------- */
  --paper: #FFFFFF;          /* services, cards */
  --paper-soft: #FDFCFA;     /* page default, trust, testimonials */
  --paper-warm: #F7F5F1;     /* gallery */
  --paper-stone: #F4F2EE;    /* areas */

  /* Aliases kept so component rules read by intent */
  --bg: var(--paper-soft);
  --bg-alt: var(--paper-stone);
  --surface: var(--paper);
  --surface-raised: var(--paper);

  /* --- Ink -------------------------------------------------------------- */
  --text: #16202F;
  --text-secondary: #46536A;
  --text-muted: #636E82;  /* clears AA (4.5:1) on every ground in the neutral family */
  --on-brand: #FFFFFF;       /* text on red */

  /* On dark grounds */
  --text-on-dark: #F2EEE7;
  --text-on-dark-secondary: #C2CADA;
  --text-on-dark-muted: #94A0B5;

  /* --- Lines ------------------------------------------------------------ */
  --line: rgba(22, 32, 47, 0.10);
  --line-strong: rgba(22, 32, 47, 0.22);
  --line-on-dark: rgba(242, 238, 231, 0.14);
  --line-on-dark-strong: rgba(242, 238, 231, 0.30);

  /* --- States ----------------------------------------------------------- */
  --error: #C0392B;
  --success: #2E7D50;

  /* --- Type ------------------------------------------------------------- */
  --font: "Archivo", system-ui, -apple-system, sans-serif;
  /* Headings only, matched to the wordmark: the same squared techno
     construction as the logo, with the chamfered C/G/S and the flat-topped A.
     Set in capitals, as the "FENCING SPECIALISTS" strapline is. */
  --font-heading: "Orbitron", "Arial Narrow", system-ui, sans-serif;
  /* Everything else that wants the display voice: the mobile menu, testimonial
     quotes, the trust numerals, the Checkatrade score and the quote builder's
     price figures. Kept on the grotesque — Cinzel has no £ at all, and these
     are the places a missing glyph would show. */
  --font-display: "Bricolage Grotesque", Georgia, serif;
  /* Sized for Orbitron set in caps, which needs about 14x its font-size in
     width for the longest hero line — roughly 58% wider than the grotesque it
     replaced. Measured, not guessed: at these caps each authored hero line
     still lands on a single line, which is what the reveal animation expects. */
  --step-hero: clamp(1.55rem, 3.9vw, 3.6rem);
  --step-h2: clamp(1.2rem, 2.5vw, 2rem);
  --step-h3: 1.15rem;
  --step-body: 1rem;
  --step-small: 0.9rem;
  --step-eyebrow: 0.75rem;

  /* --- Spacing ---------------------------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;
  --space-8: 3.5rem;
  --space-9: 5rem;
  --section-y: clamp(4.5rem, 8vw, 7.5rem);
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --measure: 1320px;

  /* --- Shape ------------------------------------------------------------ */
  --radius-btn: 6px;         /* squared off — reads built, not app-store */
  --radius-card: 12px;
  --radius-input: 6px;
  --radius-chip: 4px;

  /* --- Depth: warm-neutral, never black -------------------------------- */
  --shadow-sm: 0 1px 2px rgba(22, 32, 47, 0.06);
  --shadow: 0 4px 18px rgba(22, 32, 47, 0.07);
  --shadow-lg: 0 18px 48px rgba(22, 32, 47, 0.12);

  /* --- Motion ----------------------------------------------------------- */
  --dur-fast: 160ms;
  --dur: 260ms;
  --dur-slow: 600ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  --header-h: 76px;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--step-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
/* <picture> only exists to offer WebP — it must not introduce a box of its own,
   or the img stops being a direct child for layout purposes. */
picture { display: contents; }

h1, h2 {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  /* Capitals, as the logo strapline is set. The strapline itself is tracked
     right out, but that is a small-size trick — at heading size the same
     spacing would pull the words apart, so it is dialled back. */
  text-transform: uppercase;
  letter-spacing: 0.025em;
  line-height: 1.24;
  text-wrap: balance;
}

h2 { font-size: var(--step-h2); }

h3 {
  margin: 0;
  font-size: var(--step-h3);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  text-wrap: balance;
}

p { margin: 0; }

a { color: inherit; text-decoration: none; }

.icon { width: 1.05em; height: 1.05em; flex-shrink: 0; }

::selection { background: var(--brand); color: var(--on-brand); }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ===== Intro: a fence building itself =====
   Runs on a fixed CSS timeline with no JavaScript in the loop. Two rules make
   it safe: pointer-events never reach it, so it cannot block a click even if it
   somehow stayed on screen; and the fade-out is `forwards`, so the end state is
   permanent whether or not the tab was in the foreground while it played. */
.intro {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--paper-soft);
  pointer-events: none;
  animation: intro-out 0.5s var(--ease-out) 1.35s forwards;
}
.intro-stage { display: grid; justify-items: center; gap: var(--space-6); padding: var(--gutter); }
.intro-fence { width: min(420px, 74vw); height: auto; overflow: visible; }

/* Every part shares one transform-box so the origins below are in its own
   coordinates, not the SVG's. --d is the per-element delay set in the markup.
   Fills are set per part, never here — a type selector would outrank the
   single-class rules below and paint the whole fence one colour. */
.intro-fence rect { transform-box: fill-box; }
.intro-post, .intro-slat { fill: var(--brand); }

.intro-ground {
  fill: var(--line-strong);
  transform-origin: center;
  transform: scaleX(0);
  animation: intro-grow-x 0.5s var(--ease-out) forwards;
}
/* Posts are driven into the ground: they grow upward from their base. */
.intro-post {
  transform-origin: bottom;
  transform: scaleY(0);
  animation: intro-grow-y 0.42s var(--ease-out) var(--d) forwards;
}
/* Gravel boards run out from the post they butt against. */
.intro-board {
  fill: var(--slate);
  transform-origin: left;
  transform: scaleX(0);
  animation: intro-grow-x 0.34s var(--ease-out) var(--d) forwards;
}
/* Boards are dropped in one at a time and settle. */
.intro-slat {
  opacity: 0;
  transform: translateY(-16px);
  animation: intro-drop 0.34s var(--ease-out) var(--d) forwards;
}
.intro-logo {
  width: 166px; height: 26px;
  opacity: 0;
  animation: intro-fade 0.5s var(--ease-out) 0.95s forwards;
}

@keyframes intro-grow-x { to { transform: scaleX(1); } }
@keyframes intro-grow-y { to { transform: scaleY(1); } }
@keyframes intro-drop   { to { opacity: 1; transform: translateY(0); } }
@keyframes intro-fade   { to { opacity: 1; } }
@keyframes intro-out    { to { opacity: 0; visibility: hidden; } }

/* Seen it already this session, or asked not to be animated at: no intro. */
.intro-skip .intro { display: none; }
@media (prefers-reduced-motion: reduce) {
  .intro { display: none; }
}

.skip-link {
  position: fixed;
  top: 0.75rem; left: 0.75rem;
  z-index: 200;
  background: var(--brand);
  color: var(--on-brand);
  font-weight: 600;
  font-size: var(--step-small);
  padding: 0.6em 1.2em;
  border-radius: var(--radius-btn);
  transform: translateY(-300%);
  transition: transform var(--dur) var(--ease-out);
}
.skip-link:focus { transform: none; }

/* Anchor targets clear the fixed header */
section[id], main[id] { scroll-margin-top: calc(var(--header-h) + 12px); }

/* Text for screen readers only — used to spell out where a link goes */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Paper grain — barely there, stops the off-whites reading as flat digital fills */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  opacity: 0.022;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ===== Buttons =====
   One height per size, square-ish corners, no pills. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 46px;
  padding: 0.7em 1.5em;
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  font-weight: 600;
  font-size: 0.94rem;
  line-height: 1.2;
  white-space: nowrap;
  letter-spacing: 0.005em;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--brand); color: var(--on-brand); border-color: var(--brand); }
.btn-primary:hover { background: var(--brand-strong); border-color: var(--brand-strong); box-shadow: var(--shadow); }

.btn-ghost { border-color: var(--line-strong); color: var(--text); background: transparent; }
.btn-ghost:hover { border-color: var(--text); background: rgba(22, 32, 47, 0.04); }

.btn-lg { min-height: 54px; padding: 0.85em 1.9em; font-size: 1rem; }
.btn-wide { width: 100%; }
.btn[disabled] { opacity: 0.5; pointer-events: none; }

/* Arrow nudges on hover — the one motion cue shared by every CTA */
.btn .btn-arrow { transition: transform var(--dur) var(--ease-out); }
.btn:hover .btn-arrow { transform: translateX(3px); }

/* Ghost buttons sitting on dark grounds */
.hero-ctas .btn-ghost {
  border-color: var(--line-on-dark-strong);
  color: var(--text-on-dark);
}
.hero-ctas .btn-ghost:hover {
  border-color: var(--text-on-dark);
  background: rgba(242, 238, 231, 0.10);
}

/* ===== Header ===== */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  color: var(--text-on-dark);
  transition: background var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease-out);
}
/* While floating over the hero photo the bar needs its own scrim, or the logo
   and nav sit on whatever the sky happens to be doing. Fades out once solid. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: calc(var(--header-h) + 44px);
  background: linear-gradient(to bottom, rgba(20, 28, 46, 0.70) 0%, rgba(20, 28, 46, 0.38) 55%, transparent 100%);
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.site-header.scrolled::before,
.site-header--solid::before { opacity: 0; }
.site-header.scrolled,
.site-header--solid {
  /* Opaque rather than blurred: backdrop-filter on a fixed bar re-blurs the page
     behind it every scroll frame, and at this opacity you cannot see the blur. */
  background: rgb(253, 252, 250);
  box-shadow: 0 1px 0 var(--line), var(--shadow-sm);
  color: var(--text);
}
.header-inner {
  position: relative;
  z-index: 1; /* the ::before scrim is absolute, so it would otherwise paint over this */
  max-width: var(--measure);
  margin: 0 auto;
  height: var(--header-h);
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  gap: var(--space-7);
  transition: height var(--dur-slow) var(--ease-out);
}
.site-header.scrolled .header-inner { height: 64px; }

.wordmark { display: inline-flex; align-items: center; }
.wordmark img { width: 166px; height: 26px; object-fit: contain; }
/* Two logo variants: reversed while the header floats over the hero photo,
   full-colour once it sits on the solid light bar. */
.wordmark-on-light { display: none; }
.site-header.scrolled .wordmark-on-dark,
.site-header--solid .wordmark-on-dark { display: none; }
.site-header.scrolled .wordmark-on-light,
.site-header--solid .wordmark-on-light { display: block; }

.header-back { margin-left: auto; }

.main-nav {
  display: flex;
  gap: var(--space-5);
  margin-left: auto;
  font-size: 0.92rem;
  font-weight: 500;
}
.main-nav a {
  position: relative;
  padding: 0.35em 0;
  color: inherit;
  opacity: 0.82;
  transition: opacity var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
/* Rule grows from the left rather than a pill filling in */
.main-nav a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease-out);
}
.main-nav a:hover,
.main-nav a.active { opacity: 1; }
.main-nav a:hover::after,
.main-nav a.active::after { transform: scaleX(1); }

.header-quote { min-height: 42px; padding: 0.55em 1.25em; font-size: 0.9rem; }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px; height: 44px;
  background: none;
  border: 1px solid currentColor;
  border-radius: var(--radius-btn);
  cursor: pointer;
  padding: 0 11px;
  color: inherit;
}
.nav-toggle span {
  display: block;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur);
}
.nav-toggle.open span:first-child { transform: translateY(3.5px) rotate(45deg); }
.nav-toggle.open span:last-child { transform: translateY(-3.5px) rotate(-45deg); }

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--slate-deep);
  color: var(--text-on-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease-out), visibility var(--dur-slow);
}
.mobile-menu.open { opacity: 1; visibility: visible; }
.mobile-menu nav {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: center;
  gap: var(--space-4);
  width: min(320px, 82vw);
  font-size: 1.25rem;
  font-weight: 600;
}
.mobile-menu nav > a:not(.btn) {
  padding: 0.5em 0;
  border-bottom: 1px solid var(--line-on-dark);
  font-family: var(--font-display);
}
.mobile-menu .btn { margin-top: var(--space-2); font-size: 0.98rem; }
.mobile-menu .btn-ghost { border-color: var(--line-on-dark-strong); color: var(--text-on-dark); }

/* ===== Hero ===== */
.hero {
  min-height: 100dvh;
  display: flex;
  align-items: flex-end;
  position: relative;
  overflow: hidden;
  padding: calc(var(--header-h) + var(--space-6)) var(--gutter) clamp(3rem, 7vh, 5.5rem);
  background: var(--slate-deep);
  color: var(--text-on-dark);
}
.hero-bg { position: absolute; inset: 0; z-index: 0; }
.hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.07);
  transition: opacity 1.4s ease;
  /* No permanent will-change here: it pins a full-screen composited layer for the
     life of the page to serve one 9s settle. Chrome promotes during the transition. */
}
.hero-bg img.active {
  opacity: 1;
  transform: scale(1);
  transition: opacity 1.4s ease, transform 9s linear;
}
/* Slate scrim, weighted to the bottom-left where the copy sits */
.hero-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(20, 28, 46, 0.92) 0%, rgba(20, 28, 46, 0.45) 42%, rgba(20, 28, 46, 0.22) 100%),
    linear-gradient(105deg, rgba(20, 28, 46, 0.68) 0%, rgba(20, 28, 46, 0.10) 58%, transparent 100%);
  pointer-events: none;
}
.hero-inner {
  position: relative;
  z-index: 1;
  max-width: var(--measure);
  margin: 0 auto;
  width: 100%;
}
/* Wide enough that each authored line of the headline stays on one line in
   Orbitron caps — the two <span class="line"> blocks are what the reveal
   animates, so a wrap inside one splits the animation across two lines. */
.hero-copy { max-width: 54rem; }
.hero .eyebrow { color: var(--brand-light); }
.hero-title {
  font-size: var(--step-hero);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.18;
}
/* Orbitron ships no italic, and a synthesised slant on a squared techno face
   reads as a rendering fault. The emphasis is carried by the brand red and a
   heavier weight instead. */
.hero-title em {
  font-style: normal;
  font-weight: 700;
  color: var(--brand-light);
  line-height: 1.18;
  padding-bottom: 0.06em;
}
.hero-title .line { display: block; overflow: hidden; padding-bottom: 0.08em; }
.hero-title .line > span { display: block; }
.hero-sub {
  margin-top: var(--space-5);
  max-width: 32rem;
  font-size: 1.05rem;
  color: var(--text-on-dark-secondary);
}
.hero-ctas {
  margin-top: var(--space-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
/* Trust line: separated by rules rather than badge clutter */
.hero-trust {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line-on-dark);
  max-width: 32rem;
  font-size: 0.82rem;
  letter-spacing: 0.03em;
  color: var(--text-on-dark-muted);
}

/* ===== Trust strip ===== */
.trust { background: var(--paper-soft); border-bottom: 1px solid var(--line); }
.trust-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-8) var(--gutter);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-6);
}
.trust-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 2px solid var(--brand); /* the brand moment here, not the numerals */
}
.trust-num {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.8vw, 2.1rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  letter-spacing: -0.02em;
  line-height: 1;
}
.trust-label { font-size: 0.86rem; color: var(--text-muted); max-width: 15rem; }

/* ===== Sections shared ===== */
section { position: relative; }
.section-head {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--section-y) var(--gutter) var(--space-7);
}
.section-head p:not(.eyebrow) {
  margin-top: var(--space-4);
  color: var(--text-secondary);
  max-width: 38rem;
}
.eyebrow {
  font-size: var(--step-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--brand-strong);
  font-weight: 600;
  margin-bottom: var(--space-3);
}

/* ===== Services ===== */
.services { background: var(--paper); }
.services-grid {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter) var(--section-y);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8) var(--space-7);
  counter-reset: svc;
}
/* No card chrome — the photo, the type and the space do the work */
.service-card {
  counter-increment: svc;
  display: flex;
  flex-direction: column;
  background: none;
  border: 0;
}
.service-img {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--paper-warm);
}
.service-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease-out);
}
.service-card:hover .service-img img { transform: scale(1.045); }
.service-body {
  padding-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
}
.service-body h3 { display: flex; align-items: baseline; gap: 0.8rem; }
/* 01–03 index: small, red, deliberate */
.service-body h3::before {
  content: counter(svc, decimal-leading-zero);
  font-family: var(--font);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--brand);
  flex-shrink: 0;
}
.service-body p { color: var(--text-secondary); font-size: 0.95rem; }
.service-link {
  margin-top: auto;
  padding-top: var(--space-2);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--brand-strong);
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  align-self: flex-start;
  transition: gap var(--dur) var(--ease-out);
}
.service-link::after { content: "\2192"; }
.service-link:hover { gap: 0.7em; }

/* ===== Supplies — the workshop band =====
   Dark slate with a faint vertical slat rhythm, echoing a closeboard panel. */
.supplies {
  background:
    repeating-linear-gradient(90deg,
      rgba(242, 238, 231, 0.028) 0 1px,
      transparent 1px 13px),
    var(--slate-deep);
  color: var(--text-on-dark);
}
.supplies .eyebrow { color: var(--brand-light); }
.supplies .section-head p:not(.eyebrow) { color: var(--text-on-dark-secondary); }
.supplies-cta { margin-top: var(--space-5); }
/* Ghost button needs the on-dark treatment here, as it does in the mobile menu */
.supplies-cta .btn-ghost { border-color: var(--line-on-dark-strong); color: var(--text-on-dark); }
.supplies-cta .btn-ghost:hover { border-color: var(--text-on-dark); background: rgba(242, 238, 231, 0.08); }
.styles-grid {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter) var(--section-y);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
}
.style-card {
  background: rgba(242, 238, 231, 0.045);
  border: 1px solid var(--line-on-dark);
  border-top: 2px solid var(--brand);
  border-radius: var(--radius-card);
  padding: var(--space-6) var(--space-5);
  transition: background var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.style-card:hover { background: rgba(242, 238, 231, 0.085); transform: translateY(-2px); }
.style-card h3 { color: var(--text-on-dark); }
.style-card p { margin-top: var(--space-3); color: var(--text-on-dark-muted); font-size: 0.92rem; }
/* Actual panel profiles — the section sells a product, so show the product */
.style-diagram {
  width: 72px;
  height: 44px;
  margin-bottom: var(--space-4);
  color: var(--text-on-dark);
  opacity: 0.42;
  transition: opacity var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.style-card:hover .style-diagram { color: var(--brand-light); opacity: 1; }

/* ===== Gallery — card fan carousel =====
   A fanned hand of cards pivoting below the stage. Drag/swipe on touch, arrows
   and card-tap everywhere; the front card opens the lightbox. */
.gallery { background: var(--paper-warm); overflow: hidden; }
.fan-stage {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 1rem var(--gutter) var(--section-y);
}
.fan-deck {
  position: relative;
  /* Tall enough for the outermost card at full spread (3 slots out) to swing
     down without cutting into the caption — the deck is deeper than one card. */
  height: clamp(390px, 58vw, 540px);
  /* Vertical panning still scrolls the page; horizontal is ours to handle */
  touch-action: pan-y;
}
.fan-card {
  --fan-w: clamp(190px, 24vw, 320px);
  position: absolute;
  left: 50%;
  top: 0;
  width: var(--fan-w);
  margin-left: calc(var(--fan-w) / -2);
  aspect-ratio: 4 / 5;
  padding: 0;
  border: 0;
  background: var(--paper);
  border-radius: var(--radius-card);
  overflow: hidden;
  cursor: pointer;
  /* pivot well below the deck so cards fan like a hand of playing cards */
  transform-origin: 50% 165%;
  /* Warm-neutral depth to match the light palette — never flat black */
  box-shadow: 0 24px 60px rgba(22, 32, 47, 0.22);
  will-change: transform;
  -webkit-user-select: none;
  user-select: none;
}
/* Cards deep in the deck start parked so their lazy images don't all load at
   once. Matches the script's opening layout (front card, six either side);
   the script takes over from here. Without it the deck is a stack anyway,
   so nothing parked could have been seen. */
.fan-card:nth-child(n+8):nth-last-child(n+7) { display: none; }
.fan-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  -webkit-user-drag: none;
}
/* Cards behind the front one dim slightly so the active photo reads first */
.fan-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(22, 32, 47, 0.45);
  opacity: var(--fan-dim, 0);
  transition: opacity 0.4s var(--ease-out);
  pointer-events: none;
}
.fan-card.is-front { cursor: zoom-in; }
.fan-caption {
  text-align: center;
  margin-top: 1.8rem;
  font-size: var(--step-small);
  color: var(--text-secondary);
  min-height: 1.5em;
}
.fan-hint {
  text-align: center;
  margin-top: 0.4rem;
  font-size: 0.78rem;
  color: var(--text-muted);
}
.fan-nav {
  margin-top: 1.1rem;
  display: flex;
  justify-content: center;
  gap: 0.8rem;
}
.fan-nav button {
  width: 46px; height: 46px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.25s ease, color 0.25s ease, transform 0.25s var(--ease-out);
}
.fan-nav button:hover { border-color: var(--brand); color: var(--brand); transform: translateY(-1px); }
.fan-nav .icon { width: 1.1rem; height: 1.1rem; }

.testimonial-nav button {
  width: 46px; height: 46px;
  display: grid;
  place-items: center;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              background var(--dur) var(--ease-out);
}
.testimonial-nav button:hover { border-color: var(--brand); color: var(--brand); }
.testimonial-nav .icon { width: 1.05rem; height: 1.05rem; }

/* ===== Testimonials ===== */
.testimonials {
  background: var(--paper-soft);
  border-top: 1px solid var(--line);
  padding: var(--section-y) var(--gutter);
}
.testimonial-stage { max-width: 760px; margin: 0 auto; text-align: center; }
/* min-height is only a floor for the moment before JS measures the slide —
   the real height is set from the active quote's own content. */
.testimonial-slides { position: relative; min-height: 7rem; transition: height 0.4s var(--ease-out); }
.testimonial {
  position: absolute;
  /* Deliberately no `bottom`/`inset`: pinning all four edges would stretch the
     slide to its container, and the container is sized FROM this element —
     it could then never shrink, leaving dead space under short quotes. */
  top: 0;
  left: 0;
  right: 0;
  margin: 0;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
  pointer-events: none;
}
.testimonial.active { opacity: 1; transform: none; pointer-events: auto; }
.testimonial p {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2.3vw, 1.6rem);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--text);
}
/* Short red rule instead of a giant quote glyph */
.testimonial cite {
  display: block;
  margin-top: var(--space-5);
  font-style: normal;
  font-size: 0.86rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.testimonial cite::before {
  content: "";
  display: block;
  width: 28px;
  height: 2px;
  margin: 0 auto var(--space-4);
  background: var(--brand);
}
/* Platform credibility sits with the reviews, not scattered as badges */
.review-platforms {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  font-size: 0.86rem;
  color: var(--text-muted);
}
.review-platforms strong { color: var(--text-secondary); font-weight: 600; }
.testimonial-nav { margin-top: var(--space-4); display: flex; justify-content: center; gap: var(--space-2); }

/* ===== Checkatrade =====
   A white band between the two tinted grounds either side, so the
   third-party proof reads as its own beat rather than more testimonial. */
.checkatrade {
  background: var(--paper);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: var(--space-8) var(--gutter);
}
.checkatrade-inner {
  max-width: var(--measure);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(var(--space-5), 5vw, var(--space-9));
  flex-wrap: wrap;
}
.checkatrade-badge { display: block; flex-shrink: 0; }
.checkatrade-badge img { width: clamp(210px, 24vw, 300px); height: auto; }
.checkatrade-score { text-align: left; }
.checkatrade-rating {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin-top: var(--space-2);
  line-height: 1;
}
.checkatrade-rating strong {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 6vw, 3.6rem);
  font-weight: 700;
  color: var(--brand-strong);
  letter-spacing: -0.01em;
}
.checkatrade-rating span { font-size: 1.05rem; font-weight: 600; color: var(--text-muted); }
.checkatrade-meta { margin-top: var(--space-2); color: var(--text-secondary); font-size: var(--step-small); }
.checkatrade-meta strong { color: var(--text); font-weight: 600; }
.checkatrade-cta { flex-shrink: 0; display: inline-flex; align-items: center; gap: 0.5rem; }
.checkatrade-cta .icon { width: 1rem; height: 1rem; }
@media (max-width: 760px) {
  /* Stacked and centred — the row would otherwise wrap into an untidy L */
  .checkatrade-inner { flex-direction: column; gap: var(--space-5); text-align: center; }
  .checkatrade-score { text-align: center; }
  .checkatrade-rating { justify-content: center; }
}

/* ===== Service areas ===== */
/* ===== Service areas: the Essex coverage map =====
   The section keeps the light ground it always had, so the page's light/dark
   rhythm is untouched. The map itself sits on a dark slate panel — the same
   ground as the supply band — because every effect in it (boundary glow, the
   travelling trace, the pulse from the yard) is light being emitted, and light
   on paper reads as smudge. Palette is entirely existing tokens. */
.areas { background: var(--paper-stone); padding-bottom: var(--section-y); }
.areas-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--section-y) var(--gutter) var(--space-8);
  display: grid;
  grid-template-columns: minmax(0, 4.6fr) minmax(0, 7.4fr);
  gap: clamp(var(--space-6), 5vw, var(--space-9));
  align-items: center;
}
.areas-copy h2 { margin-top: var(--space-3); }
.areas-copy > p { margin-top: var(--space-4); color: var(--text-secondary); max-width: 34rem; }
.areas-copy .btn { margin-top: var(--space-6); }

.areas-legend {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: var(--space-5) 0 0;
  border-top: 1px solid var(--line);
  display: grid;
  gap: var(--space-3);
  font-size: 0.86rem;
  color: var(--text-muted);
}
.areas-legend li { display: flex; align-items: center; gap: 0.7rem; }
.key { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.key--home { background: var(--text); box-shadow: 0 0 0 3px rgba(22, 32, 47, 0.16); }
.key--service { background: var(--brand); box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.18); }

/* ---- the panel ---- */
.essex-stage {
  position: relative;
  border-radius: var(--radius-card);
  overflow: hidden;
  background:
    repeating-linear-gradient(90deg,
      rgba(242, 238, 231, 0.026) 0 1px,
      transparent 1px 13px),
    linear-gradient(158deg, var(--slate-deep) 0%, var(--slate-darkest) 100%);
  box-shadow: var(--shadow-lg), inset 0 0 0 1px rgba(242, 238, 231, 0.08);
  /* the plane below tilts inside this */
  perspective: 1500px;
  perspective-origin: 50% 45%;
}
/* Reserves the height before the SVG lays out, so nothing below jumps */
.essex-stage::before { content: ""; display: block; padding-top: 77.9%; }

.essex-plane {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  /* --mx/--my are written by main.js on pointer move, in the range -1..1 */
  --mx: 0;
  --my: 0;
  /* Rotation only. A translate on top of this reads much the same, but it
     slides a marker out from under the cursor just as you arrive at it, and the
     label you were reaching for flicks away. The layers' own translateZ gives
     the parallax; the pan is not worth the broken hover. */
  transform:
    rotateX(calc(7deg + var(--my) * -2.6deg))
    rotateY(calc(var(--mx) * 3.2deg));
  transition: transform 320ms var(--ease-out);
  will-change: transform;
}
/* Inset rather than filling the panel: the tilt swings the near edge outward,
   and the panel clips, so the map needs a margin to lean into. Without it the
   corner labels — Hertfordshire, Greater London — get cut off. */
.essex-layer {
  position: absolute;
  inset: 5% 5.5%;
  pointer-events: none;
  overflow: visible;
  transition: opacity 900ms var(--ease-out);
}
/* Layered in depth, so the tilt parallaxes them apart by a few pixels */
.essex-layer--base   { transform: translateZ(0); }
.essex-layer--county { transform: translateZ(18px); transition-delay: 150ms; }
.essex-layer--pins   { transform: translateZ(42px); transition-delay: 260ms; }

/* ---- base layer ---- */
.essex-ground { fill: transparent; }
.essex-sea { fill: rgba(66, 89, 121, 0.46); }   /* --slate at low alpha */
.essex-sea-hatch { fill: url(#seaHatch); }
.seaHatchLine { stroke: var(--text-on-dark-muted); stroke-opacity: 0.16; stroke-width: 1; }
.essex-grid line {
  stroke: rgba(242, 238, 231, 0.07);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.mapContext {
  font-family: var(--font);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.2em;
  fill: rgba(242, 238, 231, 0.26);
}

/* ---- county layer ---- */
.essex-fill { fill: url(#essexTint); }
/* The Essex county line, kept underneath at a whisper. It stops the coverage
   area reading as an arbitrary blob — you can see it is the county plus a
   reach into East London and along the Thames. */
.essex-county {
  fill: none;
  stroke: rgba(242, 238, 231, 0.22);
  stroke-width: 1;
  stroke-dasharray: 3 5;
  vector-effect: non-scaling-stroke;
}
.essex-glow { fill: none; stroke: var(--brand-light); stroke-width: 7; opacity: 0.42; }
.essex-edge {
  fill: none;
  stroke: var(--brand-light);
  stroke-width: 2.1;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* A fine dotted line travelling the boundary — the "surveying" moment */
.essex-trace {
  fill: none;
  stroke: #fff;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-dasharray: 1 26;
  opacity: 0.5;
  vector-effect: non-scaling-stroke;
  animation: essex-travel 24s linear infinite;
}
@keyframes essex-travel { to { stroke-dashoffset: -540; } }

/* ---- pins ---- */
/* Reach, radiating out from the yard. fill-box means each circle scales about
   its own centre, so it stays pinned to Dagenham however the map is sized. */
/* Blurred, not drawn. A crisp expanding ring reads as a radar sweep or a
   service-radius circle — both of which the map is meant to avoid. Softened
   into a wave, it reads as reach spreading out from the yard. */
.essex-ping {
  fill: none;
  stroke: var(--brand-light);
  stroke-width: 3;
  opacity: 0;
  filter: blur(4px);
  transform-box: fill-box;
  transform-origin: center;
  animation: essex-ping 9s var(--d, 0s) cubic-bezier(0.22, 0.61, 0.36, 1) infinite;
}
@keyframes essex-ping {
  0%   { opacity: 0;    transform: scale(0.4); }
  12%  { opacity: 0.14; }
  75%  { opacity: 0;    transform: scale(10); }
  100% { opacity: 0;    transform: scale(10); }
}

.pin { pointer-events: auto; cursor: default; }
.pin-hit { fill: transparent; }
.pin-ring {
  fill: none;
  stroke: var(--brand-light);
  stroke-width: 1.5;
  opacity: 0.75;
  transition: opacity var(--dur) var(--ease-out), stroke-width var(--dur) var(--ease-out);
}
.pin-core { fill: var(--brand-light); }
.pin-name {
  font-family: var(--font);
  font-size: 20px;
  font-weight: 600;
  fill: rgba(242, 238, 231, 0.9);
  transition: fill var(--dur) var(--ease-out);
}
.pin-region {
  font-family: var(--font);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.04em;
  fill: var(--brand-light);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
}
/* Smaller towns. Same colour and same meaning — every marker on this map is an
   area worked — just less typographic weight so nineteen names can coexist. */
.pin--minor .pin-name { font-size: 17px; font-weight: 500; fill: rgba(242, 238, 231, 0.68); }
/* The yard it all goes out from */
.pin--home .pin-ring { stroke: #fff; opacity: 0.9; stroke-width: 1.8; }
.pin--home .pin-core { fill: #fff; }
.pin--home .pin-name { font-size: 22px; font-weight: 700; fill: #fff; }

.pin:hover .pin-ring { opacity: 1; stroke-width: 2.6; }
.pin:hover .pin-name { fill: #fff; }
.pin:hover .pin-region { opacity: 1; }

/* ---- index strip ---- */
.areas-index-head {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
  font-size: var(--step-eyebrow);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.areas-index {
  list-style: none;
  max-width: var(--measure);
  margin: var(--space-4) auto 0;
  padding: var(--space-4) var(--gutter) 0;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem var(--space-4);
  font-size: 0.9rem;
  color: var(--text-secondary);
}
.areas-index li { display: flex; align-items: center; }
.areas-index li:not(:last-child)::after {
  content: "";
  width: 3px; height: 3px;
  margin-left: var(--space-4);
  border-radius: 50%;
  background: var(--line-strong);
}
.areas-index-more { color: var(--text-muted); font-style: italic; }

/* ---- entrance ----
   The resting state below IS the finished state. JS "arms" the section (adds
   .is-armed) and the observer disarms it on entry, so if the script never runs,
   or the observer misfires, the map is simply there. Nothing is hidden by
   default — the same rule the intro animation follows. */
.is-armed .essex-layer { opacity: 0; }
.is-armed .essex-edge { stroke-dashoffset: var(--len); }
.is-armed .essex-fill,
.is-armed .essex-trace { opacity: 0; }
.is-armed .pin { opacity: 0; }

.essex-edge {
  stroke-dasharray: var(--len);
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 2000ms cubic-bezier(0.33, 0.9, 0.3, 1) 320ms;
}
.essex-fill { transition: opacity 1100ms var(--ease-out) 900ms; }
.essex-trace { transition: opacity 900ms var(--ease-out) 1500ms; }
.pin {
  opacity: 1;
  transition: opacity 620ms var(--ease-out) calc(1250ms + var(--i) * 70ms);
}

@media (max-width: 1024px) {
  .areas-inner { grid-template-columns: 1fr; gap: var(--space-7); }
  .areas-copy > p { max-width: none; }
}
@media (max-width: 640px) {
  /* Flat on to the map: the tilt is a pointer affordance and there is no pointer */
  .essex-plane { transform: none; }
  /* A 1000-unit viewBox in a ~335px panel scales roughly 0.3, so a 20-unit
     label lands at 6px. Sizes here are set to clear 12px on screen, and only
     four well-separated labels are kept — every name is in the index list
     underneath, so nothing is actually lost. */
  .pin-name { font-size: 40px; }
  .pin--home .pin-name { font-size: 44px; }
  .pin-region { font-size: 32px; }
  .pin:not(.pin--keep) .pin-name,
  .pin:not(.pin--keep) .pin-region { display: none; }
  /* The labels are decluttered above, but every area must still READ as a dot,
     because the dots are what show the coverage. At this scale the desktop
     geometry disappears: the ring's 1.5-unit stroke lands at 0.46px and the
     4.6-unit core at 2.8px, so the map looked like an empty outline on a phone
     and the areas appeared to be missing. Scaled so each pin clears ~6px with a
     ring you can actually see. */
  .pin-ring { r: 20; stroke-width: 4; opacity: 0.85; }
  .pin-core { r: 9; }
  .pin--home .pin-ring { r: 26; stroke-width: 5; opacity: 1; }
  .pin--home .pin-core { r: 11; }
  /* Orientation labels can't be set legibly at this scale without shouting */
  .mapContext { display: none; }
  .areas-index { gap: 0.35rem var(--space-3); font-size: 0.85rem; }
}

@media (prefers-reduced-motion: reduce) {
  .essex-plane { transform: rotateX(7deg); transition: none; }
  .essex-trace { animation: none; }
  .essex-ping { animation: none; display: none; }
}

/* ===== Contact / CTA band — the one saturated red moment ===== */
.contact { background: var(--brand-deep); color: var(--on-brand); }
.contact-inner {
  max-width: 780px;
  margin: 0 auto;
  padding: var(--section-y) var(--gutter);
  text-align: center;
}
.contact .eyebrow { color: rgba(255, 255, 255, 0.75); }
.contact-inner h2 { font-size: clamp(1.4rem, 3.2vw, 2.3rem); }
.contact-inner > p:not(.eyebrow):not(.contact-address) {
  margin-top: var(--space-4);
  color: rgba(255, 255, 255, 0.88);
  max-width: 34rem;
  margin-inline: auto;
}
.contact-ctas { margin-top: var(--space-7); display: flex; justify-content: center; flex-wrap: wrap; gap: var(--space-3); }
/* Inverted primary: white block on the red band */
.contact-ctas .btn-primary { background: var(--paper); color: var(--brand-deep); border-color: var(--paper); }
.contact-ctas .btn-primary:hover { background: var(--paper-warm); border-color: var(--paper-warm); }
.contact-methods {
  list-style: none;
  margin: var(--space-7) 0 0;
  padding: var(--space-6) 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  font-size: 1rem;
  font-weight: 600;
}
.contact-methods li { display: flex; align-items: center; }
.contact-methods li:not(:last-child)::after {
  content: "";
  width: 1px;
  height: 1em;
  margin: 0 var(--space-4);
  background: rgba(255, 255, 255, 0.35);
}
.contact-methods a { border-bottom: 1px solid transparent; transition: border-color var(--dur) var(--ease-out); }
.contact-methods a:hover { border-bottom-color: currentColor; }
.contact-address { margin-top: var(--space-5); font-size: 0.86rem; color: rgba(255, 255, 255, 0.75); }

/* ===== General enquiries =====
   Sits under the red CTA band for people who don't want to build a quote.
   Two columns on desktop, stacked on mobile. */
.enquiry { background: var(--paper-stone); border-top: 1px solid var(--line); }
.enquiry-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--section-y) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(var(--space-7), 6vw, var(--space-9));
  align-items: start;
}
.enquiry-copy h2 { font-size: var(--step-h2); margin-top: var(--space-3); }
.enquiry-copy > p { margin-top: var(--space-4); color: var(--text-secondary); max-width: 32rem; }

.enquiry-direct {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: var(--space-5) 0 0;
  border-top: 1px solid var(--line-strong);
  display: grid;
  gap: var(--space-3);
}
.enquiry-direct li { display: flex; align-items: baseline; gap: var(--space-4); }
.enquiry-direct span {
  flex: 0 0 5.5rem;
  font-size: var(--step-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.enquiry-direct a { font-weight: 600; color: var(--brand-strong); }
.enquiry-direct a:hover { text-decoration: underline; }

.enquiry-form {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
  box-shadow: var(--shadow-sm);
}
.enquiry-form .field:first-child { margin-top: 0; }
.enquiry-form select {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7em 0.85em;
  font: inherit;
}
.enquiry-form select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.enquiry-form textarea { resize: vertical; }
.enquiry-form .btn { width: 100%; margin-top: var(--space-5); }
/* Two fields side by side, but never squeezed — they stack before they pinch.
   The pair owns the vertical rhythm so both fields sit on the same baseline
   rather than the second one inheriting a stacking margin. */
.field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  margin-top: var(--space-4);
}
.field-pair .field { margin-top: 0; }
.field label .optional {
  font-weight: 500;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-left: 0.4rem;
}
.form-status { margin-top: var(--space-4); font-size: 0.9rem; min-height: 1.2em; }
.form-status.is-ok { color: var(--success); font-weight: 600; }
.form-status.is-error { color: var(--error); }

@media (max-width: 860px) {
  .enquiry-inner { grid-template-columns: 1fr; }
  /* Stacked, the pair is just two ordinary fields again */
  .field-pair { grid-template-columns: 1fr; }
  .field-pair .field + .field { margin-top: 0; }
}

/* ===== Forms ===== */
.form-field { display: flex; flex-direction: column; gap: 0.45rem; }
.form-field label { font-size: 0.88rem; font-weight: 600; }
.form-field input, .form-field textarea, .form-field select {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  padding: 0.8rem 1rem;
  color: var(--text);
  font-family: inherit;
  font-size: 0.97rem;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.form-field input:focus, .form-field textarea:focus, .form-field select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.form-field.invalid input, .form-field.invalid textarea { border-color: var(--error); }
.form-error { font-size: 0.82rem; color: var(--error); min-height: 0; }
.form-note { font-size: 0.88rem; color: var(--success); min-height: 1.2em; }

/* ===== Lightbox ===== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(20, 28, 46, 0.95);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-6) clamp(1rem, 4vw, 4rem);
  opacity: 0;
  transition: opacity var(--dur) ease;
}
.lightbox.open { opacity: 1; }
.lightbox[hidden] { display: none; }
.lightbox-stage { margin: 0; max-width: min(1100px, 88vw); text-align: center; }
.lightbox-stage img {
  max-height: 78dvh;
  max-width: 100%;
  width: auto;
  margin: 0 auto;
  border-radius: var(--radius-card);
  box-shadow: 0 40px 100px rgba(0, 0, 0, 0.5);
}
.lightbox-stage figcaption { margin-top: var(--space-4); font-size: 0.9rem; color: var(--text-on-dark-muted); }
.lightbox-close, .lightbox-arrow {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  background: rgba(242, 238, 231, 0.08);
  border: 1px solid var(--line-on-dark-strong);
  border-radius: var(--radius-btn);
  color: var(--text-on-dark);
  cursor: pointer;
  transition: border-color var(--dur) ease, background var(--dur) ease;
  flex-shrink: 0;
}
.lightbox-close { position: absolute; top: 1.25rem; right: 1.25rem; }
.lightbox-close:hover, .lightbox-arrow:hover { border-color: var(--text-on-dark); background: rgba(242, 238, 231, 0.16); }
.lightbox .icon { width: 1.15rem; height: 1.15rem; }
@media (max-width: 640px) {
  .lightbox { padding: 1rem; }
  .lightbox-arrow { position: absolute; bottom: 1.25rem; top: auto; }
  .lightbox-prev { left: 1.25rem; }
  .lightbox-next { right: 1.25rem; }
  .lightbox-stage img { max-height: 66dvh; }
}

/* ===== Footer ===== */
.site-footer {
  background: var(--slate-darkest);
  color: var(--text-on-dark);
}
.footer-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-8) var(--gutter) var(--space-6);
  display: grid;
  grid-template-columns: 2fr 1fr 1.4fr;
  gap: var(--space-7);
}
.footer-logo { width: 166px; height: 26px; object-fit: contain; }
.footer-brand p { margin-top: var(--space-4); color: var(--text-on-dark-muted); font-size: 0.9rem; }
.footer-nav, .footer-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: 0.9rem;
  color: var(--text-on-dark-muted);
}
.footer-nav a, .footer-meta a { transition: color var(--dur) var(--ease-out); }
.footer-nav a:hover, .footer-meta a:hover { color: var(--text-on-dark); }
.footer-copyright {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-5) var(--gutter) var(--space-6);
  border-top: 1px solid var(--line-on-dark);
  font-size: 0.8rem;
  color: var(--text-on-dark-muted);
}

/* ===== Mobile sticky contact bar ===== */
.mobile-cta {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 95;
  padding: 0.7rem var(--gutter) calc(0.7rem + env(safe-area-inset-bottom));
  background: rgb(253, 252, 250);
  border-top: 1px solid var(--line);
  gap: var(--space-2);
  transform: translateY(0);
  transition: transform var(--dur-slow) var(--ease-out);
}
.mobile-cta.hidden, body.menu-open .mobile-cta { transform: translateY(110%); }
.mobile-cta .btn { flex: 1; font-size: 0.92rem; min-height: 46px; padding: 0.7em 1em; }

/* ===== Reveal ===== */
.reveal { opacity: 0; transform: translateY(20px); }
.reveal.in { opacity: 1; transform: none; transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }

/* ===== Responsive ===== */
@media (max-width: 1024px) {
  .main-nav { display: none; }
}
@media (max-width: 900px) {
  .trust-inner { grid-template-columns: 1fr 1fr; gap: var(--space-5); }
  .styles-grid { grid-template-columns: 1fr 1fr; }
  /* Three across gets too tight here — stack rather than orphan a card */
  .services-grid { grid-template-columns: 1fr; gap: var(--space-7); }
  .footer-inner { grid-template-columns: 1fr; gap: var(--space-6); }
  .area-cloud { columns: 2; }
}
@media (max-width: 640px) {
  .header-quote { display: none; }
  .nav-toggle { display: flex; margin-left: auto; }
  .main-nav { display: none; }
  .styles-grid { grid-template-columns: 1fr; }
  .hero { min-height: 92dvh; padding-top: calc(var(--header-h) + 1rem); }
  .hero-ctas .btn { width: 100%; }
  /* Quotes wrap to more lines on a phone, so the pre-JS floor is a little
     taller here — but nowhere near enough to strand space under a short one. */
  .testimonial-slides { min-height: 9rem; }
  .contact-methods { flex-direction: column; gap: var(--space-3); }
  .contact-methods li:not(:last-child)::after { display: none; }
  /* Phone: a wider card in a tighter fan, since the deck has less room to spread */
  .fan-card { --fan-w: min(62vw, 250px); }
  .fan-caption { margin-top: 1.4rem; }
  .wordmark img { width: 140px; height: 22px; }
}
/* Two columns hold right down to the narrowest phones — a single 21-row
   list is a lot of scrolling for no gain. */
@media (max-width: 340px) {
  .area-cloud { columns: 1; }
}
@media (min-width: 641px) {
  .nav-toggle { display: none; }
}
@media (max-width: 1024px) and (min-width: 641px) {
  .nav-toggle { display: flex; margin-left: auto; }
}
@media (max-width: 767px) {
  .mobile-cta { display: flex; }
  .site-footer { padding-bottom: 4.6rem; }
}

/* ===== Reduced motion ===== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .hero-bg img { transform: none; }
}

/* ==========================================================================
   Quote journey
   ========================================================================== */
.quote-page {
  min-height: 100vh;
  display: grid;
  place-items: start center;
  padding: calc(var(--header-h) + var(--space-7)) var(--gutter) 8rem;
  background: var(--paper-soft);
}
.quote-shell { width: min(680px, 100%); }
.quote-head h1 { font-size: clamp(1.15rem, 2.6vw, 1.75rem); }
.progress {
  height: 3px;
  background: var(--line);
  border-radius: 999px;
  margin-top: var(--space-6);
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  width: 0;
  background: var(--brand);
  border-radius: inherit;
  transition: width 0.45s var(--ease-out);
}

.step-track { position: relative; margin-top: var(--space-7); }
.qstep { display: none; }
.qstep.active { display: block; animation: stepIn 0.45s var(--ease-out); }
.qstep.leaving-back.active { animation: stepInBack 0.45s var(--ease-out); }
@keyframes stepIn { from { opacity: 0; transform: translateX(30px); } to { opacity: 1; transform: none; } }
@keyframes stepInBack { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .qstep.active { animation: none; } }

.pick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-3); margin-top: var(--space-6); }
/* Four styles read as a 2×2 block; auto-fit would leave a lone card stranded */
.qstep[data-step="style"] .pick-grid { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 480px) {
  .qstep[data-step="style"] .pick-grid { grid-template-columns: 1fr; }
}
.pick-card {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-card);
  padding: var(--space-5) var(--space-4);
  text-align: left;
  color: var(--text);
  cursor: pointer;
  display: grid;
  gap: 0.3rem;
  align-content: start;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
              background var(--dur) var(--ease-out);
}
.pick-card:hover { border-color: var(--text-muted); box-shadow: var(--shadow-sm); }
.pick-card.selected {
  border-color: var(--brand);
  background: rgba(215, 57, 50, 0.04);
  box-shadow: 0 0 0 1px var(--brand);
}
.pick-title { font-weight: 600; font-size: 1rem; }
.pick-sub { color: var(--text-muted); font-size: 0.86rem; line-height: 1.45; }
/* Flags a style the client quotes after a site visit, so the customer knows
   before they pick it that no instant figure is coming. */
.pick-tag {
  align-self: start;
  margin-top: var(--space-2);
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-chip);
  background: var(--paper-warm);
  color: var(--text-secondary);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.pick-card.selected .pick-tag { border-color: var(--brand); color: var(--brand-strong); background: var(--paper); }
/* The "priced on survey" aside inside a toggle label */
.toggle em { font-style: normal; color: var(--text-muted); }
/* Fence diagrams inherit colour, so selection recolours them */
.pick-diagram { color: var(--slate); opacity: 0.5; margin-bottom: var(--space-2); transition: color var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out); }
.pick-card:hover .pick-diagram { opacity: 0.75; }
.pick-card.selected .pick-diagram { color: var(--brand); opacity: 1; }

.seg-group { display: inline-flex; background: var(--paper); border: 1px solid var(--line-strong); border-radius: var(--radius-btn); padding: 3px; }
.seg {
  border: 0;
  background: none;
  color: var(--text-secondary);
  padding: 0.5em 1.1em;
  border-radius: 3px;
  cursor: pointer;
  font: inherit;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.seg:hover { color: var(--text); }
.seg.active, .seg.selected { background: var(--brand); color: var(--on-brand); font-weight: 600; }

.field-row { margin-top: var(--space-6); display: grid; gap: var(--space-3); }
.field-row > label, .field-row > .field-label { font-weight: 600; }
.length-controls { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.length-controls input[type="number"] {
  width: 5.5rem;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.55em 0.7em;
  font: inherit;
}
input[type="range"] { accent-color: var(--brand); flex: 1; min-width: 160px; }
/* Same control chrome as the details-step fields, so the wizard reads as one form */
.field-row select {
  justify-self: start;
  min-width: min(22rem, 100%);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7rem 0.9rem;
  font: inherit;
}
.field-row select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.field-hint { color: var(--text-muted); font-size: 0.88rem; }
.install-row .seg-group { justify-self: start; }
/* Two long answers side by side don't fit a phone; stack them there. */
@media (max-width: 480px) {
  .install-row .seg-group { justify-self: stretch; display: grid; grid-template-columns: 1fr; }
}

.toggle-list { display: grid; gap: var(--space-2); margin-top: var(--space-5); }
/* Both of these set `display`, which outranks the UA stylesheet's
   [hidden] { display: none } — restate it or the attribute does nothing. */
.toggle-list[hidden], .field-row[hidden] { display: none; }
.toggle {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  padding: 0.9rem 1rem;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out);
}
.toggle:hover { border-color: var(--text-muted); }
.toggle:has(input:checked) { border-color: var(--brand); background: rgba(215, 57, 50, 0.04); }
.toggle input { accent-color: var(--brand); width: 1.15rem; height: 1.15rem; }

.stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  padding: 0.3rem 0.5rem;
}
.stepper button {
  width: 2rem; height: 2rem;
  border-radius: 4px;
  border: 0;
  background: var(--paper-stone);
  color: var(--text);
  font-size: 1.1rem;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.stepper button:hover { background: var(--brand); color: var(--on-brand); }
.stepper span { min-width: 1.5rem; text-align: center; font-weight: 600; }

/* ===== Supply-only materials list =====
   One row per product: what it is, which size, how many, what that costs.
   The row only picks up its accent once there's a quantity against it, so a
   long list still reads as "these three are on my order". */
.materials-list { display: grid; gap: var(--space-3); margin-top: var(--space-5); }
/* The wizard column is a fixed 680px, so the controls go on their own line
   under the product rather than fighting the name for width:
     row 1  product name + note (full width)
     row 2  size select | quantity | line subtotal  */
.material-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-3) var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.material-info { grid-column: 1 / -1; }
.material-row.has-qty {
  border-color: var(--brand);
  box-shadow: 0 0 0 1px var(--brand) inset;
}
.material-info h3 { font-size: 1rem; margin: 0; }
.material-info p { margin: 0.15rem 0 0; font-size: 0.84rem; color: var(--text-muted); }
.material-variant {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.5em 0.7em;
  font: inherit;
  font-size: 0.9rem;
  min-width: 0;
}
.material-variant:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.material-qty input {
  width: 3.4rem;
  border: 0;
  background: transparent;
  font: inherit;
  font-weight: 600;
  text-align: center;
  color: var(--text);
  -moz-appearance: textfield;
}
.material-qty input::-webkit-outer-spin-button,
.material-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.material-qty input:focus { outline: none; }
.material-qty:focus-within { border-color: var(--brand); }
.material-sub {
  min-width: 4.5rem;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--brand-strong);
}

@media (max-width: 560px) {
  /* Narrow phones: the size select takes its own line so the stepper and the
     subtotal keep their full width rather than crushing together */
  .material-row { grid-template-columns: auto 1fr; }
  .material-variant { grid-column: 1 / -1; }
}

.summary-lines { margin: var(--space-6) 0 0; display: grid; gap: var(--space-3); }
.summary-lines .row { display: flex; justify-content: space-between; gap: var(--space-4); border-bottom: 1px solid var(--line); padding-bottom: var(--space-3); }
.summary-lines .row small { display: block; color: var(--text-muted); }
/* Materials subtotal closes off the goods lines above it, so it carries a
   heavier rule and bolder figure than an ordinary line item. */
.summary-lines .row-subtotal { border-bottom-color: var(--line-strong); font-weight: 600; }
/* Survey lines carry no figure, so the amount column reads as a note */
.summary-lines .row-survey dd { color: var(--text-muted); font-size: 0.88rem; font-style: italic; white-space: nowrap; }
.summary-total { display: flex; justify-content: space-between; align-items: baseline; margin-top: var(--space-5); }
.summary-total strong { font-family: var(--font-display); font-size: 2.1rem; color: var(--brand-strong); }
.summary-note { color: var(--text-muted); font-size: 0.88rem; margin-top: var(--space-3); }

/* Notes the customer adds once their estimate is on screen. Sits apart from
   the priced lines above it so it reads as their input, not ours. */
.notes-block {
  display: grid;
  gap: 0.45rem;
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}
.notes-block label { font-weight: 600; display: flex; align-items: center; gap: 0.6rem; }
.notes-block .optional {
  font-weight: 500;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.15em 0.6em;
}
.notes-block textarea {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7em 0.85em;
  font: inherit;
  resize: vertical;
  min-height: 6.5em;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.notes-block textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.notes-hint { font-variant-numeric: tabular-nums; }

.field { display: grid; gap: 0.4rem; margin-top: var(--space-4); }
.field input, .field textarea {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7em 0.85em;
  font: inherit;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.field.invalid input, .field.invalid textarea { border-color: var(--error); }
.hp { position: absolute; left: -9999px; }

.quote-nav {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 50;
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-4);
  background: rgb(253, 252, 250);
  border-top: 1px solid var(--line);
  padding: 0.9rem var(--gutter) calc(0.9rem + env(safe-area-inset-bottom));
}
.mini-summary { display: grid; text-align: center; line-height: 1.25; }
.mini-summary span { color: var(--text-muted); font-size: 0.76rem; }
.mini-summary strong {
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--brand-strong);
  transition: transform var(--dur-fast) var(--ease-out);
}
.mini-summary strong.bump { transform: scale(1.1); }

/* ===== PDF view page ===== */
/* Header is fixed, so the column starts below it — same offset .quote-page uses. */
.pdf-view { min-height: 100vh; padding-top: var(--header-h); display: flex; flex-direction: column; }

.pdf-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-3) var(--gutter);
  border-bottom: 1px solid var(--line);
  background: var(--paper-stone);
}
.pdf-bar .btn[hidden] { display: none; } /* .btn's inline-flex would beat [hidden] */
.pdf-bar-text { display: grid; line-height: 1.3; }
.pdf-bar-text strong { font-family: var(--font-display); font-size: 1.1rem; }
.pdf-bar-text span { color: var(--text-muted); font-size: 0.84rem; }

.pdf-stage { flex: 1; display: flex; min-height: 60vh; background: var(--paper-stone); }
.pdf-frame { flex: 1; width: 100%; border: 0; background: #fff; }
.pdf-empty { margin: auto; padding: var(--space-6); text-align: center; color: var(--text-secondary); }
.pdf-empty a { color: var(--brand-strong); border-bottom: 1px solid currentColor; }


/* ====== Customer photo picker (quote wizard, details step) ====== */
.photo-field { margin-top: var(--space-6); }
.field-optional { color: var(--text-muted); font-weight: 400; }
.photo-field .field-hint { margin: 0; }
.photo-field input[type="file"] {
  background: var(--paper);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7em 0.85em;
  font: inherit;
  cursor: pointer;
}
.photo-field input[type="file"]:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}

.photo-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.photo-item {
  display: flex; align-items: center; gap: 0.75rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-input);
  padding: 0.5rem 0.65rem;
}
.photo-item img {
  width: 52px; height: 52px; flex: none;
  object-fit: cover;
  border-radius: var(--radius-chip);
  background: var(--paper-warm);
}
.photo-meta {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.88rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.photo-remove {
  flex: none;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  color: var(--text-muted);
  font: inherit; font-size: 0.82rem;
  padding: 0.3em 0.7em;
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.photo-remove:hover { color: var(--brand-strong); border-color: var(--brand); }

.photo-total { margin: 0; font-size: 0.85rem; color: var(--text-muted); }
/* Rejections are advice, not form errors — the customer has done nothing wrong. */
.photo-note:empty { display: none; }
.photo-note {
  margin: 0;
  font-size: 0.85rem;
  color: var(--brand-strong);
  background: rgba(215, 57, 50, 0.06);
  border-left: 2px solid var(--brand);
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius-chip);
}
.field-note { margin: 0; font-size: 0.8rem; color: var(--text-muted); }
.field-note a { color: var(--brand-strong); }

/* ══════════════════════════════════════════════════════════
   FAQ
   Question-led, answer-first. <details> gives keyboard and
   screen-reader behaviour for free, and it stays readable with
   no JavaScript: every answer is in the HTML whether the panel
   is open or shut, so a crawler always sees the text.
   ══════════════════════════════════════════════════════════ */
.faq {
  background: var(--paper-soft);
  border-top: 1px solid var(--line);
  padding: var(--section-y) var(--gutter);
}
.faq > .eyebrow,
.faq > h2 { max-width: var(--measure); margin-inline: auto; }
.faq > h2 { font-size: var(--step-h2); margin: var(--space-2) auto var(--space-7); }
.faq-list { max-width: 820px; margin: 0 auto; }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: var(--space-5) 2.25rem var(--space-5) 0;
  position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary h3 {
  display: inline;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}
/* Plain rotating chevron: no bounce, no overshoot */
.faq-item summary::after {
  content: "";
  position: absolute;
  right: 0.35rem;
  top: 50%;
  width: 0.6rem;
  height: 0.6rem;
  border-right: 2px solid var(--brand-strong);
  border-bottom: 2px solid var(--brand-strong);
  transform: translateY(-70%) rotate(45deg);
  transition: transform var(--dur, 260ms) var(--ease-out, ease);
}
.faq-item[open] summary::after { transform: translateY(-30%) rotate(-135deg); }
.faq-item > p {
  margin: 0 0 var(--space-5);
  padding-right: 2.25rem;
  color: var(--text-secondary);
  line-height: 1.65;
}
.faq-item a {
  color: var(--brand-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.faq-item summary:focus-visible { outline: 2px solid var(--brand-strong); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .faq-item summary::after { transition: none; }
}

/* ══════════════════════════════════════════════════════════
   Legal pages (privacy policy) + agency credit
   ══════════════════════════════════════════════════════════ */
.legal {
  background: var(--paper-soft);
  padding: calc(var(--header-h) + var(--space-8)) var(--gutter) var(--section-y);
}
.legal-inner {
  max-width: 760px;
  margin: 0 auto;
}
.legal-inner h1 {
  font-family: var(--font-display);
  font-size: var(--step-hero);
  line-height: 1.05;
  margin: var(--space-2) 0 var(--space-3);
}
.legal-updated {
  color: var(--text-muted);
  font-size: var(--step-small);
  margin-bottom: var(--space-7);
}
.legal-inner h2 {
  font-size: 1.35rem;
  margin: var(--space-8) 0 var(--space-4);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}
.legal-inner p,
.legal-inner li {
  color: var(--text-secondary);
  line-height: 1.7;
}
.legal-inner p { margin: 0 0 var(--space-4); }
.legal-inner ul {
  margin: 0 0 var(--space-5);
  padding-left: 1.15rem;
}
.legal-inner li { margin-bottom: var(--space-2); }
.legal-inner strong { color: var(--text); }
.legal-inner a {
  color: var(--brand-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--space-5);
  font-size: var(--step-small);
}
.legal-table th,
.legal-table td {
  text-align: left;
  padding: var(--space-3);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  color: var(--text-secondary);
}
.legal-table th { color: var(--text); font-weight: 600; }
/* A table is the wrong shape for a narrow phone: stack it instead */
@media (max-width: 34rem) {
  .legal-table, .legal-table thead, .legal-table tbody,
  .legal-table tr, .legal-table th, .legal-table td { display: block; }
  .legal-table thead { display: none; }
  .legal-table tr {
    border-bottom: 1px solid var(--line-strong);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
  }
  .legal-table td { border: 0; padding: var(--space-1) 0; }
  .legal-table td:first-child { color: var(--text); font-weight: 600; }
}

.footer-credit {
  text-align: center;
  padding: 0 var(--gutter) var(--space-6);
  margin: 0;
  font-size: var(--step-small);
  color: var(--text-on-dark-muted);
}
.footer-credit a {
  color: var(--text-on-dark-secondary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.footer-credit a:hover { color: var(--text-on-dark); }


/* Metric equivalent under each height in the segmented control. The trade
   works in feet; plenty of customers measure their garden in metres. */
.seg-metric {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  opacity: 0.7;
  margin-top: 0.1em;
  line-height: 1.2;
}

/* ====== Summary: what the job includes ======
   The customer sees two figures — the work and the delivery. Everything the
   job includes is named here underneath, deliberately without prices. */
.postcode-row { margin-bottom: var(--space-5); }
.postcode-row input {
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-input);
  color: var(--text);
  padding: 0.7em 0.85em;
  font: inherit;
  text-transform: uppercase;
  max-width: 12rem;
}
.postcode-row input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.15);
}
.field-hint.is-ok { color: var(--success, #1F7A3D); }
.field-hint.is-warn { color: var(--brand-strong); }

.row-includes { align-items: start; }
.row-includes dt { font-weight: 400; }
.includes-list {
  list-style: none;
  margin: 0.15rem 0 0;
  padding: 0 0 0 0.9rem;
  border-left: 2px solid var(--line);
  display: grid;
  gap: 0.2rem;
  font-size: 0.88rem;
  color: var(--text-muted);
}
.includes-list em { font-style: normal; opacity: 0.85; }
.includes-survey { color: var(--text-secondary); }

/* ====== Before / after comparison ======
   Two photographs of the same boundary stacked exactly on top of each other.
   The "before" is clipped from the right by --ba-pos, so dragging the control
   wipes one state across to the other. The control is a real range input, so
   pointer, touch and keyboard all work without us reimplementing any of it. */
.beforeafter { background: var(--paper-warm); padding: var(--space-8) var(--space-4); }
.ba-grid {
  max-width: 1180px;
  margin: var(--space-6) auto 0;
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
}
.ba { margin: 0; }
.ba-frame {
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--paper-stone);
  box-shadow: 0 1px 2px rgba(22, 32, 47, 0.06), 0 8px 24px rgba(22, 32, 47, 0.08);
  /* the whole frame is the drag surface, so the cursor should say so */
  cursor: ew-resize;
  touch-action: pan-y;
}
.ba-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
/* Clipped rather than resized: a width-based reveal would squash the photo. */
.ba-before { clip-path: inset(0 calc(100% - var(--ba-pos, 50%)) 0 0); }

.ba-divider {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--ba-pos, 50%);
  width: 2px;
  margin-left: -1px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 0 0 1px rgba(22, 32, 47, 0.18);
  pointer-events: none;
}
.ba-grip {
  position: absolute;
  top: 50%; left: 50%;
  width: 2.6rem; height: 2.6rem;
  margin: -1.3rem 0 0 -1.3rem;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 2px 10px rgba(22, 32, 47, 0.28);
  display: grid;
  place-items: center;
}
/* Two chevrons, drawn in CSS so there is no icon to load. */
.ba-grip::before, .ba-grip::after {
  content: "";
  position: absolute;
  width: 0.42rem; height: 0.42rem;
  border-top: 2px solid var(--text-secondary);
  border-right: 2px solid var(--text-secondary);
}
.ba-grip::before { transform: rotate(-135deg); margin-left: -0.55rem; }
.ba-grip::after  { transform: rotate(45deg);   margin-left: 0.55rem; }

.ba-tag {
  position: absolute;
  top: 0.75rem;
  padding: 0.3em 0.7em;
  border-radius: var(--radius-chip);
  background: rgba(22, 32, 47, 0.72);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  pointer-events: none;
}
.ba-tag--before { left: 0.75rem; }
.ba-tag--after  { right: 0.75rem; }

/* The input covers the frame and is invisible; the divider above is what the
   customer sees. Keeping a real input means we inherit its accessibility. */
.ba-range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  /* It takes no pointer input - the frame handles that. Without this the
     input sat on top of everything and swallowed the drag. It is still
     focusable by keyboard, which is the whole reason it is still here. */
  pointer-events: none;
  touch-action: pan-y;
}
.ba-range::-webkit-slider-thumb { -webkit-appearance: none; width: 3rem; height: 100%; }
.ba-range::-moz-range-thumb { width: 3rem; height: 100%; border: 0; opacity: 0; }
.ba-range:focus-visible ~ .ba-divider .ba-grip {
  box-shadow: 0 0 0 3px rgba(215, 57, 50, 0.55), 0 2px 10px rgba(22, 32, 47, 0.28);
}

.ba figcaption {
  margin-top: 0.7rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

/* A finger needs a bigger grip than a cursor does. */
@media (pointer: coarse) {
  .ba-grip {
    width: 3.1rem; height: 3.1rem;
    margin: -1.55rem 0 0 -1.55rem;
  }
  .ba-tag { font-size: 0.68rem; padding: 0.28em 0.6em; }
}

/* Source line under each testimonial (verified Checkatrade reviews). */
.testimonial-source {
  display: block;
  margin-top: 6px;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--text-muted);
}
.testimonial-source a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
  transition: color var(--dur) var(--ease-out), text-decoration-color var(--dur) var(--ease-out);
}
.testimonial-source a:hover,
.testimonial-source a:focus-visible {
  color: var(--brand);
  text-decoration-color: currentColor;
}
