/* ===================== DESIGN TOKENS ===================== */
:root {
  /* --- Phase 2 core palette (brand colours retained, neutrals added) --- */
  --color-navy: #1A3B8B;
  --color-navy-dark: #112559;
  --color-blue: #1D70B8;
  --color-orange: #D14124;
  --color-ink: #0B1220;
  --color-ink-muted: #4A5568;
  --color-surface: #F2F3F5;
  --color-surface-tint-blue: #EAF1FA;
  --color-surface-tint-navy: #E9EBF5;
  --color-white: #ffffff;
  --color-border: #d6d9e0;

  /* --- Semantic state colours --- */
  --color-ok: #1c6b34;
  --color-ok-bg: #e6f4ea;
  --color-warn: #8a6410;
  --color-warn-bg: #fff4e0;
  --color-danger: #8a2e1c;
  --color-danger-bg: #fdecea;

  /* legacy aliases kept so Phase 1 rules below keep working unmodified */
  --bg: var(--color-surface);
  --navy: var(--color-navy);
  --navy-dark: var(--color-navy-dark);
  --ice-blue: var(--color-blue);
  --flame-orange: var(--color-orange);
  --text: var(--color-ink);
  --text-muted: var(--color-ink-muted);
  --white: var(--color-white);
  --border: var(--color-border);
  --radius: var(--radius-sm);

  /* --- Radii --- */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;

  /* --- Shadow --- */
  --shadow-soft: 0 4px 24px rgba(11, 18, 32, 0.08), 0 1px 2px rgba(11, 18, 32, 0.04);

  /* --- Typography --- */
  --font-heading: 'Space Grotesk', -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-body: 'Inter', -apple-system, "Segoe UI", Roboto, sans-serif;

  --text-xs: 0.8rem;
  --text-sm: 0.9rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.563rem;
  --text-2xl: 1.953rem;
  --text-3xl: 2.441rem;
  --text-4xl: 3.052rem;
  --text-5xl: 3.815rem;

  /* --- Spacing (8px grid) --- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;
  --space-6: 48px;
  --space-7: 64px;
  --space-8: 96px;
  --space-9: 128px;

  /* --- Motion (mirrors EASE/DURATION tokens in app.js, for CSS-only transitions) --- */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-micro: 0.15s;
  --dur-fast: 0.35s;
  --dur-base: 0.55s;
  --dur-slow: 0.85s;
}

* { box-sizing: border-box; }

[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-surface);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.6;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-navy-dark);
  margin: 0 0 0.5em;
  line-height: 1.2;
}

h1 { font-size: var(--text-5xl); }
h2 { font-size: clamp(var(--text-2xl), 3vw, var(--text-3xl)); text-align: center; }
h3 { font-size: var(--text-lg); }

p { margin: 0 0 1em; }

a { color: var(--color-blue); }

img { max-width: 100%; display: block; }

/* Focus states — visible on every interactive element, not just hover */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--color-blue);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.section-pad {
  padding: var(--space-8) var(--space-4);
  max-width: 1280px;
  margin: 0 auto;
}

.section-intro {
  text-align: center;
  color: var(--color-ink-muted);
  max-width: 620px;
  margin: 0 auto var(--space-5);
}


/* ===================== BUTTONS ===================== */
.btn {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-sm);
  border: none;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-sm);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  box-shadow: 0 1px 2px rgba(11, 18, 32, 0.08);
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-soft); }
.btn:active { transform: translateY(0) scale(0.97); transition-duration: var(--dur-micro); }
.btn-primary { background: var(--color-navy); color: var(--color-white); }
.btn-secondary { background: var(--color-blue); color: var(--color-white); }
.btn-accent { background: var(--color-orange); color: var(--color-white); }
.btn-small { padding: 0.4rem 0.9rem; font-size: var(--text-xs); }


/* ===================== HEADER / NAV ===================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--navy);
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.header-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0.6rem 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
}
.brand-logo { height: 42px; width: auto; }
.brand-name {
  color: var(--white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.1rem;
}
.main-nav {
  display: flex;
  gap: 1.25rem;
}
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
}
.nav-toggle span {
  width: 26px;
  height: 3px;
  background: var(--white);
  border-radius: 2px;
}

/* Sticky mobile call button */
.sticky-call-btn {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 90;
  background: var(--flame-orange);
  color: var(--white);
  text-align: center;
  padding: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  font-family: var(--font-heading);
}
/* The mark, not just the words. This bar is on screen the whole time somebody is on a phone, so
   it has to be recognised rather than read. */
.sticky-call-btn {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
}
.sticky-call-btn .ic { width: 1.3em; height: 1.3em; fill: currentColor; }

/* Shown only if the drawing cannot be built. It has to look like a considered message rather than
   an error, because the person reading it came here to be reassured about their home. */
.sim-fallback {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-4);
  text-align: center;
  color: var(--text-muted);
}
.sim-fallback p { margin: 0 0 0.75rem; }
.sim-fallback p:last-child { margin-bottom: 0; }
.sim-fallback strong { color: var(--color-ink); }
.sim-fallback a { color: var(--color-blue); font-weight: 600; }


/* ===================== HERO ===================== */
.hero {
  position: relative;
  min-height: 88vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: linear-gradient(180deg, var(--navy-dark), var(--navy));
}
.hero-content {
  position: relative;
  z-index: 2;
  text-align: center;
  color: var(--white);
  max-width: 780px;
  padding: 1.5rem;
}
.hero-content h1 {
  color: var(--white);
  font-size: clamp(1.8rem, 4.5vw, 3rem);
  margin-bottom: 0.6rem;
}
.hero-subhead {
  font-size: 1.05rem;
  color: #d7ddf0;
  margin-bottom: 1.75rem;
}
.hero-ctas {
  display: flex;
  gap: 1rem;
  justify-content: center;
  flex-wrap: wrap;
}


/* ===================== SERVICES ===================== */
.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
}
.service-card {
  background: var(--color-white);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.service-card:hover,
.service-card:focus-within {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(11, 18, 32, 0.12), 0 2px 4px rgba(11, 18, 32, 0.06);
}


/* ===================== SECTION RHYTHM: tint utilities + stats strip ===================== */
.tint-blue { background: var(--color-surface-tint-blue); }
.tint-navy { background: var(--color-surface-tint-navy); }

.stats-strip {
  background: linear-gradient(135deg, var(--color-navy-dark), var(--color-navy));
  padding: var(--space-7) var(--space-4);
}
.stats-strip-inner {
  max-width: 1000px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-5);
  text-align: center;
}
.stat-item { display: flex; flex-direction: column; gap: var(--space-1); }
.stat-number {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-4xl);
  color: var(--color-white);
}
.stat-label {
  font-size: var(--text-sm);
  color: #c3cdea;
}


/* ===================== MOULD SIMULATOR: property/system pickers (Phase 2) ===================== */
.mould-section { background: var(--color-white); border-radius: var(--radius-lg); }

.picker-block { max-width: 900px; margin: 0 auto var(--space-6); }
.picker-heading {
  text-align: center;
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
  margin-bottom: var(--space-3);
}
.picker-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
}
.picker-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-white);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink);
  text-align: center;
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.picker-card small { display: block; font-weight: 400; color: var(--color-ink-muted); margin-top: 2px; }
.picker-icon { width: 40px; height: 40px; color: var(--color-navy); }
.picker-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-soft); }
.picker-card[aria-checked="true"] {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  box-shadow: var(--shadow-soft);
}
.picker-card[aria-checked="true"] .picker-icon { color: var(--color-blue); }

.loft-toggle-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  flex-wrap: wrap;
}
.loft-toggle-label { font-weight: 600; font-size: var(--text-sm); }
.loft-toggle { display: flex; gap: var(--space-2); }
.loft-toggle-btn {
  border: 2px solid var(--color-border);
  background: var(--color-white);
  border-radius: var(--radius-sm);
  padding: 0.4rem 1.2rem;
  font-weight: 700;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.loft-toggle-btn:hover { transform: translateY(-2px); }
.loft-toggle-btn[aria-checked="true"] {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  color: var(--color-navy-dark);
}

.diagram-hint {
  text-align: center;
  color: var(--color-ink-muted);
  font-size: var(--text-sm);
  margin-top: var(--space-2);
}

/* Down from 9.5px. A room name is a caption on a drawing, not a headline - at the old size
   "Bathroom" ran the full width of the bathroom and sat on top of the thing it was naming. */
.room-label { font-family: var(--font-body); font-size: 8px; font-weight: 600; fill: #5f6a80; opacity: 0.9; }
.window-fog { pointer-events: none; }

.mould-widget {
  display: grid;
  grid-template-columns: 1.34fr 1fr;
  gap: 2rem;
  /* Top-aligned, not centred: once the result panel appears the right column is much taller, and
     centring would leave the diagram floating in the middle of a tall empty column. */
  align-items: start;
}
.house-svg {
  width: 100%;
  height: auto;
  /* The viewBox is 480x400. Stating it here rather than leaving `height: auto` to infer it is what
     stops Safari computing a height of zero and rendering the centrepiece of the site as a blank
     white rectangle. min-height is the second belt: something is always visible. */
  aspect-ratio: 480 / 400;
  min-height: 220px;
  background: #0c1730;
  border-radius: var(--radius);
}
.mould-cloud { transition: r 0.6s ease; }
.mould-colony { transition: opacity 0.5s ease; }
.mould-spot { transition: r 0.4s ease; }
.mould-controls label {
  display: block;
  font-weight: 700;
  margin-bottom: 0.5rem;
}
#humiditySlider { width: 100%; margin-bottom: 1rem; accent-color: var(--ice-blue); }
.mould-warning {
  background: #fdecea;
  color: #8a2e1c;
  border: 1px solid var(--flame-orange);
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius);
  font-weight: 600;
  margin-bottom: 1rem;
}

/* ===================== MOULD SIMULATOR: guided flow, guidance + result (Phase 4) ===================== */

/* --- Step rail --- */
.sim-steps {
  list-style: none;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  max-width: 900px;
  margin: 0 auto var(--space-5);
  padding: 0;
}
.sim-step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.45rem 0.9rem;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-white);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-muted);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.sim-step-num {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-ink-muted);
  font-size: var(--text-xs);
  font-weight: 700;
  flex: none;
}
.sim-step.is-active {
  color: var(--color-navy-dark);
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
}
.sim-step.is-active .sim-step-num { background: var(--color-blue); color: var(--color-white); }
.sim-step.is-done { color: var(--color-ok); border-color: var(--color-ok); background: var(--color-ok-bg); }
.sim-step.is-done .sim-step-num { background: var(--color-ok); color: var(--color-white); }

/* --- System cards: extra "best for" tag line --- */
.picker-cards-sys { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.sys-tag {
  display: block;
  margin-top: auto;
  padding-top: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-blue);
  letter-spacing: 0.02em;
}
.picker-card[aria-checked="true"] .sys-tag { color: var(--color-navy-dark); }

.sys-help { text-align: center; margin-top: var(--space-3); }
.btn-small { padding: 0.45rem 1rem; font-size: var(--text-sm); }

/* --- System detail panel --- */
.sys-detail {
  margin-top: var(--space-4);
  padding: var(--space-4);
  background: var(--color-surface-tint-blue);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.sys-detail h4 {
  font-family: var(--font-heading);
  margin: 0 0 var(--space-2);
  color: var(--color-navy-dark);
  font-size: var(--text-lg);
}
.sys-detail > p { margin: 0 0 var(--space-3); color: var(--color-ink); }
.sys-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-3);
  margin: 0;
}
.sys-facts div { margin: 0; }
.sys-facts dt {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--color-ink-muted);
  margin-bottom: 2px;
}
.sys-facts dd { margin: 0; font-size: var(--text-sm); color: var(--color-ink); }
.sys-recommended {
  display: inline-block;
  margin-bottom: var(--space-2);
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--color-navy);
  color: var(--color-white);
  font-size: var(--text-xs);
  font-weight: 700;
}

/* --- Humidity control --- */
.hum-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}
.mould-controls .hum-head label { margin-bottom: 0.35rem; }
.hum-readout {
  font-family: var(--font-heading);
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--color-navy);
  line-height: 1;
}
.hum-band {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
}
.hum-band.band-ok { color: var(--color-ok); }
.hum-band.band-watch { color: var(--color-warn); }
.hum-band.band-risk { color: var(--color-danger); }

/* --- Simulate button gating --- */
#simulateFlowBtn { width: 100%; }
#simulateFlowBtn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.4);
}
.sim-blocker {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  text-align: center;
}




/* One line under each question saying what to do about it. The people this has to work for are
   a ten-year-old and an eighty-year-old, and a heading on its own is an instruction only to
   somebody who already knows what the answer looks like. */
.picker-help {
  margin: -6px 0 var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  max-width: 62ch;
}
.picker-heading + .picker-help { margin-top: 0; }

/* The fans, turning. Only once the system is running - a fan spinning in a drawing of a house
   nobody has switched on is the same lie as a mock portal.
   transform-box: fill-box is not optional: without it the rotation is measured against the whole
   SVG viewport and the impeller swings off across the drawing instead of turning on its hub. */
.unit-blade { fill: #a7bcd4; opacity: 0.9; }
.unit-rotor { transform-box: fill-box; transform-origin: center; }
.is-running .unit-rotor { animation: rotorSpin 0.62s linear infinite; }
@keyframes rotorSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .is-running .unit-rotor { animation: none; }
}


/* Stairs. The newels, the rail and the balusters are what make a stepped profile read as a
   staircase rather than as a set of steps drawn in a gap. */
.stairs-newel { fill: #a89377; stroke: #86724f; stroke-width: 0.5; }
.stairs-baluster { stroke: #c6b596; stroke-width: 1; opacity: 0.9; }
.eaves-gutter-rim { stroke: #8c98a8; stroke-width: 0.8; opacity: 0.85; }

/* Air arriving and leaving, drawn outside the building. Warm and murky on the way out, cool on
   the way in - the same two colours as the ducts they came down, so the two ends of the story
   match. */
.discharge { pointer-events: none; }
.discharge-trace {
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  /* 6 on, 21 off is five per cent ink: at that ratio a trace is a row of dots and the plume it
     is meant to give a direction to reads as a static glow. */
  stroke-dasharray: 11 10;
  animation: airTrace 2.9s linear infinite;
  opacity: 0.7;
}
.discharge-chev {
  fill: none;
  stroke: #d8f0ff;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* What comes out of an exhaust louvre is warm, wet, and carrying everything the house has been
   breathing at. What goes into an intake is clean outdoor air. They should not be the same colour
   and they should not be the same weight. */
.discharge--out .discharge-trace {
  /* The plume's own murk, not a shade off the sky. These lines are what give the body its shape,
     and at #dbe4bd they were within a few per cent of a pale blue horizon. */
  stroke: #9fa987;
  stroke-width: 3;
  opacity: 0.8;
  filter: blur(0.3px);
}
/* Condensing as it goes. The opacity is set per puff so it thins with distance; this is only the
   colour and the softness. */
/* Each puff breathes on its own delay, so the plume rolls rather than pulsing as one object.
   The transform on the element itself carries its position and size, so the animation has to be
   on the ellipses INSIDE it - scaling the group would throw it back to the terminal. */
.plume-puff ellipse {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: puffBillow 5.2s ease-in-out infinite;
}
.plume-puff ellipse:nth-child(2) { animation-delay: -1.7s; }
.plume-puff ellipse:nth-child(3) { animation-delay: -3.4s; }
@keyframes puffBillow {
  0%, 100% { transform: scale(0.88) translate(0, 0); }
  50% { transform: scale(1.16) translate(0.06px, -0.04px); }
}
@media (prefers-reduced-motion: reduce) { .plume-puff ellipse { animation: none; } }
.discharge--in .discharge-trace {
  stroke: #cdeaff;
  stroke-width: 1.7;
  opacity: 0.95;
}
@media (prefers-reduced-motion: reduce) { .discharge-trace { animation: none; } }

/* The dingy wash. Multiply, so it darkens and cools what is already there rather than veiling
   it in grey - a damp house does not look like a house with a sheet over it, it looks like a house
   with the light gone out of it. */
.dingy { mix-blend-mode: multiply; pointer-events: none; }

/* Numbered balloons on the drawing, and the key to them underneath.
   The chips these replace were the width of a room, floated over the loft, overlapped each other
   and covered the very thing they pointed at - and on a phone it got worse, because the label
   grows and the drawing does not. A balloon can sit on a component without hiding it; the words
   live outside the house where there is room for them. */
.balloon-disc { fill: var(--color-navy-dark); }
.balloon-ring { fill: none; stroke: #ffffff; stroke-width: 1.6; opacity: 0.9; }
.balloon-shadow { fill: #0B1220; opacity: 0.35; }
.balloon-tick { stroke: #ffffff; stroke-width: 1.5; opacity: 0.85; }
.balloon-anchor { fill: #ffffff; stroke: var(--color-navy-dark); stroke-width: 1.2; }
.balloon-num {
  fill: #ffffff;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 9.5px;
}

.sim-key {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 8px 18px;
}
.sim-key-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: var(--text-sm);
  color: var(--color-ink);
  line-height: 1.35;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.sim-key-item.is-on { opacity: 1; transform: none; }
.sim-key-num {
  flex: 0 0 auto;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--color-navy-dark);
  color: #fff;
  display: grid;
  place-items: center;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.72rem;
  margin-top: 1px;
}
@media (prefers-reduced-motion: reduce) {
  .sim-key-item { transition: none; }
}

/* Internal walls, cut. Plasterboard faces either side of a stud core - the same treatment the
   external walls get, because a one-pixel line between two rooms is not a wall, and the upstairs
   of this house read as one continuous strip of plaster until they were drawn. */
.part-core { fill: #cfd6e2; }
.part-stud { fill: #c2ab86; opacity: 0.75; }
.part-face { fill: #eef1f6; }
.part-shadow { fill: #0B1220; opacity: 0.11; }
.part-lintel { fill: #b9a586; stroke: #93815f; stroke-width: 0.5; }
.part-reveal { fill: #dfe4ec; }

/* Screen, so it lifts what is under it rather than laying a pale film over it. */
.fresh-lift { mix-blend-mode: screen; pointer-events: none; }

/* The load going round. transform-box: fill-box, or the rotation is measured against the whole
   SVG viewport and the washing swings across the kitchen - the same trap as the fan impellers. */
.dryer-drum { transform-box: fill-box; transform-origin: 50% 50%; animation: dryerSpin 3.4s linear infinite; }
@keyframes dryerSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .dryer-drum { animation: none; } }

/* The plant cupboard in a flat, and the bulkhead over it that the distribution boxes sit in. */
.bulkhead { fill: #e4e9f0; stroke: #b3bccb; stroke-width: 0.7; }
.bulkhead-edge { fill: #0B1220; opacity: 0.13; }
.cupboard { fill: #dfe5ed; stroke: #adb7c6; stroke-width: 0.8; }
.cupboard-door { fill: #eaeff5; stroke: #b8c2d0; stroke-width: 0.7; }

.wet-wall { pointer-events: none; }
/* Water on a wall reads as water because of the RUNS down it and the beads standing on it. A
   flat wash of grey behind the tiles never read as wet at any opacity, which is what "the wetness
   needs to be obvious" was about. */
.wet-runs { pointer-events: none; }
.wet-run { fill: #cfe4f2; opacity: 0.7; }
.wet-bead { fill: #e4f1fa; opacity: 0.66; }

/* Certificates behind a button.
   The button's face is a crop of the CERTIFICATE'S OWN HEAD, not the Nuaire, Airflow or BESA mark
   lifted out of it. A manufacturer's logo standing alone on a contractor's site reads as
   approved-installer status and the BESA mark reads as membership; a course certificate is
   neither, and that is the one claim this section must not make. Inside the document the same
   marks are just part of what the certificate says. */
.cert-open {
  display: block;
  width: 100%;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
  cursor: pointer;
  overflow: hidden;
  text-align: left;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.cert-open:hover,
.cert-open:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-soft);
}
.cert-face { display: block; background: #f4f6f9; }
.cert-face img { display: block; width: 100%; height: auto; }
.cert-meta {
  display: block;
  padding: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.cert-meta strong {
  display: block;
  font-family: var(--font-heading);
  color: var(--color-navy-dark);
  margin-bottom: 3px;
}
.cert-meta > span {
  display: block;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  line-height: 1.4;
}
.cert-cue {
  margin-top: 8px;
  color: var(--color-blue) !important;
  font-weight: 600;
}
.cert-cue::after { content: ' \2192'; }

.cert-modal {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(11, 18, 32, 0.92);
  display: grid;
  place-items: center;
  padding: var(--space-5);
  overflow: auto;
}
.cert-modal[hidden] { display: none; }
.cert-modal-figure { margin: 0; text-align: center; }
.cert-modal-figure img {
  max-width: min(620px, 92vw);
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.cert-modal-figure figcaption { margin-top: var(--space-4); }
.cert-modal-figure a { color: #fff; font-weight: 600; }
.cert-modal-close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  width: 44px; height: 44px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.cert-modal-close:hover { background: rgba(255, 255, 255, 0.24); }
@media (prefers-reduced-motion: reduce) {
  .cert-open { transition: none; }
}

/* --- Simulator: the running commentary ------------------------------------------------------
   BESIDE the drawing, not over it. It used to be a pill floating on the roof, which covered the
   house at exactly the moment the house was worth watching and could only hold four words - and
   four words is a caption, not an explanation.

   Five parts: a state line with a dot that is alive while the run is; the stage in large type,
   cross-fading as it changes; a sentence of plain English a beat behind it; the five steps
   ticking off so you can see what is still to come; and a rail. */
.mould-diagram-wrap { position: relative; }

/* The right-hand column: the commentary at the top, level with the roof of the house next to it,
   and the controls that change what it is describing underneath. */
.mould-side {
  display: grid;
  gap: var(--space-5);
  align-items: start;
}

.sim-narrate {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: linear-gradient(158deg, #17253f 0%, #0e1728 58%, #0a1120 100%);
  color: #fff;
  box-shadow: var(--shadow-soft);
}
/* One slow bloom of light drifting behind the type while the thing is live. It is the only
   moving part of the panel, and it is what makes it read as an instrument rather than a caption.
   Behind the content on its own z-index so it can never wash out the text. */
.sim-narrate::after {
  content: '';
  position: absolute;
  inset: -45% -65%;
  z-index: -1;
  opacity: 0;
  background: radial-gradient(closest-side, rgba(93, 168, 224, 0.34), transparent 72%);
  transition: opacity 700ms var(--ease-out);
}
.sim-narrate[data-state="running"]::after,
.sim-narrate[data-state="rewind"]::after {
  opacity: 1;
  animation: narrateGlow 7.5s ease-in-out infinite;
}
.sim-narrate[data-state="done"]::after { opacity: 0.5; }
@keyframes narrateGlow {
  0%, 100% { transform: translate3d(-15%, -7%, 0); }
  50%      { transform: translate3d(15%, 9%, 0); }
}

.sim-narrate-eyebrow {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 var(--space-3);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.sim-narrate-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.34);
  transition: background 400ms var(--ease-out);
}
.sim-narrate[data-state="running"] .sim-narrate-dot,
.sim-narrate[data-state="rewind"] .sim-narrate-dot {
  background: var(--color-orange);
  animation: narratePulse 1.7s ease-out infinite;
}
.sim-narrate[data-state="done"] .sim-narrate-dot { background: #4ec98a; }
@keyframes narratePulse {
  0%   { box-shadow: 0 0 0 0 rgba(209, 65, 36, 0.55); }
  70%  { box-shadow: 0 0 0 8px rgba(209, 65, 36, 0); }
  100% { box-shadow: 0 0 0 0 rgba(209, 65, 36, 0); }
}

/* The headline and its ghost occupy the same box, so the outgoing line leaves through the
   incoming one instead of the panel changing height under them. */
.sim-narrate-stage {
  position: relative;
  display: block;
  min-height: 2.5em;
}
.sim-narrate-line,
.sim-narrate-ghost {
  display: block;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.15rem;
  line-height: 1.22;
  letter-spacing: -0.01em;
}
.sim-narrate-ghost {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.sim-narrate-line.is-in  { animation: narrateLineIn 540ms var(--ease-out) both; }
.sim-narrate-ghost.is-out { animation: narrateLineOut 430ms var(--ease-out) both; }
@keyframes narrateLineIn {
  from { opacity: 0; transform: translateY(15px); filter: blur(7px); }
  to   { opacity: 1; transform: none;             filter: blur(0); }
}
@keyframes narrateLineOut {
  from { opacity: 0.8; transform: none;              filter: blur(0); }
  to   { opacity: 0;   transform: translateY(-15px); filter: blur(7px); }
}

.sim-narrate-note {
  margin: 11px 0 0;
  font-size: var(--text-sm);
  line-height: 1.52;
  color: rgba(255, 255, 255, 0.74);
}
.sim-narrate-note.is-in { animation: narrateLineIn 640ms 110ms var(--ease-out) both; }

.sim-narrate-steps {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: var(--space-4) 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: grid;
  gap: 10px;
}
.sim-narrate-step {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: 0.79rem;
  line-height: 1.25;
  color: rgba(255, 255, 255, 0.4);
  transition: color 340ms var(--ease-out);
}
.sim-narrate-step-mark {
  position: relative;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.26);
  transition: border-color 340ms var(--ease-out), background 340ms var(--ease-out),
              box-shadow 340ms var(--ease-out);
}
.sim-narrate-step-text { display: block; padding-top: 1px; }
/* The detail line under each tick. Only once the run has finished - during the run the headline
   and its sentence are already saying what is happening, and a second copy under every row would
   be five paragraphs competing with the one that matters. */
.sim-narrate-step-text small {
  display: none;
  margin-top: 3px;
  font-size: 0.72rem;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.56);
}
.sim-narrate[data-state="done"] .sim-narrate-step.is-done .sim-narrate-step-text small { display: block; }
.sim-narrate[data-state="done"] .sim-narrate-steps { gap: 13px; }
.sim-narrate[data-state="done"] .sim-narrate-step { align-items: flex-start; }

.sim-narrate-step.is-now { color: #fff; }
.sim-narrate-step.is-now .sim-narrate-step-mark {
  border-color: var(--color-orange);
  background: var(--color-orange);
  box-shadow: 0 0 0 4px rgba(209, 65, 36, 0.22);
}
.sim-narrate-step.is-done { color: rgba(255, 255, 255, 0.68); }
.sim-narrate-step.is-done .sim-narrate-step-mark {
  border-color: #4ec98a;
  background: #4ec98a;
}
.sim-narrate-step.is-done .sim-narrate-step-mark::after {
  content: '';
  position: absolute;
  left: 4.6px;
  top: 1.4px;
  width: 3.6px;
  height: 7.6px;
  border: solid #0a1120;
  border-width: 0 1.8px 1.8px 0;
  transform: rotate(42deg);
}

.sim-narrate-track {
  display: block;
  margin-top: var(--space-4);
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}
.sim-narrate-track > i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--color-orange), #ffb38f);
  transition: width 620ms var(--ease-out);
}

@media (min-width: 1100px) {
  /* Two-up, which keeps the panel about as tall as the roof beside it - but one column once the
     detail lines open underneath, or the rows tear into ragged pairs. */
  .sim-narrate-steps { grid-template-columns: 1fr 1fr; gap: 10px 18px; }
  .sim-narrate[data-state="done"] .sim-narrate-steps { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  .sim-narrate { padding: var(--space-4); }
  .sim-narrate-steps { grid-template-columns: 1fr 1fr; gap: 9px 16px; }
  .sim-narrate[data-state="done"] .sim-narrate-steps { grid-template-columns: 1fr; }
  /* On a phone the commentary goes ABOVE the drawing. Left in source order it lands under the
     key, the caption and the Start button - so the moment you press that button, the words
     describing what you are watching are off the bottom of the screen. `display: contents` drops
     the wrapper out of the grid so its two children can be ordered against the drawing itself. */
  .mould-side { display: contents; }
  .sim-narrate { order: -1; margin-bottom: var(--space-4); }
  /* THE PANEL AND THE DRAWING HAVE TO BE ON ONE SCREEN TOGETHER, or the words describing what you
     are watching are off the bottom of it the moment you press the button. At desktop sizing the
     panel measured 628px against a 313px drawing on a 390px phone - the two together were taller
     than the viewport. */
  .sim-narrate { padding: var(--space-4); }
  .sim-narrate-line,
  .sim-narrate-ghost { font-size: 1.05rem; }
  .sim-narrate-stage { min-height: 2.3em; }
  .sim-narrate-note { font-size: 0.86rem; margin-top: 8px; }
  .sim-narrate-steps { margin-top: 12px; padding-top: 12px; gap: 7px; }
  .sim-narrate-step { font-size: 0.76rem; gap: 9px; }
  .sim-narrate-step-mark { width: 14px; height: 14px; }
  .sim-narrate[data-state="done"] .sim-narrate-steps { gap: 10px; }
  .sim-narrate-step-text small { font-size: 0.7rem; }
  /* ...but once it has finished, back UNDER the drawing. The done state opens a detail line under
     every tick, which is most of a phone screen, and by then the thing worth looking at is the
     house rather than the commentary. */
  .sim-narrate[data-state="done"] { order: 1; margin-bottom: 0; margin-top: var(--space-4); }
}
@media (max-width: 460px) {
  /* Still two-up. One column costs about sixty pixels of height, and the panel and the house
     have to fit on the same phone screen at the same time or the commentary is not commentary. */
  .sim-narrate-line, .sim-narrate-ghost { font-size: 1.02rem; }
  .sim-narrate-note { font-size: 0.82rem; }
  .sim-narrate-step { font-size: 0.72rem; gap: 8px; }
  .sim-narrate-steps { gap: 8px 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .sim-narrate::after { animation: none; }
  .sim-narrate-dot { animation: none; }
  .sim-narrate-line.is-in,
  .sim-narrate-ghost.is-out,
  .sim-narrate-note.is-in { animation: none; }
  .sim-narrate-ghost { display: none; }
  .sim-narrate-track > i,
  .sim-narrate-step,
  .sim-narrate-step-mark { transition: none; }
}

/* The ring drawn round each source of moisture while it is being named. */
.cause-ring { pointer-events: none; }
/* Everything that is NOT the cause, dimmed. A ring on its own is a weak instruction on a picture
   this busy; taking the light off the rest of the house is not. */
.cause-dim { fill: #04070f; opacity: 0.66; }
.cause-ring-halo { fill: none; stroke: #D14124; stroke-width: 9; opacity: 0.16; }
.cause-ring-line { fill: none; stroke: #ff7a4d; stroke-width: 2; opacity: 0.95; stroke-dasharray: 5 4; }
/* Its name, on the drawing, inside the lit circle. Two words - the sentence stays in the panel. */
.cause-tick { stroke: #ff7a4d; stroke-width: 1.2; opacity: 0.8; fill: none; }
.cause-tag-bg { fill: #10192b; opacity: 0.92; }
.cause-tag {
  fill: #ffe8dd;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 7.6px;
  letter-spacing: 0.01em;
}
.cause-ring-line { animation: causeSpin 7s linear infinite; transform-box: fill-box; transform-origin: 50% 50%; }
@keyframes causeSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cause-ring-line { animation: none; } }

/* The one pass of light. Screen blend so it lifts the drawing rather than veiling it. */
.sim-sweep { mix-blend-mode: screen; pointer-events: none; }

/* --- Simulator: the optional half ---------------------------------------------------------
   Closed by default. Everything inside it only refines the "before" picture, and none of it is
   needed to get a correct answer - so it should not be standing between the visitor and the one
   button on the page. */
.sim-tune {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
  margin-bottom: var(--space-4);
}
.sim-tune > summary {
  cursor: pointer;
  padding: var(--space-4);
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 44px;
  position: relative;
  padding-right: 44px;
}
.sim-tune > summary::-webkit-details-marker { display: none; }
.sim-tune > summary::after {
  content: '';
  position: absolute;
  right: var(--space-4);
  top: 50%;
  width: 9px; height: 9px;
  border-right: 2px solid var(--color-ink-muted);
  border-bottom: 2px solid var(--color-ink-muted);
  transform: translateY(-70%) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.sim-tune[open] > summary::after { transform: translateY(-30%) rotate(-135deg); }
.sim-tune-title {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-navy-dark);
}
.sim-tune-sub { font-size: var(--text-sm); color: var(--color-ink-muted); }
.sim-tune-body { padding: 0 var(--space-4) var(--space-4); }
.sim-tune-body > *:first-child { margin-top: 0; }

/* The button now lives under the drawing, so it spans that column rather than a sidebar. */
.mould-diagram-wrap .sim-cta { margin-top: var(--space-4); }
.mould-diagram-wrap .sim-result { margin-top: var(--space-4); }

/* --- Simulator: how the duct gets outside -------------------------------------------------
   Three real options with three real prices. Showing them here is the difference between a
   customer understanding a quote and being surprised by one. */
.piv-loft-note {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--color-blue);
  background: var(--color-surface-tint-blue);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--color-ink);
}
.piv-loft-note strong { color: var(--color-navy); }

.outlet-wrap {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
}
.outlet-head {
  margin: 0 0 var(--space-3);
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-navy-dark);
}
.outlet-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); }
.outlet-btn {
  display: block;
  padding: 0.6rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-white);
  cursor: pointer;
  text-align: center;
  min-height: 56px;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.outlet-btn:hover { border-color: var(--color-blue); }
.outlet-btn.is-active { border-color: var(--color-blue); background: var(--color-surface-tint-blue); }
.outlet-btn-name {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-navy-dark);
  line-height: 1.25;
}
.outlet-btn-cost {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
}
.outlet-btn.is-active .outlet-btn-cost { color: var(--color-blue); font-weight: 600; }
.outlet-note {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  line-height: 1.6;
}

/* Eaves: fascia, soffit board and gutter. A roof that sits straight on the wall with no overhang
   is a lid, and there is nowhere for a soffit terminal to go. */
/* Eaves. White uPVC fascia and soffit against a red roof, which is what most of the housing
   stock in South East London actually has, and a grey aluminium-look gutter hung off the fascia.
   The shadow line under the soffit is the detail that makes the overhang read as depth rather
   than as a white stripe painted on the wall. */
.eaves-soffit { fill: #eef1f6; stroke: #b3bccb; stroke-width: 0.6; }
.eaves-fascia { fill: #dfe4ec; stroke: #9aa5b6; stroke-width: 0.6; }
/* A darker gutter, so the fascia, the gutter and the terminal plenum stop reading as one white
   mass at the end of the roof. Most of this stock has black or grey half-round on it. */
.eaves-gutter { fill: #55606f; stroke: #39424f; stroke-width: 0.6; }
.eaves-shadow { stroke: #0B1220; stroke-width: 1.1; opacity: 0.22; }

/* The gable: the end wall carried up into the loft, which is where the wall route's louvres go.
   The verge is the mortared edge the tiling finishes against. */
.gable-verge { stroke: #cfd5df; stroke-width: 2.6; stroke-linecap: round; opacity: 0.85; }

/* --- Simulator: the one button that matters -----------------------------------------------
   It sits at the bottom of a column of choices, and it is the only control on the page that
   actually does anything, so it is sized like it. The ring breathes only while it is enabled and
   has not been pressed - a control that pulses after you have used it is just noise. */
.sim-cta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 0;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--color-orange) 0%, #b8371c 100%);
  color: var(--color-white);
  text-align: left;
  cursor: pointer;
  box-shadow: 0 2px 4px rgba(11, 18, 32, 0.12), 0 14px 30px -10px rgba(209, 65, 36, 0.55);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sim-cta:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(11, 18, 32, 0.14), 0 20px 40px -10px rgba(209, 65, 36, 0.6); }
.sim-cta:disabled { background: var(--color-surface); color: var(--color-ink-muted); box-shadow: none; cursor: not-allowed; }
.sim-cta-play {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  position: relative;
}
.sim-cta-play::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 54%;
  transform: translate(-50%, -50%);
  border-left: 14px solid currentColor;
  border-block: 9px solid transparent;
}
.sim-cta:disabled .sim-cta-play { background: rgba(11, 18, 32, 0.08); }
.sim-cta-text { display: block; }
.sim-cta-label {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.sim-cta-sub { display: block; font-size: var(--text-sm); opacity: 0.82; }
.sim-cta.is-ready { animation: simReady 2.4s var(--ease-in-out) infinite; }
@keyframes simReady {
  0%, 100% { box-shadow: 0 2px 4px rgba(11, 18, 32, 0.12), 0 14px 30px -10px rgba(209, 65, 36, 0.55), 0 0 0 0 rgba(209, 65, 36, 0.45); }
  55%      { box-shadow: 0 2px 4px rgba(11, 18, 32, 0.12), 0 14px 30px -10px rgba(209, 65, 36, 0.55), 0 0 0 14px rgba(209, 65, 36, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .sim-cta.is-ready { animation: none; }
}
.sim-blocker { margin-top: var(--space-2); }

/* --- Result panel --- */
.sim-result {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-blue);
  border-radius: var(--radius-md);
  background: var(--color-white);
  box-shadow: var(--shadow-soft);
}
.sim-result h4 {
  font-family: var(--font-heading);
  margin: 0 0 var(--space-2);
  color: var(--color-navy-dark);
  font-size: var(--text-lg);
}
.sim-result-lead { margin: 0 0 var(--space-3); color: var(--color-ink); }
.sim-spec { list-style: none; padding: 0; margin: 0 0 var(--space-3); }
.sim-spec li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-ink);
}
.sim-spec li::before {
  content: '✓';
  position: absolute;
  left: 0;
  top: 0;
  color: var(--color-ok);
  font-weight: 700;
}
.sim-delta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font-size: var(--text-sm);
  font-weight: 600;
}
.sim-delta .from { color: var(--color-danger); }
.sim-delta .to { color: var(--color-ok); }
.sim-delta .arrow { color: var(--color-ink-muted); }
.sim-result .btn { width: 100%; }
.sim-result-flag {
  margin: var(--space-4) 0 0;
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--color-orange);
  background: #fdf1ec;
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: 0.94rem;
  line-height: 1.65;
  color: var(--color-ink);
}
.sim-result-flag strong { color: var(--color-navy); }

.sim-result-note {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  text-align: center;
}

@media (max-width: 768px) {
  .sim-steps { gap: var(--space-1); }
  .sim-step { font-size: var(--text-xs); padding: 0.35rem 0.6rem; }
  .hum-head { flex-direction: row; }
}

/* Reduced motion: kill the page-level smooth scroll and trim transitions to near-instant */
@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;
  }
}

.disclaimer {
  font-size: 0.82rem;
  color: var(--text-muted);
  font-style: italic;
  margin-top: 1rem;
}
.disclaimer-strong {
  background: #fff4e5;
  border: 1px solid var(--flame-orange);
  color: #7a2f19;
  padding: 0.9rem 1.1rem;
  border-radius: var(--radius);
  font-style: normal;
  font-weight: 600;
  max-width: 800px;
  margin: 0 auto 1.5rem;
  text-align: center;
}


/* ===================== PART F TOOL ===================== */
.partf-widget { background: var(--white); border-radius: var(--radius); padding: 1.5rem; }
.partf-add-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
.partf-add-row select,
.partf-add-row input {
  padding: 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.95rem;
}
.room-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.25rem;
}
.room-list li {
  display: flex;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
}
.partf-banner {
  padding: 1rem;
  border-radius: var(--radius);
  font-weight: 700;
  text-align: center;
  margin-bottom: 1rem;
}
.partf-banner.risk-low { background: #e6f4ea; color: #1c6b34; border: 1px solid #1c6b34; }
.partf-banner.risk-borderline { background: #fff4e0; color: #8a6410; border: 1px solid #c98a12; }
.partf-banner.risk-critical { background: #fdecea; color: #8a2e1c; border: 1px solid var(--flame-orange); }


/* ===================== TRUST / TESTIMONIALS ===================== */
.trust-section { text-align: center; }

.before-after-wrap { margin: 2rem 0; }
.before-after-slider {
  position: relative;
  max-width: 700px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}
.ba-img { width: 100%; display: block; }
.ba-before { position: absolute; top: 0; left: 0; }
#baRange {
  position: absolute;
  bottom: 10px;
  left: 5%;
  width: 90%;
  margin: 0;
}
.ba-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--white);
  left: 50%;
  pointer-events: none;
  box-shadow: 0 0 6px rgba(0,0,0,0.4);
}

.reviews-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  max-width: 400px;
  margin: 0 auto;
}


/* ===================== AREAS WE COVER ===================== */
.areas-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1.25rem;
}
.area-card {
  background: var(--color-white);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.area-card:hover,
.area-card:focus-within {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(11, 18, 32, 0.12), 0 2px 4px rgba(11, 18, 32, 0.06);
}


/* ===================== FAQ ===================== */
.faq-accordion { max-width: 760px; margin: 0 auto; }
.faq-item {
  border-bottom: 1px solid var(--border);
}
.faq-question {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 1rem 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1rem;
  cursor: pointer;
  color: var(--navy-dark);
  position: relative;
  padding-right: 2rem;
}
.faq-question::after {
  content: "+";
  position: absolute;
  right: 0;
  top: 1rem;
  font-size: 1.3rem;
  color: var(--ice-blue);
}
.faq-question[aria-expanded="true"]::after { content: "−"; }
.faq-answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
.faq-answer p { padding-bottom: 1rem; color: var(--text-muted); }


/* ===================== ACCREDITATIONS ===================== */
.accreditations-section { text-align: center; }
.accred-strip {
  display: flex;
  gap: 1.5rem;
  justify-content: center;
  flex-wrap: wrap;
}
.accred-badge {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 0.85rem 1.1rem;
  cursor: pointer;
  font-family: var(--font-body);
  text-align: left;
  max-width: 260px;
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.accred-badge:hover,
.accred-badge:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 12px 32px rgba(11, 18, 32, 0.12), 0 2px 4px rgba(11, 18, 32, 0.06);
}
.accred-badge-icon { font-size: 1.4rem; }
.accred-desc-output {
  margin-top: 1.25rem;
  background: var(--white);
  border: 1px solid var(--ice-blue);
  border-radius: var(--radius);
  padding: 0.8rem 1.1rem;
  max-width: 500px;
  margin-inline: auto;
  color: var(--text-muted);
}


/* ===================== FORMS ===================== */
.site-form {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  max-width: 600px;
}
.site-form label { font-weight: 700; font-size: 0.9rem; margin-top: 0.5rem; }
.site-form input,
.site-form textarea,
.site-form select {
  padding: 0.6rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.site-form input:hover,
.site-form textarea:hover,
.site-form select:hover {
  border-color: var(--color-blue);
}
.site-form input:focus-visible,
.site-form textarea:focus-visible,
.site-form select:focus-visible {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px rgba(29, 112, 184, 0.18);
}
.site-form button { margin-top: 1rem; align-self: flex-start; }
.form-status {
  margin-top: 0.75rem;
  background: #eef0f4;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius);
  font-size: 0.88rem;
}

.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  text-align: center;
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.dropzone.dragover {
  border-color: var(--ice-blue);
  background: #eaf3fa;
}
.thumb-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0.75rem 0;
}
.thumb-preview img {
  width: 72px;
  height: 72px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--border);
}

.contact-layout {
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: 2rem;
  align-items: start;
}
.contact-details { padding-top: 0.5rem; }


/* ===================== PORTAL ===================== */
.portal-section {
  background: #e9ecf3;
  border-radius: var(--radius);
}
.portal-card {
  background: var(--color-white);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  box-shadow: var(--shadow-soft);
}
.pipeline-stepper {
  list-style: none;
  display: flex;
  gap: 0.5rem;
  padding: 0;
  flex-wrap: wrap;
}
.pipeline-stepper li {
  flex: 1 1 150px;
  background: #eef0f4;
  border-radius: var(--radius);
  padding: 0.75rem;
  text-align: center;
  font-size: 0.85rem;
  font-weight: 600;
  border: 2px solid transparent;
  color: var(--text-muted);
}
.pipeline-stepper li.current {
  background: var(--navy);
  color: var(--white);
  border-color: var(--ice-blue);
}

.servicing-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1rem;
}
.servicing-table th,
.servicing-table td {
  text-align: left;
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}
.status-overdue { color: var(--flame-orange); font-weight: 700; }
.status-ok { color: #1c6b34; font-weight: 700; }
.tiny-note { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.5rem; }

.doc-list {
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
}
.doc-list li {
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}

.variation-box {
  border: 3px solid var(--flame-orange);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  background: #fff7f4;
  font-size: 0.9rem;
  margin-bottom: 1rem;
}
.signature-pad {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--white);
  touch-action: none;
  cursor: crosshair;
  display: block;
  max-width: 100%;
}
.variation-disclaimer {
  font-size: 0.75rem;
  margin-top: 1.25rem;
}


/* ===================== FOOTER ===================== */
.site-footer {
  background: var(--navy-dark);
  color: #cfd6ea;
  padding: 2.5rem 1.25rem 1.5rem;
}
.footer-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}
.footer-logo { height: 40px; margin-bottom: 0.75rem; filter: brightness(0) invert(1); }
.site-footer a { color: #a9c4ee; }
.footer-copy {
  text-align: center;
  font-size: 0.8rem;
  margin-top: 2rem;
  color: #8b93ab;
}


/* ===================== RESPONSIVE ===================== */
@media (max-width: 768px) {
  .main-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--navy);
    flex-direction: column;
    padding: 0.5rem 1.25rem 1rem;
    display: none;
  }
  .main-nav.open { display: flex; }
  .nav-toggle { display: flex; }
  .sticky-call-btn { display: flex; }
  body { padding-bottom: 3.2rem; }

  .mould-widget { grid-template-columns: 1fr; }
  .contact-layout { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; }
}

@media (max-width: 480px) {
  .hero-ctas { flex-direction: column; align-items: stretch; }
}


/* ===================== SURVEY: how a damp & mould survey works ===================== */
.survey-section { background: var(--color-white); border-radius: var(--radius-lg); }
.survey-steps {
  list-style: none;
  padding: 0;
  margin: var(--space-6) auto 0;
  max-width: 1000px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
}
.survey-step {
  position: relative;
  padding: var(--space-5) var(--space-4) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-white);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.survey-step:hover { transform: translateY(-4px); box-shadow: var(--shadow-soft); }
.survey-step-num {
  position: absolute;
  top: calc(var(--space-4) * -1);
  left: var(--space-4);
  display: grid;
  place-items: center;
  min-width: 48px;
  height: 48px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  background: var(--color-navy);
  color: var(--color-white);
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.survey-step h3 {
  font-size: var(--text-lg);
  margin: var(--space-2) 0 var(--space-2);
  color: var(--color-navy-dark);
}
.survey-step p { margin: 0; color: var(--color-ink-muted); font-size: var(--text-sm); line-height: 1.65; }

.survey-cta {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
}


/* ===================== OUR WORK: before/after gallery ===================== */
.work-section { background: var(--color-surface); border-radius: var(--radius-lg); }
.ba-gallery { max-width: 900px; margin: var(--space-6) auto 0; }
.ba-tag {
  position: absolute;
  z-index: 5;
  top: var(--space-3);
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.ba-tag-before { left: var(--space-3); background: rgba(11, 18, 32, 0.72); color: var(--color-white); }
.ba-tag-after { right: var(--space-3); background: rgba(29, 112, 184, 0.85); color: var(--color-white); }
.ba-handle-grip {
  display: block;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--color-white);
  box-shadow: 0 2px 12px rgba(11, 18, 32, 0.35);
  position: relative;
}
.ba-handle-grip::before,
.ba-handle-grip::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  border-block: 5px solid transparent;
  transform: translateY(-50%);
}
.ba-handle-grip::before { left: 8px; border-right: 6px solid var(--color-navy); }
.ba-handle-grip::after { right: 8px; border-left: 6px solid var(--color-navy); }

.ba-caption {
  text-align: center;
  color: var(--color-ink-muted);
  font-size: var(--text-sm);
  margin: var(--space-3) 0 0;
}
.ba-thumbs {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.ba-thumb {
  border: 1px solid var(--color-border);
  background: var(--color-white);
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-muted);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease-out);
}
.ba-thumb[aria-selected="true"] {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  color: var(--color-navy-dark);
}
.work-empty {
  text-align: center;
  max-width: 520px;
  margin: var(--space-5) auto 0;
}
.work-empty p { color: var(--color-ink-muted); margin-bottom: var(--space-3); }



/* ===================== FORMS ===================== */
.req { color: var(--color-orange); font-weight: 700; }
.form-note {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  margin-top: var(--space-2);
}
.form-status { padding: 0.6rem 0.8rem; border-radius: var(--radius-sm); font-weight: 600; font-size: var(--text-sm); }
.form-status.is-ok { background: var(--color-ok-bg); color: var(--color-ok); border: 1px solid var(--color-ok); }
.form-status.is-error { background: var(--color-danger-bg); color: var(--color-danger); border: 1px solid var(--color-danger); }
.site-form select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-base);
  background: var(--color-white);
  color: var(--color-ink);
  margin-bottom: 1rem;
}

@media (max-width: 768px) {
  .survey-cta { flex-direction: column; }
}



/* ===================== PART F EXTRACT CHECK ===================== */
.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;
}
.room-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-white);
  margin-bottom: var(--space-2);
}
.room-item > span:first-child { flex: 1; font-size: var(--text-sm); }
.room-rate--none { font-weight: 500; font-size: var(--text-sm); color: var(--color-ink-muted); }
.partf-step-note { margin: -4px 0 var(--space-3); font-size: var(--text-sm); color: var(--color-ink-muted); }
.room-rate {
  font-family: var(--font-heading);
  color: var(--color-navy);
  font-size: var(--text-base);
  white-space: nowrap;
}
.room-remove {
  border: none;
  background: var(--color-surface);
  color: var(--color-ink-muted);
  width: 28px; height: 28px;
  border-radius: 50%;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  flex: none;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.room-remove:hover { background: var(--color-danger-bg); color: var(--color-danger); }

.partf-banner {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  text-align: center;
  padding: var(--space-5) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-surface-tint-blue);
  border: 1px solid var(--color-blue);
  margin-top: var(--space-4);
}
.partf-total {
  font-family: var(--font-heading);
  font-size: var(--text-4xl);
  font-weight: 700;
  color: var(--color-navy);
  line-height: 1;
}
.partf-total-label { font-weight: 600; color: var(--color-navy-dark); font-size: var(--text-sm); }
.partf-total-note {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  max-width: 46ch;
  margin: var(--space-2) auto 0;
  line-height: 1.55;
}
.partf-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.partf-add-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
/* A select sizes to its longest option, and a flex item will not shrink below its own
   min-content width unless told to. Without min-width:0 the long room labels pushed the
   whole page 94px wider than a phone viewport. */
.partf-add-row select { flex: 1 1 260px; min-width: 0; }
.partf-add-row input[type="number"] { width: 84px; }
.partf-add-row select,
.partf-add-row input[type="number"] {
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-base);
  background: var(--color-white);
  color: var(--color-ink);
}


/* ===================== SIMULATOR STAGE (Phase 5) ===================== */
/* The stage is painted inside the SVG now, so the element itself is transparent. */
.house-svg {
  /* NOT transparent. The drawing paints its own sky, so an opaque ground behind it is never seen
     when things are working - and when they are not, the visitor sees the night sky rather than a
     white hole where the house should be. */
  background: #0c1730;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 18px 48px rgba(11, 18, 32, 0.28), 0 2px 6px rgba(11, 18, 32, 0.16);
}

/* The whole-dwelling figure. The number gets to be a number - it is the one thing on this panel
   somebody will write down - and the sentence beside it carries the caveat that stops it being a
   quote. */
.sim-result-rate {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-tint-blue);
  border-radius: var(--radius-md);
  border-left: 3px solid var(--ice-blue);
}
.sim-rate-figure {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.55rem;
  line-height: 1.1;
  color: var(--navy);
  flex: none;
}
.sim-rate-body { font-size: 0.88rem; line-height: 1.55; color: var(--text-muted); }
@media (max-width: 560px) {
  .sim-result-rate { flex-direction: column; gap: var(--space-2); }
}

/* --- The engineer -------------------------------------------------------------------------
   Read from the kit, not from the body, and never from a face - see the note in engineerSymbol.
   Everything is flat: a highlight on a figure this size reads as a cartoon before it reads as
   light. */
.eng-boot { fill: #1d2026; }
/* ORANGE WORK TROUSERS. With the white polo and the hard hat that is the whole read: you know what
   he does before you have looked at him, which is the point of drawing the kit rather than the
   person. */
.eng-trews { fill: #D9541F; stroke: #A83E13; stroke-width: 0.22; }
.eng-pad { fill: #2b3038; }
.eng-belt { fill: #1a1d23; }
.eng-pouch { fill: #2a2f38; }
/* WHITE, because the mark goes on it. Navy on navy put the logo on a body its own colour. */
.eng-polo { fill: #f4f6f8; stroke: #c8d0d9; stroke-width: 0.28; }
.eng-polo-shade { fill: #cfd6de; opacity: 0.75; }
.eng-collar { fill: none; stroke: #1A3B8B; stroke-width: 0.7; stroke-linejoin: round; }
.kit-box { fill: #c2a077; stroke: #9a7f5c; stroke-width: 0.35; }
.kit-box-lid { fill: #d4b48d; }
.kit-tape { stroke: #8d7350; stroke-width: 0.5; }
.kit-label { fill: #eee6d6; }
.eng-sleeve-line { stroke: #f4f6f8; }
.eng-sleeve-edge { stroke: #c3ccd6; }
.eng-skin-line { stroke: #c99b76; }
.eng-skin { fill: #c99b76; }
.eng-hair { fill: #4a382b; }
/* A HARD HAT, not a cap: a dome over the whole top of the head with a brim right round it, and
   the ribs that make it a hat rather than a bowl. Drawn as a band across the crown the skull shows
   above it and he reads as bald with a sweatband on, which is exactly what the cap did. */
.eng-hat { fill: #E8641E; stroke: #A8410E; stroke-width: 0.22; }
.eng-hat-brim { fill: #C9500F; }
.eng-hat-rib { fill: none; stroke: #C05412; stroke-width: 0.3; opacity: 0.85; }

/* The tool bag. It goes in and out with him, which is most of what says "on a job" rather than
   "standing in a garden". */
.eng-bag { fill: #2b3240; stroke: #171c25; stroke-width: 0.3; }
.eng-bag-top { fill: #3c4657; }
.eng-bag-handle { fill: none; stroke: #1d232e; stroke-width: 0.55; stroke-linecap: round; }
.eng-bag-tool { stroke: #c8cfd8; stroke-width: 0.5; stroke-linecap: round; }

/* A pair of steps. The raked back legs are the part that stops it reading as a ladder leaning on
   nothing. */
.eng-steps-stile { fill: none; stroke: #b9a271; stroke-width: 1.2; stroke-linecap: round; }
.eng-steps-back { fill: none; stroke: #9d885d; stroke-width: 1.1; stroke-linecap: round; }
.eng-steps-tread { stroke: #cdb684; stroke-width: 1.1; stroke-linecap: round; }
.eng-steps-top { fill: #d6c08a; }
.eng-board { fill: #e6dcc4; stroke: #a8a08a; stroke-width: 0.4; }
.eng-board-clip { fill: #8a919b; }
.eng-meter { fill: #2b3240; }
.eng-meter-lcd { fill: #9fd3a8; }
.eng-meter-vane { fill: none; stroke: #a8bcd8; stroke-width: 0.3; stroke-linecap: round; }
.eng-coil { fill: none; stroke: #b8c0ca; stroke-width: 1.6; }
.eng-coil-eye { fill: none; stroke: #b8c0ca; stroke-width: 1.1; }
.eng-coil-rib { stroke: #cdd4dc; stroke-width: 0.45; opacity: 0.8; }
.eng-box { fill: #D14124; }
.eng-box-lid { fill: #b3341b; }
.eng-box-handle { fill: none; stroke: #2a2f38; stroke-width: 0.7; }

/* --- Why it matters -------------------------------------------------------------------------
   Deliberately NOT four cards in a grid. A grid flattens everything on it to the same weight, and
   the quotation that carries the whole argument ends up looking like the paragraph about dust
   mites. This is editorial: a pull quote, three numbers, two columns of prose, then citations.

   And deliberately NOT an alarm panel. This is the section where a parent finds out it is not
   their fault, and a red-bordered warning box says the opposite of that before a word is read. */
.health-section { background: var(--bg); }
/* Left, not centred. Everything below it is left-aligned to a measure, and a centred lead
   floating over the top of that reads as a different page. */
.health-section .section-intro {
  max-width: 62ch;
  margin-left: 0;
  margin-right: 0;
  text-align: left;
}

/* 1. The pull quote. It is the government saying it is not your fault - the strongest sentence
      available on this subject, and not ours - so it leads and it gets the contrast band. */
.health-quote {
  /* Left, on the same edge as the lead and the numbers. Centred inside a wider container it sat
     on no grid line at all, which is the one thing a page like this cannot afford. */
  margin: var(--space-6) 0 0;
  max-width: 62rem;
  padding: clamp(1.75rem, 4vw, 3rem) clamp(1.5rem, 4vw, 3.25rem);
  background: linear-gradient(150deg, var(--navy) 0%, var(--navy-dark) 100%);
  border-radius: var(--radius-lg);
  color: #fff;
  position: relative;
  overflow: hidden;
}
/* A quiet oversized quote mark, cropped by the corner. Enough to say "quotation" and not enough
   to be a graphic. */
.health-quote::before {
  content: "\201C";
  position: absolute;
  /* A quote glyph carries its weight at the top of the em box, so a negative offset crops it to
     two blobs in the corner. It sits inside the box. */
  top: 0.5rem;
  right: 1.5rem;
  font-family: var(--font-heading);
  font-size: 8.5rem;
  line-height: 1;
  color: #fff;
  opacity: 0.07;
  pointer-events: none;
}
.health-quote blockquote { margin: 0; }
.health-quote p {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(1.15rem, 2.5vw, 1.6rem);
  line-height: 1.42;
  letter-spacing: -0.011em;
  max-width: 40ch;
}
.health-quote figcaption {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.health-quote-who {
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
}
.health-quote-doc {
  font-size: 0.82rem;
  line-height: 1.45;
  opacity: 0.68;
  max-width: 46ch;
}

/* 2. The numbers. A number is the only form of this anybody remembers, so each one gets room and
      nothing competes with it. Hairline rules, not boxes. */
.health-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  margin-top: var(--space-6);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.health-stat {
  padding: var(--space-5) var(--space-4);
  border-left: 1px solid var(--border);
}
.health-stat:first-child { border-left: 0; padding-left: 0; }
.health-stat:last-child { padding-right: 0; }
.health-stat-num {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(2.1rem, 5vw, 3rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--navy);
}
.health-stat-unit {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ice-blue);
}
.health-stat p {
  margin: var(--space-3) 0 0;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 30ch;
}

/* 3. Prose. No cards, no borders, no shadows - text that trusts itself does not need a box. */
.health-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--space-5) var(--space-6);
  margin-top: var(--space-6);
}
.health-cols h3 {
  margin: 0 0 var(--space-3);
  font-size: 1.22rem;
  letter-spacing: -0.012em;
  color: var(--navy);
}
.health-cols p {
  margin: 0 0 var(--space-3);
  line-height: 1.68;
  max-width: 46ch;
}
.health-cols p:last-child { margin-bottom: 0; }

/* Quotations inside running text. `q` gets real quote marks and a slight colour shift, so a
   quoted sentence reads as quoted without being pulled out of the paragraph. */
.health-section q { quotes: "\201C" "\201D" "\2018" "\2019"; color: var(--text); font-style: normal; }

/* 4. The findings. A source set underneath in small caps is what a citation looks like, and it is
      the difference between a claim and a reference. */
.health-evidence { margin-top: var(--space-6); }
.health-evidence h3 {
  margin: 0 0 var(--space-4);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.health-findings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
}
.health-findings li { border-top: 2px solid var(--navy); padding-top: var(--space-3); }
.health-findings li:last-child { border-top-color: var(--border); }
/* The last item is an observation of ours rather than a quoted finding, so it does not get a
   source line pretending to be one - it gets our own voice, in italic, clearly not a citation. */
.health-findings li:last-child .health-src {
  font-style: italic;
  font-size: 0.86rem;
  letter-spacing: 0;
  color: var(--ice-blue);
}
.health-findings p {
  margin: 0;
  line-height: 1.6;
  font-size: 0.98rem;
  max-width: 40ch;
}
.health-src {
  margin-top: var(--space-3) !important;
  font-size: 0.76rem;
  line-height: 1.5;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

/* 5. The close. Narrow measure, and the one sentence that is ours in bold. */
.health-close {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
  max-width: 60ch;
}
.health-close p { margin: 0 0 var(--space-3); line-height: 1.68; }
.health-close p:last-child { margin-bottom: 0; }
.health-awaab { color: var(--text-muted); font-size: 0.94rem; }
.health-close strong { color: var(--navy); }

.health-sources {
  margin-top: var(--space-5);
  font-size: 0.78rem;
  line-height: 1.65;
  color: var(--text-muted);
  max-width: 84ch;
}
.health-sources strong { color: var(--text); }

@media (max-width: 780px) {
  /* Three numbers side by side stop being numbers and become a table. */
  .health-stats { grid-template-columns: 1fr; }
  .health-stat {
    border-left: 0;
    border-top: 1px solid var(--border);
    padding: var(--space-4) 0;
  }
  .health-stat:first-child { border-top: 0; }
  .health-stat:last-child { padding-right: 0; }
  .health-stat p { max-width: none; }
  .health-quote::before { font-size: 5.5rem; top: 0.4rem; right: 1rem; }
}

/* The sign-off card. It is a sign-off, not a testimonial - the line under the words is what
   makes it mean something, and there is nothing on it pretending to be a review. */
.signoff-card {
  fill: #ffffff;
  stroke: #d6d9e0;
  stroke-width: 0.8;
  filter: drop-shadow(0 3px 10px rgba(11, 18, 32, 0.28));
}
.signoff-head {
  font-family: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  font-size: 10px;
  font-weight: 700;
  fill: #1A3B8B;
  letter-spacing: -0.01em;
}
.signoff-sub {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 5.9px;
  font-weight: 500;
  fill: #4A5568;
}

/* The loft question, when the "pick one for me" button needs it answered before it can act. It
   comes off as soon as it is answered, or after a few seconds if it is not - a control that stays
   lit is a control that looks broken. */
.loft-toggle-wrap.is-asking {
  border-radius: var(--radius-md);
  outline: 2px solid var(--ice-blue);
  outline-offset: 6px;
  animation: askPulse 1.1s ease-in-out 3;
}
@keyframes askPulse {
  0%, 100% { outline-color: rgba(29, 112, 184, 0.35); }
  50% { outline-color: rgba(29, 112, 184, 1); }
}
@media (prefers-reduced-motion: reduce) {
  .loft-toggle-wrap.is-asking { animation: none; outline-color: var(--ice-blue); }
}

/* Extract fans. The sleeve is the part that says this goes OUTSIDE rather than into a cupboard,
   so it is drawn as a hole through the masonry - darker than the casing, with the backdraught
   shutter visible at the far end of it. */
.fan-sleeve { fill: #2f3845; stroke: #566274; stroke-width: 0.6; opacity: 0.95; }
.fan-shutter { stroke: #c3ccd9; stroke-width: 1.1; fill: none; stroke-linecap: round; }
.fan-fascia { fill: #f4f7fb; stroke: #8a94a5; stroke-width: 0.7; }
.fan-cord { stroke: #b9c3d1; stroke-width: 0.7; }
.fan-cord-bead { fill: #e8edf4; stroke: #96a1b2; stroke-width: 0.4; }
.fan-strap { stroke: #8d99aa; stroke-width: 1.1; stroke-linecap: round; }

/* What he says at the end, in a bubble beside him. It does not say "healthy": that is a claim
   about a person's health, and every health statement on this site is a direct quotation from a
   named source precisely so the business never makes one in its own voice. */
.speech-body {
  fill: #ffffff;
  stroke: #d6d9e0;
  stroke-width: 0.8;
  filter: drop-shadow(0 3px 10px rgba(11, 18, 32, 0.3));
}
.speech-head {
  font-family: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  font-size: 7.4px;
  font-weight: 700;
  fill: #1A3B8B;
  letter-spacing: -0.01em;
}
.speech-sub {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 5.6px;
  font-weight: 500;
  fill: #4A5568;
}

/* The drill. Three shapes and it is unmistakable, which is the whole point of putting one in his
   hand rather than relying on the pose. */
.eng-tool { fill: #2f3742; }
.eng-tool-chuck { fill: #9aa5b4; }
.eng-tool-batt { fill: #D14124; }

/* The background ventilator, with the air going through it. Every stream in this drawing has to
   start or finish outside, and the ones that start at a trickle vent used to begin in mid-room -
   which quietly left out the one thing extract-only systems depend on.

   Static, deliberately. This is the one place that has to say a DIRECTION and nothing else, and
   there is already plenty moving. */
.vent-slot { fill: #33404f; stroke: #7c8899; stroke-width: 0.5; }
.vent-air--in .vent-chev {
  fill: none;
  stroke: #8fd0f5;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.92;
}
.vent-air--out .vent-chev {
  fill: none;
  stroke: #b7bd93;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

/* --- Channel marks ---------------------------------------------------------------------------
   One sprite, emitted into every page by build-pages.js. `currentColor` everywhere, so a mark
   picks up whatever it is sitting on - except the WhatsApp one where the background will take
   ONE TREATMENT FOR ALL OF THEM: every mark is a single-colour line glyph taking the colour of
   the text beside it. A row mixing WhatsApp green, Facebook blue and the four-colour G read as
   stickers dropped onto the page rather than as part of it, and the shapes carry the recognition
   on their own. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ic {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  fill: currentColor;
  vertical-align: -0.16em;
}
.btn .ic { margin-right: 0.5em; }
.nav-call .ic { margin-right: 0.4em; }

/* A channel, as a row you can pick out of a list rather than as another line of blue text. */
.social-link {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.social-link .ic { width: 1.15em; height: 1.15em; }

/* A channel is a thing you tap, so it should look like one. These were underlined blue text with
   a small mark in front - which on a phone is indistinguishable from any other link in the copy,
   and is what "they don't look professional" was. As chips they read as a set of ways to reach
   us, they clear the 44px tap target on their own, and the mark is the part the eye lands on.
   Placed LAST in this file on purpose: every earlier `.social-link` rule is more general. */
.contact-channels .social-link,
.footer-contact .social-link {
  gap: 0.6em;
  min-height: 46px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-ink);
  font-weight: 600;
  font-size: 0.94rem;
  text-decoration: none;
  width: 100%;
  transition: border-color var(--dur-fast, .18s) ease, background var(--dur-fast, .18s) ease;
}
.contact-channels .social-link:hover,
.footer-contact .social-link:hover {
  border-color: var(--color-blue);
  background: var(--color-surface);
}
.contact-channels li,
.footer-contact li { margin-bottom: var(--space-2); }

/* In the footer the ground is navy, so the chip is a light on it rather than a card. */
.footer-contact .social-link {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.16);
  color: var(--color-white);
}
.footer-contact .social-link:hover {
  background: rgba(255, 255, 255, 0.13);
  border-color: rgba(255, 255, 255, 0.32);
}

/* The marks keep their own colours where the brand allows it. WhatsApp green and Facebook blue
   are both permitted; the Google G is used in its own four and never recoloured. Instagram stays
   the one-colour outline glyph - the gradient is theirs, not ours to put on a contractor's site -
   so it takes the text colour and earns its recognition from the shape. */
.contact-channels .social-link .ic,
.footer-contact .social-link .ic { width: 1.3em; height: 1.3em; }

@media (max-width: 768px) {
  .contact-channels .social-link,
  .footer-contact .social-link { font-size: 0.9rem; }
}

.contact-channels { list-style: none; padding: 0; margin: 0 0 var(--space-3); }
.contact-channels li { margin-bottom: 0.45rem; }

.footer-contact li { margin-bottom: 0.5rem; }
.footer-note {
  margin-top: 0.75rem;
  font-size: 0.82rem;
  line-height: 1.5;
  opacity: 0.72;
  max-width: 30ch;
}

/* --- The van -------------------------------------------------------------------------------
   White body, navy wrap, flame hairline: the brand at a distance, in two colours, which is all
   livery ever gets. Everything here is flat - a gradient and a gloss on a vehicle in a drawing
   this size reads as a toy car, not as a van. */
.van-shell { fill: #f2f5f8; stroke: #b9c2cc; stroke-width: 0.7; stroke-linejoin: round; }
.van-wrap { fill: #1A3B8B; }
.van-flame { fill: none; stroke: #D14124; stroke-width: 1.5; }
.van-shut { stroke: #b3bcc7; stroke-width: 0.7; stroke-linecap: round; }
.van-glass { fill: #97b5cd; stroke: #6f8ba3; stroke-width: 0.6; stroke-linejoin: round; }
.van-glass-lit { fill: #cfe0ee; opacity: 0.55; }
.van-trim { fill: #414c5c; }
.van-trim-line { stroke: #414c5c; stroke-width: 1.6; stroke-linecap: round; }
.van-lamp { fill: #dfe7ee; stroke: #9aa7b5; stroke-width: 0.5; }
.van-lamp-rear { fill: #c0342a; stroke: #8d251d; stroke-width: 0.5; }
/* An arch is an OPENING with a wheel in it, not an arc drawn on the panel. The dark inner face
   and the lip either side are what make that read. */
.van-arch-in { fill: #1b2029; }
.van-arch { fill: none; stroke: #59626f; stroke-width: 1.5; }
/* The line every van has and almost nothing else does. */
.van-crease { fill: none; stroke: #c3cbd4; stroke-width: 1.1; opacity: 0.9; }
.van-grille { fill: #2f3742; }
.van-bumper { fill: #4a535f; }
.van-sheen-line { stroke: #ffffff; stroke-width: 1.2; opacity: 0.5; stroke-linecap: round; }
.van-spin { transform-box: view-box; }
.van-rack { fill: #464e59; stroke: #2f353d; stroke-width: 0.4; }
/* Trade yellow, and not restyled toward the brand palette - a ladder is yellow because it has to
   be seen, and that is the whole reason it reads as one at this size. */
.van-ladder { fill: #F0B429; stroke: #A9781A; stroke-width: 0.4; }
.van-ladder-rung { stroke: #C6931F; stroke-width: 1.2; stroke-linecap: round; }
.van-sheen { stroke: #ffffff; stroke-width: 1.4; opacity: 0.75; stroke-linecap: round; }

/* Livery text. The one thing in this drawing that has to be READ rather than looked at, so it
   gets the brand face at a real size rather than being squeezed to fit. */
/* The two contact lines sit ON the navy wrap now, between the wheel arches, so they are white.
   Left navy they were navy on navy and simply did not exist. */
.van-line {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 8px;
  font-weight: 700;
  fill: #ffffff;
  letter-spacing: -0.01em;
}
/* The handle is the longest thing on the van and it sets its own size: at the line above's size
   it runs into the rear wheel arch, and type that crosses an arch is type nobody signwrote. */
.van-line--sm { font-size: 6.5px; letter-spacing: -0.015em; }
/* No longer used: the email came off the van. Twenty-nine characters is the longest thing on the
   vehicle and it was what forced the phone number down to 5.9px, which could not be read. Kept
   defined in case a shorter line ever wants this slot. */
.van-mail {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 5.3px;
  font-weight: 600;
  fill: #ffffff;
  letter-spacing: -0.005em;
  opacity: 0.94;
}
.van-url {
  font-family: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  font-size: 9.2px;
  font-weight: 700;
  fill: #1A3B8B;
  letter-spacing: 0.005em;
}

/* Room labels sit on lit interior surfaces, so they keep the dark ink colour */
.room-label { paint-order: stroke; stroke: #ffffff; stroke-width: 2.5px; stroke-linejoin: round; }
/* A name for a room too narrow to take one at full size. A landing is 48 units across and
   "Landing" is not, so it ran out of both ends and into the bedrooms either side. */
.room-label--tight { font-size: 6.4px; letter-spacing: 0.01em; stroke-width: 2px; }

/* --- Leader-line callouts --- */
.callout-dot { fill: var(--color-white); stroke: var(--color-blue); stroke-width: 1.6; }
/* Two-tone leader: a pale halo under a dark core. A single white line vanished against the
   plaster wall, which is where most of these callouts point, leaving the chip apparently
   floating unattached to anything. */
.callout-leg-halo {
  fill: none;
  stroke: rgba(255, 255, 255, 0.85);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.callout-leg {
  fill: none;
  stroke: #16233d;
  stroke-width: 1;
  stroke-opacity: 0.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.callout-chip { fill: rgba(9, 18, 40, 0.92); stroke: rgba(120, 168, 224, 0.55); stroke-width: 0.8; }
.callout-text {
  font-family: var(--font-body);
  font-size: 8.5px;
  font-weight: 600;
  fill: #ffffff;
  letter-spacing: 0.01em;
}

/* --- Cold-surface toggle --- */
.sim-toggles {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}
.sim-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  align-self: flex-start;
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-white);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-muted);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease-out);
}
.sim-toggle:hover { border-color: var(--color-blue); color: var(--color-navy-dark); }
.sim-toggle-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-border);
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sim-toggle.is-on {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  color: var(--color-navy-dark);
}
.sim-toggle.is-on .sim-toggle-dot {
  background: var(--color-blue);
  box-shadow: 0 0 0 3px rgba(29, 112, 184, 0.22);
}
.sim-toggle-hint { font-size: var(--text-xs); color: var(--color-ink-muted); }


/* ===================== PHASE 6: HERO ===================== */
.hero {
  position: relative;
  min-height: min(92vh, 860px);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--color-navy-dark);
  isolation: isolate;
}
.hero-media { position: absolute; inset: 0; z-index: 0; }
.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 45%;
  /* The source is a dark venue already; a slight lift keeps the ductwork readable under the scrim */
  filter: brightness(1.08) contrast(1.04) saturate(0.92);
}
/* Two-part scrim: a vertical wash for overall legibility, plus a warm brand tint from the left
   so the type sits on brand colour rather than flat black. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(105deg, rgba(10, 20, 48, 0.94) 0%, rgba(10, 20, 48, 0.78) 42%, rgba(10, 20, 48, 0.38) 100%),
    linear-gradient(180deg, rgba(10, 20, 48, 0.55) 0%, rgba(10, 20, 48, 0.1) 40%, rgba(10, 20, 48, 0.72) 100%);
}

.hero-content {
  position: relative;
  z-index: 3;
  color: var(--color-white);
  max-width: 1280px;
  width: 100%;
  padding: var(--space-7) var(--space-4);
  text-align: left;
}
.hero-eyebrow {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #9dc2ec;
  margin-bottom: var(--space-3);
}
.hero-content h1 {
  color: var(--color-white);
  font-size: clamp(2.2rem, 6vw, 4.1rem);
  line-height: 1.03;
  letter-spacing: -0.02em;
  max-width: 15ch;
  margin-bottom: var(--space-4);
  text-wrap: balance;
}
.hero-subhead {
  font-size: clamp(var(--text-base), 1.5vw, var(--text-lg));
  color: rgba(255, 255, 255, 0.82);
  max-width: 52ch;
  margin-bottom: var(--space-5);
}
.hero-ctas { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-7); }
.btn-ghost {
  background: rgba(255, 255, 255, 0.08);
  color: var(--color-white);
  border: 1px solid rgba(255, 255, 255, 0.34);
  backdrop-filter: blur(8px);
}
.btn-ghost:hover { background: rgba(255, 255, 255, 0.16); }

.hero-trust {
  list-style: none;
  padding: var(--space-4) 0 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  max-width: 680px;
}
.hero-trust li { display: flex; flex-direction: column; gap: 2px; }
.hero-trust strong {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  color: var(--color-white);
}
.hero-trust span { font-size: var(--text-xs); color: rgba(255, 255, 255, 0.62); }

.hero-scroll {
  position: absolute;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  width: 26px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.32);
  border-radius: 999px;
  display: grid;
  place-items: start center;
  padding-top: 8px;
}
.hero-scroll-line {
  width: 2px;
  height: 8px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.8);
  animation: heroScroll 1.8s var(--ease-in-out) infinite;
}
@keyframes heroScroll {
  0%   { transform: translateY(0); opacity: 1; }
  70%  { transform: translateY(16px); opacity: 0; }
  100% { transform: translateY(0); opacity: 0; }
}

@media (max-width: 768px) {
  .hero { min-height: 78vh; }
  /* LEFT on a phone, the same side as the desktop hero. This was set to the right for a while,
     to keep the copy off the run of duct in the photograph, and it read as inconsistent with
     every other width — so it came back. Nothing here overrides the base `text-align: left`; the
     only phone-specific things left are the height and the tighter gap.

     If it is ever moved back to the right, the phone scrim at the END of this file has to be
     flipped with it. The two go together: the wash is dark on whichever side the copy is on, and
     alignment alone does nothing because the type just moves onto the light half. */
  .hero-trust { gap: var(--space-4); }
  .hero-scroll { display: none; }
}


/* ===================== PHASE 6: PART F TOOL v2 ===================== */
.partf-step { margin-bottom: var(--space-6); }
.partf-step-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
  margin-bottom: var(--space-3);
}
.partf-step-head span {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--color-navy);
  color: var(--color-white);
  font-size: var(--text-xs);
  flex: none;
}
.partf-systems {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-3);
}
.partf-system {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
  cursor: pointer;
  font-family: var(--font-body);
  transition: all var(--dur-fast) var(--ease-out);
}
.partf-system:hover { transform: translateY(-2px); box-shadow: var(--shadow-soft); }
.partf-system strong { color: var(--color-navy-dark); font-size: var(--text-base); }
.partf-system small { color: var(--color-ink-muted); font-size: var(--text-xs); line-height: 1.5; }
.partf-system[aria-checked="true"] {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  box-shadow: var(--shadow-soft);
}

.partf-dwelling {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
  align-items: start;
}
.partf-dwelling label { display: block; font-weight: 600; font-size: var(--text-sm); margin-bottom: var(--space-1); }
.partf-dwelling input[type="number"] {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-base);
  background: var(--color-white);
  color: var(--color-ink);
}
.field-hint { display: block; font-size: var(--text-xs); color: var(--color-ink-muted); margin-top: var(--space-1); }
.partf-occupancy { display: flex; flex-direction: column; gap: var(--space-2); }
.check-row { display: flex; align-items: flex-start; gap: var(--space-2); font-weight: 500 !important; cursor: pointer; }
.check-row input { margin-top: 3px; accent-color: var(--color-blue); flex: none; }
.check-row span { font-size: var(--text-sm); color: var(--color-ink); }

.partf-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
  text-align: center;
}
.partf-figure { display: flex; flex-direction: column; gap: var(--space-1); }
.partf-figure .partf-total-label em { display: block; font-size: var(--text-xs); color: var(--color-ink-muted); font-style: normal; margin-top: 2px; }
.partf-flags {
  list-style: none;
  padding: var(--space-4) 0 0;
  margin: var(--space-3) 0 0;
  border-top: 1px solid var(--color-border);
  text-align: left;
}
.partf-flags li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--color-ink);
}
.partf-flags li:last-child { margin-bottom: 0; }
.partf-flags li::before {
  content: '!';
  position: absolute;
  left: 0; top: 1px;
  display: grid;
  place-items: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--color-warn-bg);
  color: var(--color-warn);
  font-size: 11px;
  font-weight: 700;
}


/* ===================== PHASE 6: BEFORE/AFTER SLIDER ===================== */
.before-after-slider {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 18px 48px rgba(11, 18, 32, 0.22);
  aspect-ratio: 4 / 3;
  background: var(--color-navy-dark);
}
.ba-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ba-before { z-index: 2; }
.ba-after { z-index: 1; }
#baRange {
  position: absolute;
  inset: 0;
  z-index: 4;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
}
.ba-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 3;
  width: 2px;
  margin-left: -1px;
  background: rgba(255, 255, 255, 0.92);
  display: grid;
  place-items: center;
  pointer-events: none;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
}
.ba-caption strong { display: block; color: var(--color-navy-dark); font-family: var(--font-heading); margin-bottom: 2px; }


/* ===================== PHASE 6: SERVICES ===================== */
.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  max-width: 1160px;
  margin: 0 auto;
}
.service-card {
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-white);
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.service-card:hover,
.service-card:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-soft);
  border-color: var(--color-blue);
}
.service-icon {
  width: 40px;
  height: 40px;
  color: var(--color-blue);
  margin-bottom: var(--space-3);
}
.service-card h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.service-card p { margin: 0; color: var(--color-ink-muted); font-size: var(--text-sm); line-height: 1.65; }


/* ===================== PHASE 6: FEATURE BANDS ===================== */
.feature-band { background: var(--color-white); }
.feature-band-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--space-7);
  padding: var(--space-8) var(--space-4);
}
.feature-band:nth-of-type(even) .feature-media { order: 2; }
.feature-media { margin: 0; }
.feature-media img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 52px rgba(11, 18, 32, 0.24);
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.feature-eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-blue);
  margin-bottom: var(--space-2);
}
.feature-copy h2 { text-align: left; margin-bottom: var(--space-4); }
.feature-copy p { color: var(--color-ink-muted); line-height: 1.75; }
.feature-copy .btn { margin-top: var(--space-3); }

@media (max-width: 900px) {
  .feature-band-inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .feature-band:nth-of-type(even) .feature-media { order: 0; }
  .feature-copy h2 { text-align: center; }
}

/* The Phase 1 hero centred its CTA row; the new hero is left-aligned, so re-anchor it.
   SCOPED TO WIDE. Unconditional, this sat AFTER the phone block that sets `flex-end` and beat it —
   a media query adds no specificity — so on a phone the copy was right-aligned and its buttons
   were left-aligned under it. Eighth instance of that same fault in this stylesheet. */
@media (min-width: 769px) {
  .hero-ctas { justify-content: flex-start; }
}
@media (max-width: 480px) {
  .hero-ctas { flex-direction: column; align-items: stretch; }
}


/* ===================== PHASE 7: PLANT, TERMINALS AND AIR ===================== */
/* Ductwork reads as three weights: trunk to outside, branch runs, then terminals.

   Four air streams, not two. Diagrams that colour ducts by "supply" and "extract" alone cannot
   tell the story heat recovery exists to tell, because the interesting thing is that each stream
   CHANGES as it crosses the exchanger. Hue carries fresh (blue) against stale (orange); lightness
   carries temperature. So cold fresh air arrives dark and cold, leaves the core bright and warm;
   warm stale air arrives bright, and leaves for the outside world dulled and cooled. */
.duct-line { fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 4.2; }
.duct-line.intake  { stroke: #35708f; }   /* outside -> unit: cool fresh air */
.duct-line.supply  { stroke: #59a7d4; }   /* unit -> rooms: warm fresh air */
.duct-line.extract { stroke: #c4713f; }   /* wet rooms -> unit: warm stale air */
.duct-line.exhaust { stroke: #8a5b45; }   /* unit -> outside: cooled stale air */
.duct-line.trunk   { stroke-width: 6.8; }

/* Insulation on the two runs between the unit and outside. Those are the only ducts that have to
   be lagged - they carry air at outdoor temperature through warm indoor air, so bare they sweat
   onto whatever is underneath them. It is the detail nearly every published drawing omits. */
.duct-lag {
  fill: none;
  stroke: #d8dfe8;
  stroke-width: 10.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.95;
}
.duct-lag-wrap {
  fill: none;
  stroke: #a9b4c4;
  stroke-width: 10.4;
  stroke-linecap: butt;
  stroke-dasharray: 0.9 4.6;
  opacity: 0.55;
}

/* Plant: white bodies with a navy case line, so the units read as real equipment on the dark stage */
.vent-unit-body {
  fill: url(#matCasing);
  stroke: #7f8b9d;
  stroke-width: 1;
  filter: url(#lift);
}
.unit-accent { fill: var(--color-blue); opacity: 0.85; }
/* Inside the heat recovery unit. Each stream is filtered on the way IN and fanned on the way
   OUT, and the two cross at the core - so the supply fan is genuinely on the supply side and the
   extract fan on the exhaust side, which is how the machine is built and why the supply fan is
   the one commissioned to the room rates. */
.unit-stream { fill: none; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; opacity: 0.5; }
.unit-stream--intake  { stroke: #35708f; }
.unit-stream--supply  { stroke: #59a7d4; }
.unit-stream--extract { stroke: #c4713f; }
.unit-stream--exhaust { stroke: #8a5b45; }
.unit-fan-body--supply  { fill: #2f5f80; }
.unit-fan-body--exhaust { fill: #5e4334; }

/* A loft PIV has no duct to outside - it breathes the roof space, which is ventilated at the
   eaves. Drawn as loose air rather than as pipework, because that is what it is. */
.loft-air-band {
  fill: none; stroke: #7fb6d8; stroke-width: 6;
  stroke-linecap: round; opacity: 0.16; filter: blur(1.3px);
}
.loft-air-trace {
  fill: none; stroke: #cfe7f7; stroke-width: 1.3;
  stroke-linecap: round; stroke-dasharray: 8 30;
}
/* Nothing moves before the button is pressed, same rule as the duct highlights. Every dash cycle
   divides the 152 the ductFlow keyframe travels, so it loops seamlessly. */
.is-running .loft-air { opacity: 0.95; }
.is-running .loft-air-trace { animation: ductFlow 4.6s linear infinite; }

.unit-filter { fill: #dbe2ec; stroke: #8a95a7; stroke-width: 0.7; }
.unit-filter-slot { stroke: #9aa8bf; stroke-width: 0.8; }
.unit-spigot { fill: url(#matSteel); stroke: #6f7b8d; stroke-width: 0.7; }
.unit-spigot-main { fill: #b9c4d6; }
.vent-unit-core { stroke: var(--color-blue); stroke-width: 1.5; fill: none; }
.unit-fan { stroke: var(--color-blue); stroke-width: 1.6; fill: none; stroke-linecap: round; }

/* Terminals, drawn as the three components that are really there: a plenum box screwed to a
   joist in the ceiling void, its collar through the plasterboard, and the valve on the face of
   it. A supply valve and an extract valve are usually the same product - what differs is the
   duct behind them - so they are drawn alike and the arrows carry the distinction. */
.plenum-body {
  fill: url(#matSteel);
  stroke: #6c7788;
  stroke-width: 0.8;
  filter: url(#liftSoft);
}
.plenum-lug { fill: #98a3b4; }
.plenum-collar { fill: #b6c0ce; stroke: #798496; stroke-width: 0.6; }
.valve-flange { fill: #f4f7fb; stroke: #8a94a5; stroke-width: 0.7; }
.valve-stem { stroke: #8a94a5; stroke-width: 0.9; }
/* Wound open, because a valve sitting flush is a valve nobody has commissioned. */
.valve-disc { fill: #fdfefe; stroke: #8a94a5; stroke-width: 0.7; filter: url(#liftSoft); }
.diffuser-cap { fill: #eef2f7; stroke: #8a94a5; stroke-width: 0.7; }

/* The manifold the room ducts fan out of - one for supply, one for extract. */
.manifold-face { fill: #d3dae6; stroke: #9aa5b6; stroke-width: 0.5; }
.spigot-band { fill: #77839a; }
.unit-display { fill: #16233f; stroke: #47566f; stroke-width: 0.5; }
.manifold-body {
  fill: url(#matCasing);
  stroke: #7f8b9d;
  stroke-width: 0.9;
  filter: url(#liftSoft);
}
.manifold-supply .manifold-body { stroke: #59a7d4; }
.manifold-extract .manifold-body { stroke: #c4713f; }

/* The one plumbing connection on an MVHR unit, and it has to run into a trap. */
.condensate-pipe {
  fill: none;
  stroke: #9aa5b6;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The cords a PIV unit is hung from a rafter on. */
.unit-hanger { stroke: #8f9aab; stroke-width: 0.9; stroke-linecap: round; }
.grille-arrow-supply {
  stroke: #56b4f5; stroke-width: 1.5; fill: none; stroke-linecap: round;
}
.grille-arrow-extract {
  stroke: #e8763f; stroke-width: 1.5; fill: none; stroke-linecap: round;
}
.perimeter-arrow-line {
  stroke: #7fc4ff; stroke-width: 2; fill: none; stroke-linecap: round;
  filter: drop-shadow(0 0 3px rgba(127, 196, 255, 0.55));
}

/* Flow particles */

/* Air */

/* Belt and braces: no form control may exceed its container on a narrow screen. */
.site-form input,
.site-form select,
.site-form textarea,
.partf-add-row select,
.partf-add-row input,
.partf-dwelling input { max-width: 100%; }


/* ===================== PHASE 8: MOBILE ===================== */
/* The sticky header would otherwise sit on top of whatever a nav link jumps to. */
main > section,
#home, #services, #mould-assessment, #part-f-tool, #survey, #contact, #portal {
  scroll-margin-top: 76px;
}

@media (max-width: 768px) {
  /* The house diagram scales with its container, so at phone width every label inside it shrinks
     to around 7px. Bumping the SVG-internal type keeps the drawing readable rather than decorative. */
  .room-label { font-size: 13px; }
  .callout-text { font-size: 11.5px; }
  .house-svg { border-radius: var(--radius-sm); }

  /* Give the diagram the full width of the screen — it is the centrepiece and needs the pixels. */
  .mould-diagram-wrap { margin-inline: calc(var(--space-4) * -1); }
  .mould-diagram-wrap .house-svg { border-radius: 0; }
  .diagram-hint { padding-inline: var(--space-4); }

  /* Two ventilation system cards per row rather than one, so step 2 is not three screens tall. */
  .picker-cards-sys { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .picker-cards-sys .sys-tag { font-size: 0.7rem; }

  /* Section rhythm: 96px top and bottom is desktop spacing and reads as dead air on a phone. */
  .section-pad { padding: var(--space-6) var(--space-4); }

  /* The hero trust row sat directly behind the fixed call button. */
  .hero-content { padding-bottom: var(--space-8); }

  /* The phone scrim lives at the END of this file, immediately after the plain `.hero-scrim`
     that used to override it from further down. Do not put one here again. */
  .hero-media img { object-position: center 38%; }

  /* Respect the home-indicator area on iPhones so the call button is not half under it. */
  .sticky-call-btn { padding-bottom: calc(0.85rem + env(safe-area-inset-bottom, 0px)); }
  body { padding-bottom: calc(3.2rem + env(safe-area-inset-bottom, 0px)); }
}

@media (max-width: 420px) {
  /* Long option labels make the Part F row cramped; stack it instead of squeezing. */
  .partf-add-row { flex-direction: column; align-items: stretch; }
  .partf-add-row input[type="number"] { width: 100%; }
  .survey-step-num { position: static; margin-bottom: var(--space-2); display: inline-grid; }
  .survey-step { padding-top: var(--space-4); }
}


/* ===================== PHASE 8: TAP TARGETS & KEYBOARD ===================== */
/* Keyboard users get a way past the navigation without tabbing through every link. */
.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -60px;
  z-index: 200;
  padding: 0.7rem 1.2rem;
  background: var(--color-orange);
  color: var(--color-white);
  font-weight: 700;
  text-decoration: none;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  transition: top var(--dur-fast) var(--ease-out);
}
.skip-link:focus { top: 0; }
#main:focus { outline: none; }

/* WCAG asks for a 44px minimum touch target. These were the ones that missed. */
.nav-toggle {
  min-width: 44px;
  min-height: 44px;
  align-items: center;
}
.sim-toggle { min-height: 44px; }
.check-row {
  min-height: 44px;
  align-items: center;
  padding: var(--space-1) 0;
}
.check-row input {
  width: 20px;
  height: 20px;
  margin-top: 0;
}
.room-remove { width: 36px; height: 36px; }
.ba-thumb { min-height: 40px; }

/* Contact and footer links are the ones people actually tap — give them real area. */
.contact-details a,
.site-footer a {
  display: inline-block;
  padding: 0.25rem 0;
  min-height: 30px;
}
/* Standalone links inside cards are tap targets in their own right, not inline prose. */
.reviews-card a,
.work-empty a,
.tiny-note a,
.sim-result-note a { display: inline-block; padding: 0.3rem 0; min-height: 30px; }


/* ===================== PHASE 8: AREAS, FAQ, ACCREDITATIONS ===================== */
/* The navy band was flat colour behind flat cards. A soft radial lift and translucent cards give
   it depth without adding another image to download. */
.tint-navy {
  background:
    radial-gradient(120% 90% at 15% 0%, rgba(29, 112, 184, 0.35) 0%, rgba(29, 112, 184, 0) 60%),
    linear-gradient(160deg, var(--color-navy-dark) 0%, var(--color-navy) 100%);
  color: var(--color-white);
  border-radius: var(--radius-lg);
}
.tint-navy h2, .tint-navy h3 { color: var(--color-white); }
.tint-navy .section-intro { color: rgba(255, 255, 255, 0.74); }

.areas-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  max-width: 1160px;
  margin: 0 auto;
}
.area-card {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(6px);
  transition: transform var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.area-card:hover,
.area-card:focus-within {
  transform: translateY(-4px);
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(143, 208, 255, 0.5);
}
.area-card h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* A small locator dot, so each area card reads as a place rather than a paragraph */
.area-card h3::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--color-blue);
  box-shadow: 0 0 0 4px rgba(29, 112, 184, 0.28);
  flex: none;
}
.area-card p { margin: 0; color: rgba(255, 255, 255, 0.76); font-size: var(--text-sm); line-height: 1.7; }

/* --- FAQ --- */
.faq-accordion { max-width: 820px; margin: 0 auto; }
.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
  margin-bottom: var(--space-3);
  overflow: hidden;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.faq-item:has(.faq-question[aria-expanded="true"]) {
  border-color: var(--color-blue);
  box-shadow: var(--shadow-soft);
}
.faq-question {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  text-align: left;
  padding: var(--space-4);
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--color-navy-dark);
  min-height: 56px;
}
.faq-question::after {
  content: '';
  flex: none;
  width: 10px; height: 10px;
  border-right: 2px solid var(--color-blue);
  border-bottom: 2px solid var(--color-blue);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-fast) var(--ease-out);
}
.faq-question[aria-expanded="true"]::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq-answer { overflow: hidden; max-height: 0; transition: max-height var(--dur-base) var(--ease-out); }
.faq-answer p { padding: 0 var(--space-4) var(--space-4); margin: 0; color: var(--color-ink-muted); line-height: 1.7; }

/* --- Accreditations --- */
.accred-strip {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-4);
}
.accred-badge {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-white);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--color-navy-dark);
  text-align: left;
  min-height: 44px;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.accred-badge:hover { transform: translateY(-3px); box-shadow: var(--shadow-soft); }
.accred-badge small { display: block; font-weight: 400; color: var(--color-ink-muted); font-size: var(--text-xs); }
.accred-badge-icon { font-size: 1.6rem; line-height: 1; }
.accred-desc-output {
  text-align: center;
  max-width: 620px;
  margin: var(--space-4) auto 0;
  color: var(--color-ink-muted);
  font-size: var(--text-sm);
}


/* ===================== PHASE 9: CREDENTIALS BENTO ===================== */
/* Layered depth over flat colour: a dark ground, translucent cells, and the flame/snowflake
   duality from the logo running warm on one cell and cool on the next. */
.bento-section {
  max-width: 1280px;
  margin: 0 auto;
  padding: var(--space-7) var(--space-4);
}
.bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}
.bento-cell {
  position: relative;
  overflow: hidden;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--color-white);
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.bento-cell:hover {
  transform: translateY(-3px);
  border-color: rgba(143, 208, 255, 0.42);
  background: rgba(255, 255, 255, 0.08);
}
.bento-section {
  background:
    radial-gradient(90% 120% at 8% 0%, rgba(209, 65, 36, 0.18) 0%, rgba(209, 65, 36, 0) 55%),
    radial-gradient(90% 120% at 92% 100%, rgba(29, 112, 184, 0.28) 0%, rgba(29, 112, 184, 0) 55%),
    linear-gradient(155deg, #0d1a3a 0%, var(--color-navy-dark) 55%, #0a1430 100%);
}
.bento-lead { grid-column: span 2; grid-row: span 2; display: flex; flex-direction: column; }
.bento-lead h2 {
  text-align: left;
  color: var(--color-white);
  font-size: clamp(1.6rem, 2.6vw, 2.3rem);
  letter-spacing: -0.015em;
  margin-bottom: var(--space-3);
  text-wrap: balance;
}
.bento-lead p { color: rgba(255, 255, 255, 0.76); line-height: 1.75; }
.bento-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: auto; }
.btn-ghost-dark {
  background: rgba(255, 255, 255, 0.08);
  color: var(--color-white);
  border: 1px solid rgba(255, 255, 255, 0.3);
}
.btn-ghost-dark:hover { background: rgba(255, 255, 255, 0.16); }

.bento-eyebrow {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #9dc2ec;
  margin-bottom: var(--space-3);
}
.bento-stat { display: flex; flex-direction: column; justify-content: flex-end; min-height: 168px; }
.bento-icon { width: 30px; height: 30px; margin-bottom: auto; }
.bento-number {
  font-family: var(--font-heading);
  font-size: clamp(2.4rem, 4vw, 3.2rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
}
.bento-plus { font-size: 0.55em; vertical-align: super; margin-left: 2px; }
.bento-label { font-size: var(--text-sm); color: rgba(255, 255, 255, 0.66); margin-top: var(--space-2); }

/* The flame cell runs warm, the snowflake cell runs cool — straight from the logo. */
.bento-warm { background: linear-gradient(150deg, rgba(209, 65, 36, 0.22), rgba(209, 65, 36, 0.05)); }
.bento-warm .bento-icon, .bento-warm .bento-number { color: #ff9a72; }
.bento-cool { background: linear-gradient(150deg, rgba(86, 180, 245, 0.2), rgba(86, 180, 245, 0.04)); }
.bento-cool .bento-icon, .bento-cool .bento-number { color: #8fd0ff; }

.bento-accred { grid-column: span 2; }
.bento-list { list-style: none; padding: 0; margin: 0 0 var(--space-3); }
.bento-list li { display: flex; flex-direction: column; margin-bottom: var(--space-3); }
.bento-list strong { font-family: var(--font-heading); font-size: var(--text-base); }
.bento-list span { font-size: var(--text-sm); color: rgba(255, 255, 255, 0.62); }
.bento-note { margin: 0; font-size: var(--text-xs); color: rgba(255, 255, 255, 0.5); }

/* Full width closes the grid — spanning two left a hole bottom-right. */
.bento-measure { grid-column: span 4; }
.bento-measure p { margin: 0; color: rgba(255, 255, 255, 0.76); font-size: var(--text-base); line-height: 1.7; max-width: 78ch; }

@media (max-width: 900px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
  .bento-lead, .bento-accred, .bento-measure { grid-column: span 2; grid-row: auto; }
}
@media (max-width: 520px) {
  .bento { grid-template-columns: 1fr; }
  .bento-lead, .bento-accred, .bento-measure { grid-column: span 1; }
  .bento-actions .btn { width: 100%; }
}


/* ===================== PHASE 9: HEADER ===================== */
/* Transparent over the hero photograph, frosted and solid once you scroll past it. The class is
   toggled from app.js on an IntersectionObserver watching the hero. */
.site-header {
  /* Fixed, not sticky. A sticky header sits in normal flow ABOVE the hero, so a transparent
     one showed the page background and rendered white type on a white band. Fixed lets it
     float over the hero photograph, which is the whole point of the transparent state. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  box-shadow: none;
  border-bottom: 1px solid transparent;
  transition: background var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.site-header.is-scrolled {
  background: rgba(13, 26, 58, 0.82);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom-color: rgba(255, 255, 255, 0.12);
  box-shadow: 0 8px 30px rgba(5, 10, 25, 0.32);
}
/* Without a hero to sit over (a future inner page), the solid state is the default. */
.site-header.no-hero { background: rgba(13, 26, 58, 0.94); }

.header-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0.7rem var(--space-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.brand { display: flex; align-items: center; gap: 0.65rem; text-decoration: none; }
.brand-mark { width: 46px; height: 46px; flex: none; }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name {
  color: var(--color-white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}
.brand-tag {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.nav-call {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.9rem;
  border-radius: 999px;
  background: var(--color-orange);
  color: var(--color-white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.86rem;
  text-decoration: none;
  white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.nav-call::before { content: '\260E'; font-size: 0.9em; }
.nav-call:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(209, 65, 36, 0.42); }

@media (max-width: 768px) {
  /* The floating nav panel needs its own solid ground — it is not over the hero. */
  .main-nav { background: rgba(13, 26, 58, 0.97); backdrop-filter: blur(14px); }
  .nav-call { width: 100%; justify-content: center; margin-top: var(--space-2); }
  .brand-tag { display: none; }
  .brand-mark { width: 40px; height: 40px; }
}


/* ===================== PHASE 9: FOOTER ===================== */
.site-footer {
  background:
    radial-gradient(80% 120% at 12% 0%, rgba(209, 65, 36, 0.14) 0%, rgba(209, 65, 36, 0) 55%),
    linear-gradient(160deg, var(--color-navy-dark) 0%, #0a1430 100%);
  color: rgba(255, 255, 255, 0.78);
  padding: var(--space-8) var(--space-4) var(--space-5);
}
.footer-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--space-6);
}
.footer-inner h3 {
  color: var(--color-white);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: var(--space-3);
}

.footer-inner p { margin: 0 0 var(--space-2); font-size: var(--text-sm); line-height: 1.7; }
.site-footer a { color: #9dc2ec; text-decoration: none; }
.site-footer a:hover { color: var(--color-white); text-decoration: underline; }
.footer-links { list-style: none; padding: 0; margin: 0; }
.footer-links li { margin-bottom: var(--space-2); font-size: var(--text-sm); }
.footer-copy {
  max-width: 1280px;
  margin: var(--space-6) auto 0;
  padding-top: var(--space-4);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
}
@media (max-width: 860px) {
  .footer-inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .footer-copy { justify-content: flex-start; }
}
.footer-brand p { color: rgba(255, 255, 255, 0.7); max-width: 46ch; }
.footer-address { color: rgba(255, 255, 255, 0.52) !important; font-size: var(--text-xs) !important; }
.footer-hours-head { margin-top: var(--space-5); }
.footer-hours { color: rgba(255, 255, 255, 0.6); font-size: var(--text-sm); }
.footer-links li { color: rgba(255, 255, 255, 0.7); }
.footer-brand-lockup {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: var(--space-4);
}
.footer-brand-lockup img { width: 50px; height: 50px; flex: none; }
.footer-brand-lockup span { display: flex; flex-direction: column; line-height: 1.2; }
.footer-brand-lockup strong {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  color: var(--color-white);
}
.footer-brand-lockup em {
  font-style: normal;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}


/* ===================== PHASE 10: NAVIGATION ===================== */
/* Underlined text tabs read as a 2010 template. This is a single floating pill group: the links
   sit inside one translucent capsule, and the active item gets a solid pill rather than a rule
   under the words. */
.main-nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.nav-link {
  position: relative;
  padding: 0.5rem 0.85rem;
  border-radius: 999px;
  border: none;
  color: rgba(255, 255, 255, 0.74);
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.855rem;
  letter-spacing: -0.005em;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.nav-link:hover { color: var(--color-white); background: rgba(255, 255, 255, 0.1); }
.nav-link.active {
  color: var(--color-navy-dark);
  background: var(--color-white);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.nav-call {
  margin-left: var(--space-1);
  padding: 0.5rem 1rem;
  font-family: var(--font-body);
  font-size: 0.855rem;
  letter-spacing: -0.005em;
}
/* WhatsApp glyph rather than a telephone receiver — this no longer starts a call. */
.nav-call::before { content: none; }
.nav-call svg { width: 15px; height: 15px; }

@media (max-width: 1100px) {
  .nav-link { padding: 0.5rem 0.6rem; font-size: 0.8rem; }
}
@media (max-width: 768px) {
  .main-nav {
    position: absolute;
    top: calc(100% + 8px);
    left: var(--space-3);
    right: var(--space-3);
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: var(--space-2);
    border-radius: var(--radius-lg);
    background: rgba(13, 26, 58, 0.97);
    border-color: rgba(255, 255, 255, 0.14);
    box-shadow: 0 18px 44px rgba(5, 10, 25, 0.5);
    display: none;
  }
  .main-nav.open { display: flex; }
  .nav-link { padding: 0.75rem 0.9rem; border-radius: var(--radius-sm); font-size: 0.95rem; }
  .nav-link.active { background: rgba(255, 255, 255, 0.14); color: var(--color-white); box-shadow: none; }
  .nav-call { margin: var(--space-2) 0 0; border-radius: var(--radius-sm); }
}

/* A fixed header overlays whatever is first on the page. The hero is designed for that; any
   other first section would slide underneath it, so give it clearance. */
main > section:first-child:not(.hero) { padding-top: calc(var(--space-8) + 76px); }
/* Clearance for the fixed header, so the eyebrow never tucks under the navigation. */
.hero-content { padding-top: calc(var(--space-7) + 76px); }
@media (max-width: 768px) { .hero-content { padding-top: calc(var(--space-6) + 72px); } }



/* ===================== PHASE 11: CLIENT PORTAL / APP SECTION ===================== */
/* Tokens lifted from the engineer app's own theme so the two products read as one company.
   These are the app's values, not approximations: darkSlate #0D1826, safetyOrange #D5393C,
   iceBlue #1878AB, info #3DA9DE, success #22C55E, warning #F59E0B. */
:root {
  --app-slate: #0D1826;
  --app-slate-light: #142235;
  --app-orange: #D5393C;
  --app-ice: #1878AB;
  --app-info: #3DA9DE;
  --app-ok: #22C55E;
  --app-warn: #F59E0B;
  --app-text: #F1F5F9;
  --app-text-dim: #94A3B8;
  --app-border: rgba(255, 255, 255, 0.12);
}

.portal-section {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background:
    radial-gradient(80% 120% at 85% 10%, rgba(24, 120, 171, 0.20) 0%, rgba(24, 120, 171, 0) 55%),
    radial-gradient(70% 110% at 10% 90%, rgba(213, 57, 60, 0.14) 0%, rgba(213, 57, 60, 0) 55%),
    linear-gradient(165deg, var(--app-slate) 0%, #0a1220 100%);
  color: var(--app-text);
  border-radius: var(--radius-lg);
  padding: var(--space-8) var(--space-4);
}

/* --- Airflow overlay --- */
.portal-airfield {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.34;
  filter: blur(0.6px);
}
.airline {
  /* A long visible segment with a long gap: a passing current, not a dash. */
  stroke-dasharray: 460 520;
  animation: airDrift 22s linear infinite;
}
.airline.a2 { animation-duration: 27s; animation-delay: -5s; }
.airline.a3 { animation-duration: 33s; animation-delay: -11s; }
.airline.a4 { animation-duration: 25s; animation-delay: -17s; }
.airline.a5 { animation-duration: 38s; animation-delay: -8s; }
@keyframes airDrift {
  from { stroke-dashoffset: 980; }
  to   { stroke-dashoffset: 0; }
}
.mote { animation: moteRise 19s ease-in-out infinite; }
.mote.m2 { animation-duration: 24s; animation-delay: -6s; }
.mote.m3 { animation-duration: 28s; animation-delay: -12s; }
.mote.m4 { animation-duration: 21s; animation-delay: -3s; }
@keyframes moteRise {
  0%   { transform: translateY(0) scale(1);    opacity: 0; }
  18%  { opacity: 1; }
  80%  { opacity: 0.5; }
  100% { transform: translateY(-120px) scale(1.35); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .airline, .mote { animation: none; }
  .airline { stroke-dashoffset: 0; stroke-dasharray: none; }
}

.portal-inner {
  position: relative;
  z-index: 1;
  max-width: 1180px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: var(--space-7);
  align-items: center;
}
.portal-eyebrow {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--app-info);
  margin-bottom: var(--space-3);
}
.portal-copy h2 {
  text-align: left;
  color: var(--app-text);
  font-size: clamp(1.8rem, 3vw, 2.6rem);
  letter-spacing: -0.02em;
  margin-bottom: var(--space-3);
}
.portal-copy > p { color: var(--app-text-dim); line-height: 1.75; margin-bottom: var(--space-4); }
.portal-features { list-style: none; padding: 0; margin: 0 0 var(--space-5); }
.portal-features li {
  position: relative;
  padding-left: 1.7rem;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  color: rgba(241, 245, 249, 0.86);
}
.portal-features li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.55em;
  width: 9px; height: 9px;
  border-radius: 50%;
  /* The app's duotone, as a bullet */
  background: linear-gradient(135deg, var(--app-orange), var(--app-ice));
}
.btn-portal {
  background: linear-gradient(135deg, var(--app-orange), #b8302f);
  color: #fff;
  box-shadow: 0 8px 24px rgba(213, 57, 60, 0.34);
}
.btn-portal:hover { box-shadow: 0 12px 30px rgba(213, 57, 60, 0.44); }
.portal-note { margin: var(--space-3) 0 0; font-size: var(--text-xs); color: var(--app-text-dim); }

/* --- Illustrative app panel --- */
.app-panel {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius-xl);
  background: rgba(20, 34, 53, 0.72);
  border: 1px solid var(--app-border);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 26px 60px rgba(0, 0, 0, 0.42);
}
.app-panel-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-text-dim);
  padding: 0 var(--space-1) var(--space-1);
}
.app-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--app-ok);
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
}
.app-card {
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--app-border);
}
.app-card-lead { display: flex; align-items: center; gap: var(--space-4); }
.app-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.app-card-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.app-label {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--app-text-dim);
  margin-bottom: 4px;
}
.app-value {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  color: var(--app-text);
  letter-spacing: -0.01em;
}
.app-value small { font-size: 0.62em; opacity: 0.8; margin-left: 1px; }
.app-value-warn { color: var(--app-warn); font-size: var(--text-2xl); }
.app-value-ok { color: var(--app-ok); font-size: var(--text-2xl); }
.app-sub { display: block; font-size: var(--text-xs); color: var(--app-text-dim); margin-top: 2px; }

/* The app's signature: a gradient-stroke gauge ring. */
.app-gauge { position: relative; width: 92px; height: 92px; flex: none; }
.app-gauge svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.gauge-track { fill: none; stroke: rgba(255, 255, 255, 0.1); stroke-width: 9; }
.gauge-arc {
  fill: none;
  stroke-width: 9;
  stroke-linecap: round;
  stroke-dasharray: 301.6;            /* 2 * pi * 48 */
  stroke-dashoffset: 96.5;            /* 68% filled */
  animation: gaugeFill 1.6s var(--ease-out) both;
}
@keyframes gaugeFill { from { stroke-dashoffset: 301.6; } }
.app-gauge-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--app-text);
}
.app-gauge-value small { font-size: 0.6em; opacity: 0.75; }

.app-spark { width: 100%; height: 28px; margin-top: var(--space-2); overflow: visible; }
.app-spark polyline {
  fill: none;
  stroke: var(--app-warn);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 160;
  animation: sparkDraw 1.8s var(--ease-out) both;
}
@keyframes sparkDraw { from { stroke-dashoffset: 160; } to { stroke-dashoffset: 0; } }

.app-pill {
  padding: 0.35rem 0.75rem;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 700;
  background: rgba(34, 197, 94, 0.16);
  color: #6ee7a0;
  border: 1px solid rgba(34, 197, 94, 0.3);
}
.app-pill-alt {
  background: rgba(61, 169, 222, 0.16);
  color: #8ed0f2;
  border-color: rgba(61, 169, 222, 0.32);
}

@media (max-width: 900px) {
  .portal-inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .portal-copy h2 { text-align: left; }
}
@media (max-width: 520px) {
  .app-grid { grid-template-columns: 1fr; }
  .app-card-lead { flex-direction: column; align-items: flex-start; }
}


/* ===================== PHASE 11: SIMULATOR AIRFLOW ===================== */
/* A travelling highlight over each duct. The duct itself stays a solid pipe; this rides on top of
   it as a bright dash, so the run reads as air moving through it rather than as a coloured line
   with a dot on it. Supply is cool and bright, extract is warm and murkier — fresh air in, stale
   air out, readable at a glance. */
/* Three layers on the same path at three speeds — see addDuct(). The separation as they travel
   is what makes it read as air with volume rather than a dashed line. */
.duct-flow { fill: none; stroke-linecap: round; pointer-events: none; }
/* Slower and fainter than they were. Air in a 125mm duct at 30 l/s moves at about 2.4 m/s, and
   the eye should read "moving" without being able to count the dashes going past. The old timings
   were roughly twice this speed at nearly twice the opacity, which made a quiet diagram look like
   a fairground. */
/* NOTHING here may set an opacity outside .is-running.
   These three layers are built with opacity="0" as a presentation attribute and revealed by
   revealFlow() once the run starts - but a CSS `opacity` declaration BEATS a presentation
   attribute, so every one of these rules was quietly cancelling that and the highlights were
   sliding along the ducts of a house nobody had switched on. The animation is behind the same
   gate for the same reason. */
/* Crisp rather than glowing. The wide layer used to carry a 1.1px blur, which at this scale is
   a quarter of the duct's width - so the highlight bled through the casing and the run looked
   soft-focus while everything around it stayed sharp. No blur, tighter dashes, lower opacity: a
   clean pulse travelling inside a clean pipe.
   Every dash cycle divides 152, which is what one turn of the ductFlow keyframe travels. */
.duct-flow--halo { stroke-width: 4.6; stroke-dasharray: 22 54; }
.duct-flow--body { stroke-width: 2; stroke-dasharray: 14 62; }
.duct-flow--core { stroke-width: 0.8; stroke-dasharray: 6 70; }

/* Weighted the other way round now that the colour is the duct's own: the wide soft layer can
   carry more, because it no longer bleaches the pipe it is inside. */
.is-running .duct-flow--halo { opacity: 0.16; animation: ductFlow 6.6s linear infinite; }
.is-running .duct-flow--body { opacity: 0.3; animation: ductFlow 4.9s linear infinite; }
.is-running .duct-flow--core { opacity: 0.62; animation: ductFlow 3.5s linear infinite; }
/* The colour of the run it is travelling down, not white. A near-white streak on a blue pipe
   and the same near-white streak on an orange one read as the same light in both, and the whole
   point of two colours is that you can tell fresh from stale at a glance. These are a deeper,
   more saturated version of each duct's own colour - so the highlight looks like faster-moving
   air inside that duct rather than a lamp being dragged along it. */
.duct-flow.intake {
  stroke: #4d9cc9;
}
.duct-flow.supply {
  stroke: #5fb4e8;
}
.duct-flow.exhaust {
  stroke: #a9673f;
}
/* Extract paths are built room -> unit and unit -> outside, i.e. already in the direction the air
   travels, so they use the same forward keyframes. An earlier reverse animation ran the highlight
   against the arrows on the very same duct. */
.duct-flow.extract {
  stroke: #d97a45;
}
@keyframes ductFlow { to { stroke-dashoffset: -152; } }

/* The outside runs are the trunk, so their flow is heavier. A general sibling combinator, because
   the duct's specular line is appended between the duct and its flow highlight. */
.duct-flow--halo.trunk { stroke-width: 8; }
.duct-flow--body.trunk { stroke-width: 5.2; }
.duct-flow--core.trunk { stroke-width: 1.6; }

/* No glow on any of it. A drop-shadow on a moving dash is the one thing that reads as lighting
   rather than as air, and with the highlight now carrying the duct's own colour it does not need
   one to be seen. */

/* The arrows say which way, so they only have to be legible - not bright. */
.flow-particle { opacity: 0; }
.is-running .flow-particle { opacity: 0.55; }
.flow-particle-intake  { fill: #9ccbe4; }
.flow-particle-supply  { fill: #c4e4fa; }
.flow-particle-extract { fill: #f0bd9c; }
.flow-particle-exhaust { fill: #d2b3a2; }

/* Terminal arrows pulse gently once running, so the eye is drawn to where air enters and leaves.
   Gated behind .is-running: an unconditional opacity animation overrides the opacity="0" the
   arrows are built with, which put them on screen before anything had been simulated. */
.is-running .grille-arrows { animation: terminalPulse 2.4s var(--ease-in-out) infinite; }
@keyframes terminalPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

/* Stale air needs to be seen before it can be seen to go. */
.stale-air { pointer-events: none; mix-blend-mode: multiply; }

@media (prefers-reduced-motion: reduce) {
  .duct-flow, .grille-arrows { animation: none; }
}


/* ===================== SCROLL REVEALS ===================== */
/* Only applied once JS has added .reveal, so with JS off or broken nothing is ever hidden.
   The old system hid content with GSAP and depended on ScrollTrigger to bring it back, which
   left whole sections — including the simulator — invisible when a trigger did not fire. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--ease-out) var(--reveal-delay, 0ms),
              transform 0.6s var(--ease-out) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.reveal.is-revealed {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}


/* ===================== PHASE 12: HERO AIRFLOW ===================== */
/* Sits above the scrim but below the copy, so it reads as air moving through the ductwork in the
   photograph rather than as a graphic laid over the headline. */
.hero-airfield {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
  opacity: 0.62;
}
/* Paired halo and core, so the air over the photograph has body rather than being four hairlines
   ruled across it. The halo is displaced by turbulence and drifts slower than the core it wraps. */
/* Air drifting over a photograph of real ductwork. It has to be felt rather than looked at: the
   moment any one strand is long enough and bright enough to follow from one side to the other, it
   stops being air and becomes a line somebody drew on the picture. Broken streaks, low opacity,
   three depths, speeds that never sync. Every dash cycle divides 960 so one keyframe loops them
   all seamlessly. */
.heroline { animation: heroAirDrift 17s linear infinite; }
.heroline--halo { stroke-width: 15; opacity: 0.14; stroke-dasharray: 100 220; filter: url(#heroWisp); }
.heroline--mid  { stroke-width: 4.5; opacity: 0.2; stroke-dasharray: 26 134; }
.heroline--core { stroke-width: 1.2; opacity: 0.42; stroke-dasharray: 8 72; }
.heroline--halo.h1 { animation-duration: 26s; }
.heroline--mid.h1  { animation-duration: 21s; animation-delay: -3s; }
.heroline--core.h1 { animation-duration: 17s; }
.heroline--halo.h2 { animation-duration: 34s; animation-delay: -6s; }
.heroline--mid.h2  { animation-duration: 28s; animation-delay: -11s; }
.heroline--core.h2 { animation-duration: 23s; animation-delay: -4s; }
.heroline--halo.h3 { animation-duration: 43s; animation-delay: -13s; }
.heroline--mid.h3  { animation-duration: 36s; animation-delay: -5s; }
.heroline--core.h3 { animation-duration: 29s; animation-delay: -9s; }
.heroline--halo.h4 { animation-duration: 52s; animation-delay: -20s; }
.heroline--mid.h4  { animation-duration: 44s; animation-delay: -17s; }
.heroline--core.h4 { animation-duration: 35s; animation-delay: -14s; }
@keyframes heroAirDrift {
  from { stroke-dashoffset: 960; }
  to   { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .heroline { animation: none; opacity: 0.18; }
}
/* On a phone the whole width is the copy column, so the lines would cross the headline. */
@media (max-width: 768px) {
  .hero-airfield { opacity: 0.3; }
}


/* ===================== PHASE 12: MOBILE HEADER ===================== */
/* The bar was a bare hamburger against a transparent header, which read as unfinished. It now has
   a defined surface of its own, a proper icon that becomes a close cross, and enough room to hit. */
@media (max-width: 768px) {
  /* Solid, always. See the note above: a translucent blurred bar loses the hamburger against a
     pale section, and iOS Safari will not repaint the blur on a sticky element until you scroll. */
  .site-header,
  .site-header.is-scrolled {
    background: var(--color-navy-dark);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .header-inner { padding: 0.55rem var(--space-3); }

  .nav-toggle {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    width: 46px;
    height: 46px;
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.18);
    transition: background var(--dur-fast) var(--ease-out);
  }
  .nav-toggle:hover { background: rgba(255, 255, 255, 0.16); }
  .nav-toggle span {
    display: block;
    width: 20px;
    height: 2px;
    border-radius: 2px;
    background: var(--color-white);
    transition: transform var(--dur-fast) var(--ease-out),
                opacity var(--dur-micro) var(--ease-out);
  }
  /* Open state: the bars fold into a cross, so the control says how to close itself. */
  .nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* The channels at the foot of the mobile menu. Marks, not a button: you recognise WhatsApp by
   its shape faster than you read the word, and a row of them says "here is every way to reach us"
   in the space one button was taking. Each keeps its own colour where the brand permits it -
   WhatsApp green, Facebook blue, the Google G in its four - because a monochrome row is a row of
   grey squares and the whole point is that they are noticed. */
.nav-socials { display: none; }
@media (max-width: 768px) {
  .nav-call { display: none; }
  .nav-socials {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid rgba(255, 255, 255, 0.14);
  }
  .nav-social {
    flex: 1 1 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45em;
    min-height: 44px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid rgba(255, 255, 255, 0.14);
    color: var(--color-white);
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
  }
  .nav-social .ic { width: 1.2em; height: 1.2em; }
}

/* The open menu must be a solid surface. It sat at 97% over a backdrop-filtered header, which
   let the hero headline ghost through the links and made them hard to read. */
@media (max-width: 768px) {
  .main-nav {
    background: #0f1d38;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: rgba(255, 255, 255, 0.16);
  }
}


/* ===================== PHASE 12: PART F DISCLOSURE + MOBILE DENSITY ===================== */
.partf-collapse { max-width: 900px; margin: 0 auto; }
.partf-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--color-blue);
  border-radius: var(--radius-md);
  background: var(--color-surface-tint-blue);
  cursor: pointer;
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-navy-dark);
  list-style: none;
  min-height: 56px;
  transition: background var(--dur-fast) var(--ease-out);
}
.partf-summary::-webkit-details-marker { display: none; }
.partf-summary:hover { background: #dfeafa; }
.partf-summary-hint {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
}
.partf-summary::after {
  content: '';
  flex: none;
  width: 10px; height: 10px;
  border-right: 2px solid var(--color-blue);
  border-bottom: 2px solid var(--color-blue);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-fast) var(--ease-out);
}
.partf-collapse[open] .partf-summary { margin-bottom: var(--space-4); }
.partf-collapse[open] .partf-summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
/* Open on a desktop, where the room exists — the summary is only a control on small screens. */
@media (min-width: 769px) {
  .partf-summary { display: none; }
}

/* --- Mobile density --- */
/* The page ran to twenty-four screens on a phone. None of this removes content; it tightens the
   padding that reads as generous on a desktop and as dead scrolling on a handset. */
@media (max-width: 768px) {
  .service-card { padding: var(--space-4); }
  .service-card p { font-size: 0.86rem; line-height: 1.6; }
  .service-icon { width: 32px; height: 32px; margin-bottom: var(--space-2); }
  .area-card { padding: var(--space-4); }
  .area-card p { font-size: 0.86rem; line-height: 1.6; }
  .survey-step { padding: var(--space-4); }
  .survey-step p { font-size: 0.86rem; line-height: 1.6; }
  .bento-cell { padding: var(--space-4); }
  .feature-band-inner { padding: var(--space-6) var(--space-4); }
  .feature-copy p { font-size: 0.9rem; line-height: 1.65; }
  .portal-section { padding: var(--space-6) var(--space-4); }
  .app-card { padding: var(--space-3); }
  .section-intro { margin-bottom: var(--space-4); }
}


/* ===================== PHASE 13: MULTI-PAGE ===================== */
/* Inner pages have no hero, so they need their own heading block that clears the fixed header. */
.page-head {
  background:
    radial-gradient(90% 130% at 12% 0%, rgba(29, 112, 184, 0.16) 0%, rgba(29, 112, 184, 0) 60%),
    linear-gradient(160deg, var(--color-navy-dark) 0%, var(--color-navy) 100%);
  color: var(--color-white);
  padding: calc(var(--space-8) + 76px) var(--space-4) var(--space-7);
}
.page-head-inner { max-width: 820px; margin: 0 auto; }
.page-eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #9dc2ec;
  margin-bottom: var(--space-3);
}
.page-head h1 {
  color: var(--color-white);
  font-size: clamp(2rem, 5vw, 3.2rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-3);
  text-wrap: balance;
}
.page-lead {
  font-size: clamp(var(--text-base), 1.4vw, var(--text-lg));
  color: rgba(255, 255, 255, 0.8);
  margin: 0;
  max-width: 62ch;
}

/* --- Closing call to action, shared by every inner page --- */
.cta-band {
  background: var(--color-surface-tint-blue);
  border-radius: var(--radius-lg);
  padding: var(--space-8) var(--space-4);
  margin: 0 auto;
  max-width: 1280px;
}
.cta-band-inner { max-width: 620px; margin: 0 auto; text-align: center; }
.cta-band h2 { margin-bottom: var(--space-3); }
.cta-band p { color: var(--color-ink-muted); margin-bottom: var(--space-5); }
.cta-band-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: center; }
.cta-band .btn-ghost {
  background: var(--color-white);
  color: var(--color-navy-dark);
  border: 1px solid var(--color-border);
  backdrop-filter: none;
}
.cta-band .btn-ghost:hover { background: #f4f7fc; }

/* --- Home: tool teasers, so the tools live on their own pages --- */
.tools-teaser { background: var(--color-white); border-radius: var(--radius-lg); }
.tool-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--space-4);
  max-width: 940px;
  margin: 0 auto;
}
.tool-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-white);
  text-decoration: none;
  color: inherit;
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.tool-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-soft);
  border-color: var(--color-blue);
}
.tool-tag {
  align-self: flex-start;
  padding: 0.2rem 0.65rem;
  margin-bottom: var(--space-3);
  border-radius: 999px;
  background: var(--color-surface-tint-blue);
  color: var(--color-navy-dark);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.tool-card h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.tool-card p { color: var(--color-ink-muted); font-size: var(--text-sm); line-height: 1.65; margin-bottom: var(--space-4); }
.tool-go { margin-top: auto; font-weight: 700; color: var(--color-blue); font-size: var(--text-sm); }
.tool-card:hover .tool-go span { margin-left: 4px; }

/* --- Home: work teaser --- */
.work-teaser {
  background: linear-gradient(160deg, var(--color-navy-dark) 0%, var(--color-navy) 100%);
  border-radius: var(--radius-lg);
  color: var(--color-white);
}
.work-teaser-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-7);
  align-items: center;
  padding: var(--space-8) var(--space-4);
}
.work-teaser-media { margin: 0; }
.work-teaser-media img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 52px rgba(0, 0, 0, 0.36);
}
.work-teaser-copy h2 { text-align: left; color: var(--color-white); margin-bottom: var(--space-3); }
.work-teaser-copy p { color: rgba(255, 255, 255, 0.78); margin-bottom: var(--space-5); }
.work-teaser .cta-band-actions { justify-content: flex-start; }

@media (max-width: 900px) {
  .work-teaser-inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .work-teaser-copy h2 { text-align: left; }
}
@media (max-width: 768px) {
  .page-head { padding: calc(var(--space-6) + 72px) var(--space-4) var(--space-6); }
  .cta-band { padding: var(--space-6) var(--space-4); }
  .cta-band-actions .btn { width: 100%; }
  .work-teaser-inner { padding: var(--space-6) var(--space-4); }
}


/* ===================== PHASE 14: HERO ENTRANCE (CSS, not JS) ===================== */
/* Declared here so the browser owns it. Two rules matter for reliability:
   - Nothing is hidden unless the animation is actually running, so a slow or blocked GSAP can no
     longer leave the headline invisible, which is what made the hero look empty on a phone.
   - No stagger delays. With fill-mode both, an element sits in its 'from' state for the whole
     delay, which is an invisible element for as long as the delay lasts. One short pass for
     everything is less of a flourish and far less to go wrong. */
@media (prefers-reduced-motion: no-preference) {
  .hero-content.hero-in .hero-eyebrow,
  .hero-content.hero-in h1,
  .hero-content.hero-in .hero-subhead,
  .hero-content.hero-in .hero-ctas,
  .hero-content.hero-in .hero-trust {
    animation: heroIn 0.5s var(--ease-out) both;
  }
}
@keyframes heroIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}


/* ===================== PHASE 15: DUCT AS A ROUND PIPE ===================== */
/* A stroke is flat. A second, thinner, lighter stroke along the same path reads as the specular
   top of a cylinder — the cheapest way to turn a line into a pipe. */
.duct-spec { fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.4; opacity: 0.8; }
.duct-spec.intake  { stroke: #7fb8d6; }
.duct-spec.supply  { stroke: #a8d8f5; }
.duct-spec.extract { stroke: #f3b48c; }
.duct-spec.exhaust { stroke: #c9a390; }
#duct-outside-intake + .duct-spec,
#duct-outside-exhaust + .duct-spec { stroke-width: 1.8; }


/* ===================== PHASE 16: DAMP QUESTIONNAIRE ===================== */
/* Replaces the humidity slider. Answerable questions, big tap targets, and the derived figure
   labelled as an estimate rather than a measurement. */
.damp-quiz {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin: 0 0 var(--space-4);
  background: var(--color-white);
}
.damp-quiz-legend {
  padding: 0 var(--space-2);
  margin-left: calc(var(--space-2) * -1);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--color-navy-dark);
}
.damp-q + .damp-q { margin-top: var(--space-4); }
.damp-q-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink);
  margin-bottom: var(--space-2);
}
.damp-opts { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.damp-opt {
  flex: 1 1 auto;
  min-width: 84px;
  min-height: 44px;
  padding: 0.5rem 0.8rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-white);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-muted);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease-out);
}
.damp-opt:hover { border-color: var(--color-blue); color: var(--color-navy-dark); }
.damp-opt.is-on {
  border-color: var(--color-blue);
  background: var(--color-surface-tint-blue);
  color: var(--color-navy-dark);
  box-shadow: inset 0 0 0 1px var(--color-blue);
}
.hum-derived {
  margin: calc(var(--space-2) * -1) 0 var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  line-height: 1.55;
}
.hum-exact { margin: 0 0 var(--space-3); }
.hum-exact summary {
  cursor: pointer;
  font-size: var(--text-xs);
  color: var(--color-blue);
  font-weight: 600;
  padding: var(--space-2) 0;
  min-height: 32px;
}
.hum-exact #humiditySlider { width: 100%; margin-top: var(--space-2); accent-color: var(--color-blue); }


/* ===================== PHASE 17: ANNOTATED PHOTO PLATES =====================
   A real site photograph carries far more weight than a drawing, but a photograph shot
   looking up into a ceiling void has no horizon in it, so a reader can just as easily
   read it upside down. Naming the parts removes the ambiguity and, done with restraint,
   reads the way a manufacturer's technical plate reads.

   Two presentations of the same data: leader lines with labels where there is width for
   them, numbered markers plus a legend underneath where there is not. */
.photo-plate { margin: 0; }
.plate-frame {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 20px 52px rgba(11, 18, 32, 0.24);
  background: var(--color-ink);
}
/* Overrides the fixed 4:3 crop the feature bands apply, so the annotation percentages
   land on the pixels they were measured against rather than on a cropped frame. */
.photo-plate .plate-frame img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: auto;
  object-fit: fill;
  border-radius: 0;
  box-shadow: none;
}
.plate-layer { position: absolute; inset: 0; pointer-events: none; }

.an {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: flex;
  align-items: center;
  white-space: nowrap;
  /* Never hidden by default. Only a plate that JS has armed starts invisible, so a script
     failure leaves the annotations on screen rather than blanking them. */
  transition: opacity 620ms var(--ease-out, cubic-bezier(.22,.61,.36,1)),
              transform 620ms var(--ease-out, cubic-bezier(.22,.61,.36,1));
}
.an--right { transform: translate(0, -50%); }
.an--left  { transform: translate(-100%, -50%); flex-direction: row-reverse; }
.plate-armed .an { opacity: 0; }
.plate-armed .an--right { transform: translate(-6px, -50%); }
.plate-armed .an--left  { transform: translate(calc(-100% + 6px), -50%); }
.plate-on.plate-armed .an { opacity: 1; }
.plate-on.plate-armed .an--right { transform: translate(0, -50%); }
.plate-on.plate-armed .an--left  { transform: translate(-100%, -50%); }
.plate-on .an:nth-child(2) { transition-delay: 110ms; }
.plate-on .an:nth-child(3) { transition-delay: 220ms; }

.an-dot {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-orange);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.92), 0 2px 10px rgba(11, 18, 32, 0.45);
  font-style: normal;
  font-family: var(--font-heading);
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--color-white);
  display: grid;
  place-items: center;
}
.an-dot::after { content: attr(data-n); }

.an-t {
  position: relative;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  color: var(--color-white);
  background: rgba(11, 18, 32, 0.74);
  border: 1px solid rgba(255, 255, 255, 0.16);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
/* The leader line. Hairline, and the same white as the dot's ring so the two read as one mark. */
.an-t::before {
  content: '';
  position: absolute;
  top: 50%;
  width: 28px;
  height: 1px;
  background: rgba(255, 255, 255, 0.8);
}
.an--right .an-t { margin-left: 28px; }
.an--right .an-t::before { right: 100%; }
.an--left  .an-t { margin-right: 28px; }
.an--left  .an-t::before { left: 100%; }

.plate-caption {
  margin-top: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  line-height: 1.6;
}
.plate-cap-lead { display: block; }
.plate-legend { display: none; }

/* Below this width a label chip would run off the frame or sit on top of its neighbour,
   so the markers keep their positions and the words move underneath the photograph. */
@media (max-width: 640px) {
  .an-t { display: none; }
  /* A marker anchored near an edge would otherwise sit half over the frame's rounded corner. */
  .an { left: clamp(18px, var(--x), calc(100% - 18px)); }
  .an--left, .an--right,
  .plate-armed .an--left, .plate-armed .an--right,
  .plate-on.plate-armed .an--left, .plate-on.plate-armed .an--right { transform: translate(-50%, -50%); }
  .plate-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-3);
    list-style: none;
    margin: var(--space-2) 0 0;
    padding: 0;
  }
  .plate-legend li { display: flex; align-items: center; gap: 6px; }
  .plate-legend b {
    width: 17px;
    height: 17px;
    flex: none;
    border-radius: 50%;
    background: var(--color-orange);
    color: var(--color-white);
    font-family: var(--font-heading);
    font-size: 0.63rem;
    display: grid;
    place-items: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .an { transition: none; }
}

/* The spam trap on both forms. Off-screen rather than display:none — a bot that skips hidden
   inputs would otherwise skip this one too, which is the whole point of it. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The finished ceiling line under the void band, and the boxing round the vertical riser.
   Real vertical bundles are boxed in; without the boxing several parallel lanes read as loose
   pipes crossing the wall. */
.ceiling-line { stroke: #8d97a8; stroke-width: 0.8; }
/* Boxing. A real riser is cased in plasterboard, taped and skimmed, and the only thing a customer
   ever sees of it is a square column in the corner. Drawn solid with a lit arris rather than as a
   dashed outline: a dashed rectangle reads as an annotation, and the point of this one is that it
   is a physical thing in the room. */
.riser-body {
  fill: #cfd6e2;
  stroke: #a8b2c2;
  stroke-width: 0.9;
}
.riser-bead {
  stroke: #eef1f6;
  stroke-width: 1.6;
  opacity: 0.95;
  stroke-linecap: round;
}


/* ===================== PHASE 18: TAP TARGETS =====================
   Footer and inline links were rendering 30-35px tall on a phone. Anything a thumb has to hit
   wants about 44px, and in a footer the padding costs nothing because the links are stacked
   anyway. Applied only below the tablet breakpoint so the desktop footer stays compact. */
@media (max-width: 900px) {
  .site-footer a,
  .areas-section .btn-text,
  .contact-details a,
  .reviews-card a,
  .work-empty a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .footer-legal a { min-height: 44px; padding-block: var(--space-1); }

  /* The loft answer and the hygrometer disclosure are real controls in the middle of the
     simulator's flow, not incidental links — they get the full target. */
  .loft-toggle-btn { min-height: 44px; padding-inline: var(--space-4); }
  .hum-exact summary { min-height: 44px; display: flex; align-items: center; }
}


/* ===================== PHASE 19: LEGAL PAGES =====================
   A privacy policy is read by someone looking for one specific answer, not read through. So it is
   set at a comfortable measure, the headings are strong enough to scan against, and the summary
   at the top answers the three questions most people actually arrive with. */
.legal-section { background: var(--color-white); }
.legal-inner {
  max-width: 46rem;              /* ~72 characters — a readable measure for continuous prose */
  margin: 0 auto;
  padding-inline: var(--space-4);
}
.legal-meta {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  margin-bottom: var(--space-5);
}
.legal-inner h2 {
  text-align: left;
  font-size: var(--text-xl);
  margin: var(--space-7) 0 var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.legal-summary h2 { border-top: 0; padding-top: 0; margin-top: 0; }
.legal-inner h3 {
  font-size: var(--text-base);
  margin: var(--space-5) 0 var(--space-2);
  color: var(--color-navy-dark);
}
.legal-inner p,
.legal-inner li { color: var(--color-ink-muted); line-height: 1.75; }
.legal-inner p { margin-bottom: var(--space-3); }
.legal-inner a { color: var(--color-blue); }

.legal-summary {
  background: var(--color-surface-tint-blue);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  margin-bottom: var(--space-6);
}
.legal-summary ul { margin: 0; padding-left: 1.15rem; }
.legal-summary li { margin-bottom: var(--space-2); }
.legal-summary li:last-child { margin-bottom: 0; }

.legal-list { margin: 0 0 var(--space-4); padding-left: 1.15rem; }
.legal-list li { margin-bottom: var(--space-2); }

/* The lawful-basis table. On a phone a two-column table of prose would squeeze to a few words per
   line, so below the breakpoint each row becomes a labelled block instead of shrinking. */
.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
}
.legal-table th,
.legal-table td {
  text-align: left;
  vertical-align: top;
  padding: var(--space-3);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-ink-muted);
  line-height: 1.6;
}
.legal-table th {
  color: var(--color-ink);
  font-family: var(--font-heading);
  font-weight: 600;
  background: var(--color-surface);
}
.legal-inner code {
  font-size: 0.85em;
  background: var(--color-surface);
  padding: 1px 5px;
  border-radius: 4px;
}

@media (max-width: 600px) {
  .legal-table thead { position: absolute; left: -9999px; }
  .legal-table tr {
    display: block;
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-3) 0;
  }
  .legal-table td { display: block; border: 0; padding: 0 0 var(--space-2); }
  .legal-table td:last-child { padding-bottom: 0; }
  .legal-table td:first-child { color: var(--color-ink); font-weight: 600; }
  .legal-table td:last-child::before {
    content: 'Lawful basis: ';
    font-weight: 600;
    color: var(--color-ink);
  }
}


/* ===================== PHASE 20: TESTIMONIALS =====================
   The section is hidden in the markup and only revealed by JS once TESTIMONIALS has entries, so
   an empty array leaves no trace on the page rather than an empty heading. */
.testimonials-section { background: var(--color-surface); }
.testimonial-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--space-4);
  max-width: 1080px;
  margin: var(--space-6) auto 0;
}
.testimonial-card {
  margin: 0;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-soft);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
/* An open quote mark set large and pale, rather than a row of stars we cannot substantiate. */
.testimonial-card blockquote {
  margin: 0;
  position: relative;
  padding-top: var(--space-4);
  color: var(--color-ink);
  font-size: var(--text-base);
  line-height: 1.7;
}
.testimonial-card blockquote::before {
  content: '\201C';
  position: absolute;
  top: -14px;
  left: -4px;
  font-family: var(--font-heading);
  font-size: 3.4rem;
  line-height: 1;
  color: var(--color-blue);
  opacity: 0.22;
}
.testimonial-card figcaption {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.testimonial-card figcaption strong { color: var(--color-navy-dark); font-family: var(--font-heading); }
.testimonial-source { text-align: center; margin-top: var(--space-5); }
.testimonial-source a { color: var(--color-blue); font-weight: 600; }

/* The average and the count, as one tap target that goes to the listing. A reader scans for this
   before reading a single quote, so it sits above the grid rather than under it. */
.reviews-summary {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.7rem;
  margin: 0 auto var(--space-5);
  padding: 0.6rem 1.1rem;
  width: fit-content;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg, 16px);
  color: var(--color-ink);
  text-decoration: none;
  box-shadow: var(--shadow-soft);
}
.reviews-summary:hover { border-color: var(--color-blue); }
.reviews-summary .ic { width: 1.25em; height: 1.25em; }
.reviews-score {
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.reviews-count { color: var(--text-muted); font-size: 0.9rem; }

/* Stars are amber, not the brand orange - a five-star row in a contractor's accent colour reads
   as a graphic rather than as a rating, because a rating is a thing people already recognise. */
.reviews-stars, .testimonial-stars { display: inline-flex; gap: 0.1em; color: #e8a33d; }
.testimonial-stars { margin-bottom: 0.55rem; }
.star { width: 1.05em; height: 1.05em; }
.star--empty { color: var(--color-border); }

/* The link back to the review on Google. This is the attribution, and it is also what makes the
   quote checkable in one click rather than merely asserted - which is the entire reason the
   reviews are read from Google rather than typed into a list. */
.testimonial-src {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  margin-top: 0.5rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-blue);
  text-decoration: none;
}
.testimonial-src:hover { text-decoration: underline; }
.testimonial-src .ic { width: 1em; height: 1em; }

.reviews-ask {
  text-align: center;
  margin-top: var(--space-5);
  color: var(--text-muted);
  font-size: 0.95rem;
}
.reviews-ask a { color: var(--color-blue); font-weight: 600; }

@media (max-width: 900px) {
  .reviews-summary, .testimonial-src, .reviews-ask a { min-height: 44px; }
  .testimonial-src, .reviews-ask a { align-items: center; }
}

/* The cold-surface overlay. Transitioned in CSS so the toggle works whether or not the animation
   library ever loads — this control decides whether something is visible, and those must never
   depend on a script from a CDN. */
#thermalLayer { transition: opacity 0.35s var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  #thermalLayer { transition: none; }
}


/* ===================== PHASE 21: THE JOURNEY OF THE AIR =====================
   A pinned cutaway with four steps scrolling past it. Every stream is drawn at full strength by
   default and every step is ordinary text in the document, so with the script disabled this is
   still a complete, readable explanation. The script only adds emphasis.

   The machine — casing, spigots, ducts, filters, fans, core — never dims. Only the air does. An
   earlier version dimmed the whole stream group including its pipe, which made three quarters of
   the installation disappear every time a step scrolled past, and a machine that keeps vanishing
   does not read as a machine. */
.air-journey {
  background: var(--color-navy-dark);
  color: var(--color-white);
  padding: var(--space-8) var(--space-4);
}
.aj-head { max-width: 46rem; margin: 0 auto var(--space-6); text-align: center; }
.aj-head h2 { color: var(--color-white); }
.aj-head .section-intro { color: rgba(255, 255, 255, 0.72); }
.section-eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #7fc4ff;
  margin-bottom: var(--space-2);
}
.aj-layout {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-7);
  align-items: start;
}
.aj-stage { position: sticky; top: 96px; }

/* The drawing is an object on the page, not a picture printed on it: a hairline of light along
   its top edge, a contact shadow under it, and a 1px inner ring. */
.aj-frame {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    0 2px 6px rgba(0, 0, 0, 0.4),
    0 26px 60px -18px rgba(0, 0, 0, 0.7);
  outline: 1px solid rgba(255, 255, 255, 0.09);
  outline-offset: -1px;
}
.aj-svg { width: 100%; height: auto; aspect-ratio: 1000 / 640; display: block; }

.aj-key {
  display: none;
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.6);
}
.aj-key li { display: flex; align-items: center; gap: 0.5rem; }
.aj-key-swatch {
  width: 14px; height: 14px; border-radius: 3px; flex: none;
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.aj-key-swatch--lag    { background: repeating-linear-gradient(125deg, #3f4d63 0 3px, #5a6a83 3px 5px); }
.aj-key-swatch--filter { background: repeating-linear-gradient(90deg, #cbd6e4 0 2px, #93a1b5 2px 4px); }
.aj-key-swatch--core   { background: linear-gradient(135deg, #f2f6fb, #94a3b8); }

.aj-footnote {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.5);
  line-height: 1.55;
}

/* --- The space --------------------------------------------------------------------------- */
.aj-wall-edge { stroke: rgba(255, 255, 255, 0.16); stroke-width: 1.2; }
/* Contrast. Half the labels on this drawing were white at 0.30-0.42 on a mid-navy ground, which
   is under 2:1 - readable on the machine it was drawn on and nowhere else. */
.aj-zone {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.18em;
  fill: rgba(255, 255, 255, 0.55);
}
.aj-room {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  fill: rgba(255, 255, 255, 0.68);
}
.aj-micro {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  fill: rgba(255, 255, 255, 0.66);
}
.aj-joist path { stroke: #24374f; stroke-width: 5; }
.aj-ceiling-line { stroke: rgba(255, 255, 255, 0.22); stroke-width: 1.4; }
.aj-ceiling-line--soft { stroke: rgba(255, 255, 255, 0.1); }

/* --- Terminals --------------------------------------------------------------------------- */
.aj-grille-body { fill: #97a3b5; stroke: #6b7688; stroke-width: 1; }
.aj-louvre path { stroke: #5c6879; stroke-width: 1.5; stroke-linecap: round; }
.aj-plenum { fill: #97a3b5; stroke: #6b7688; stroke-width: 0.8; }
.aj-plenum-top { fill: #c3ccd9; }
.aj-valve-collar { fill: #8e9aac; }
.aj-valve-disc { fill: #e3e9f1; stroke: #8d99ab; stroke-width: 0.8; }
.aj-valve-lip { fill: #f4f7fb; }

/* --- Ductwork ---------------------------------------------------------------------------- */
/* A round pipe seen from the side is a dark body with one bright line along the top of it. That
   single highlight is the whole difference between a duct and a coloured stroke. */
.aj-duct {
  fill: none;
  stroke: url(#ajDuctBody);
  stroke-width: 30;
  stroke-linecap: butt;
  stroke-linejoin: round;
}
.aj-duct-spec {
  fill: none;
  stroke: rgba(255, 255, 255, 0.30);
  stroke-width: 3;
  stroke-linecap: butt;
  stroke-linejoin: round;
  transform: translateY(-7px);
}
.aj-lagging {
  fill: none;
  stroke: url(#ajLag);
  stroke-width: 48;
  stroke-linecap: butt;
  stroke-linejoin: round;
  opacity: 0.95;
}
.aj-band rect { fill: #cfd7e3; opacity: 0.85; }
/* Jubilee clip over the duct where it is pushed onto the spigot. */
.aj-clamp rect { fill: #6f7c90; stroke: #aeb9c9; stroke-width: 0.8; }

/* --- The unit ---------------------------------------------------------------------------- */
.aj-case-edge { fill: none; stroke: rgba(255, 255, 255, 0.6); stroke-width: 1.2; }
.aj-case-hilite { fill: rgba(255, 255, 255, 0.5); }
.aj-cut-edge {
  fill: none;
  stroke: rgba(255, 255, 255, 0.28);
  stroke-width: 1.2;
  stroke-dasharray: 5 4;
}
/* Light, not dark. It sits on the dark cutaway INSIDE the casing, not on the pale casing itself -
   at #46536a it was invisible and at #2f3a4d it was worse. */
.aj-case-name {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  fill: rgba(255, 255, 255, 0.55);
}
.aj-led circle { fill: #63718a; }
.aj-led .aj-led-on { fill: #6ee7a8; }

.aj-filter rect { fill: #ccd6e4; stroke: #8b98ab; stroke-width: 0.9; }
.aj-pleat { fill: none; stroke: #6f7d92; stroke-width: 1.1; }
.aj-fan circle { fill: #33455f; stroke: #9dabc0; stroke-width: 1.6; }
.aj-fan .aj-fan-hub { fill: #9dabc0; stroke: none; }
.aj-blade { fill: none; stroke: #b3c1d5; stroke-width: 2.4; stroke-linecap: round; }
.aj-plate path { stroke: #64748c; stroke-width: 1.2; }
.aj-core-edge { fill: none; stroke: #ffffff; stroke-width: 1.4; stroke-opacity: 0.55; }
.aj-drain path { fill: none; stroke: #8e9ab0; stroke-width: 3; stroke-linecap: round; }

/* --- The air ----------------------------------------------------------------------------- */
/* Four strands on the same path, each a different width, dash rhythm and speed. The speed
   difference is the whole trick: strands that separate as they travel read as air with depth in
   it, where a single dashed stroke only ever reads as a line with a pattern on it.
   Every dash cycle divides 480, so one shared keyframe loops all four seamlessly. */
.aj-breeze { fill: none; stroke-linecap: round; }
.aj-breeze--halo {
  stroke-width: 21;
  stroke-dasharray: 120 120;
  opacity: 0.2;
  filter: url(#ajWisp);
  animation: ajDrift 9s linear infinite;
}
.aj-breeze--a {
  stroke-width: 6;
  stroke-dasharray: 30 130;
  opacity: 0.5;
  filter: url(#ajWispFine);
  animation: ajDrift 5.2s linear infinite;
}
.aj-breeze--b {
  stroke-width: 3.2;
  stroke-dasharray: 20 100;
  opacity: 0.78;
  animation: ajDrift 3.8s linear infinite;
  animation-delay: -1.3s;
}
.aj-breeze--c {
  stroke-width: 1.5;
  stroke-dasharray: 10 70;
  opacity: 1;
  animation: ajDrift 2.9s linear infinite;
  animation-delay: -0.6s;
}
@keyframes ajDrift { to { stroke-dashoffset: -480; } }

/* Motes riding the same path, on a CSS motion path. Dashes alone give you a rhythm; loose
   particles moving at their own speeds are what make it read as a breeze rather than a signal.
   Hidden where motion paths are unsupported — the strands already carry the meaning. */
.aj-mote { visibility: hidden; }
@supports (offset-path: path("M0 0")) {
  .aj-mote {
    visibility: visible;
    offset-path: var(--d);
    offset-rotate: auto;
    animation: ajMote 7s linear infinite;
  }
}
.aj-mote:nth-of-type(1) { animation-duration: 6.4s;  animation-delay: -0.2s; }
.aj-mote:nth-of-type(2) { animation-duration: 8.9s;  animation-delay: -2.1s; }
.aj-mote:nth-of-type(3) { animation-duration: 5.6s;  animation-delay: -3.4s; }
.aj-mote:nth-of-type(4) { animation-duration: 10.2s; animation-delay: -1.1s; }
.aj-mote:nth-of-type(5) { animation-duration: 7.3s;  animation-delay: -4.6s; }
.aj-mote:nth-of-type(6) { animation-duration: 12s;   animation-delay: -5.9s; }
@keyframes ajMote {
  0%   { offset-distance: 0%;   opacity: 0; }
  10%  { opacity: 0.95; }
  86%  { opacity: 0.95; }
  100% { offset-distance: 100%; opacity: 0; }
}
/* A slow sideways sway, perpendicular to the path because offset-rotate turns the frame with it.
   Air does not travel in a straight line down the middle of a duct, and this is the cheapest
   honest way to say so. */
.aj-mote-dot { animation: ajSway 3.4s ease-in-out infinite alternate; }
.aj-mote:nth-of-type(2n) .aj-mote-dot { animation-duration: 4.7s; animation-delay: -1.6s; }
.aj-mote:nth-of-type(3n) .aj-mote-dot { animation-duration: 2.6s; animation-delay: -0.9s; }
@keyframes ajSway {
  from { transform: translateY(-5px); }
  to   { transform: translateY(5px); }
}

/* Four streams, four temperatures. Intake and exhaust are the cold pair, supply and extract the
   warm pair — the same convention the simulator uses, so the two drawings teach each other. */
.aj-stream--intake  .aj-breeze--halo { stroke: #4f9ec4; }
.aj-stream--intake  .aj-breeze--a { stroke: #6fb3d6; }
.aj-stream--intake  .aj-breeze--b { stroke: #a7d8f0; }
.aj-stream--intake  .aj-breeze--c { stroke: #e2f4ff; }
.aj-stream--intake  .aj-mote-dot { fill: #cbeaff; }

.aj-stream--supply  .aj-breeze--halo { stroke: #7fc4ea; }
.aj-stream--supply  .aj-breeze--a { stroke: #9ed6f4; }
.aj-stream--supply  .aj-breeze--b { stroke: #c9ecff; }
.aj-stream--supply  .aj-breeze--c { stroke: #ffffff; }
.aj-stream--supply  .aj-mote-dot { fill: #ffffff; }

.aj-stream--extract .aj-breeze--halo { stroke: #c4713f; }
.aj-stream--extract .aj-breeze--a { stroke: #dd9059; }
.aj-stream--extract .aj-breeze--b { stroke: #f3b48c; }
.aj-stream--extract .aj-breeze--c { stroke: #ffe8d6; }
.aj-stream--extract .aj-mote-dot { fill: #ffd9b8; }

.aj-stream--exhaust .aj-breeze--halo { stroke: #8a5b45; }
.aj-stream--exhaust .aj-breeze--a { stroke: #a4735a; }
.aj-stream--exhaust .aj-breeze--b { stroke: #c9a390; }
.aj-stream--exhaust .aj-breeze--c { stroke: #efdcd1; }
.aj-stream--exhaust .aj-mote-dot { fill: #e6cfc2; }

/* --- Chips ------------------------------------------------------------------------------- */
/* A temperature floating on its own reads as a label someone dropped on a drawing. On a filled
   pill with a rule of its own colour it reads as a readout, which is what it is. */
.aj-chip rect {
  fill: rgba(9, 17, 30, 0.82);
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-opacity: 0.55;
}
.aj-chip-temp {
  font-family: var(--font-heading);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  fill: currentColor;
}
.aj-chip-text {
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  fill: rgba(255, 255, 255, 0.92);
}
.aj-stream--intake  .aj-chip { color: #a7d8f0; }
.aj-stream--supply  .aj-chip { color: #d6efff; }
.aj-stream--extract .aj-chip { color: #f7c49e; }
.aj-stream--exhaust .aj-chip { color: #cfae9c; }

.aj-dir {
  fill: none;
  stroke: rgba(255, 255, 255, 0.85);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.aj-legend rect {
  fill: rgba(9, 17, 30, 0.66);
  stroke: rgba(255, 255, 255, 0.14);
  stroke-width: 1;
}
.aj-legend-title {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  fill: rgba(255, 255, 255, 0.45);
}
.aj-legend-rule { stroke: rgba(255, 255, 255, 0.12); stroke-width: 1; }
.aj-legend-text {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  fill: rgba(255, 255, 255, 0.82);
}
.aj-num circle { fill: rgba(9, 17, 30, 0.9); stroke: rgba(255, 255, 255, 0.55); stroke-width: 1; }
.aj-num text {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 700;
  fill: #ffffff;
}
.aj-leader { fill: none; stroke: rgba(255, 255, 255, 0.4); stroke-width: 1.2; stroke-dasharray: 3 3; }
.aj-leader-dot { fill: rgba(255, 255, 255, 0.7); }
.aj-core-label {
  font-family: var(--font-heading);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  fill: rgba(255, 255, 255, 0.92);
}

/* --- Armed state: only once the script can undo it ---------------------------------------- */
.aj-armed .aj-stream {
  opacity: 0.24;
  transition: opacity 0.55s var(--ease-out);
}
.aj-armed .aj-stream.is-active { opacity: 1; }
.aj-armed .aj-stream.is-active .aj-breeze--c { filter: url(#ajGlow); }
.aj-armed .aj-core-note { transition: opacity 0.5s var(--ease-out); opacity: 0.3; }
.aj-armed.aj-core-on .aj-core-note { opacity: 1; }

/* --- Steps ------------------------------------------------------------------------------- */
.aj-steps { list-style: none; margin: 0; padding: 0; }
.aj-step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-6) 0 var(--space-6) var(--space-5);
  border-left: 2px solid rgba(255, 255, 255, 0.12);
}
.aj-armed .aj-step { transition: border-color 0.4s var(--ease-out), opacity 0.4s var(--ease-out); opacity: 0.45; }
.aj-armed .aj-step.is-active { opacity: 1; border-left-color: #7fc4ff; }
.aj-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.25);
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-white);
  transition: background 0.4s var(--ease-out), border-color 0.4s var(--ease-out);
}
.aj-armed .aj-step.is-active .aj-step-n { background: var(--color-orange); border-color: transparent; }
.aj-step-tag {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #7fc4ff;
  margin: 0 0 var(--space-1);
}
.aj-step h3 {
  color: var(--color-white);
  font-size: var(--text-lg);
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-2);
}
.aj-step p { color: rgba(255, 255, 255, 0.7); line-height: 1.75; margin: 0; }

@media (max-width: 900px) {
  .air-journey { padding-inline: var(--space-4); }
  .aj-layout { grid-template-columns: 1fr; gap: var(--space-4); }
  /* The diagram pins to the top of the screen and the steps run underneath it. */
  .aj-stage {
    top: 64px;
    background: var(--color-navy-dark);
    padding-bottom: var(--space-3);
    margin-inline: calc(var(--space-4) * -1);
    padding-inline: var(--space-4);
    z-index: 1;
  }
  .aj-frame { border-radius: var(--radius-md); }

  /* The drawing is 1000 units wide against roughly 350 real pixels, so everything inside it is
     rendered at about a third of its nominal size. Type has to be set enormous in viewBox units
     to come out legible, and the chips are dropped entirely — the step they belong to is directly
     underneath carrying the same temperature in its tag, and a 4px readout on a phone is
     decoration pretending to be information. */
  .aj-chip, .aj-micro, .aj-case-name, .aj-legend, .aj-marks { display: none; }
  .aj-key { display: flex; }
  .aj-room { display: none; }
  .aj-dir { stroke-width: 4; }
  .aj-zone { font-size: 30px; letter-spacing: 0.1em; }
  .aj-core-label { font-size: 30px; }
  .aj-breeze--halo { stroke-width: 24; }
  .aj-breeze--a { stroke-width: 8; }
  .aj-breeze--b { stroke-width: 4.5; }
  .aj-breeze--c { stroke-width: 2.4; }
  /* padding-right was 0, so every line of the step copy ended flush against the screen edge. */
  .aj-step { padding: var(--space-5) var(--space-2) var(--space-5) var(--space-4); }
  .aj-footnote { font-size: 0.72rem; }
}

@media (prefers-reduced-motion: reduce) {
  .aj-breeze { animation: none; }
  .aj-breeze--halo, .aj-breeze--a { opacity: 0.4; }
  .aj-mote, .aj-mote-dot { animation: none; }
  .aj-mote { visibility: hidden; }
  .aj-armed .aj-stream, .aj-armed .aj-step { transition: none; }
}



.work-areas-link {
  text-align: center;
  max-width: 44rem;
  margin: var(--space-5) auto 0;
  color: var(--color-ink-muted);
  font-size: var(--text-sm);
  line-height: 1.7;
}



/* --- Simulator: inside the plant --------------------------------------------------------
   The units are drawn with the front panel off. A box with a cross in it says "some equipment";
   a core, two fans and two filters says what the customer is paying for. */
.unit-hilite { fill: #ffffff; opacity: 0.5; }
.unit-cavity { fill: #1e3050; }
.unit-core-body { fill: #dbe3ee; }
.unit-core-plate { stroke: #8794a8; stroke-width: 0.5; }
.unit-fan-body { fill: #38496a; stroke: #b6c3d6; stroke-width: 0.8; }
.unit-fan-hub { fill: #b0bccd; }
.unit-led { fill: #6ee7a8; }

/* --- Simulator: building fabric --------------------------------------------------------- */
.fabric-face { stroke: #8f99a9; stroke-width: 0.9; }
.fabric-skirting { fill: #eef1f6; opacity: 0.9; }
.fabric-skirting-top { fill: #ffffff; opacity: 0.7; }
.fabric-floor-line { fill: #0B1220; opacity: 0.16; }

/* The floor going back into the room. One trapezoid per room, and suddenly the drawing has a
   third dimension in it everywhere at once. */
/* A house is not one material. Boards in the hall, carpet in the bedrooms and the living room,
   tiles in the kitchen and the bathroom - the eye reads a room by its floor before it reads
   anything else in it, and every room here used to have the same one. */
.fabric-floor { opacity: 0.94; }
/* A floor is DARKER than the wall above it - in a real room, always, and here it is also what
   stops the room reading as one flat field of the same value from skirting to ceiling. */
.fabric-floor--board { fill: #7d6d55; }
.fabric-floor--carpet { fill: #6d685f; }
.fabric-floor--tile { fill: #838d99; }
.fabric-board { stroke: #6d6050; stroke-width: 0.6; opacity: 0.5; }
.fabric-tile-joint { stroke: #74808d; stroke-width: 0.55; opacity: 0.6; }
.fabric-nap { stroke: #6b6862; stroke-width: 0.8; opacity: 0.34; stroke-linecap: round; }
.fabric-floor-shadow { stroke: #0B1220; stroke-width: 1.6; opacity: 0.14; fill: none; }

/* Radiator under the window: instantly recognisable, and the reason the glass above it is the
   coldest surface in the room rather than the warmest. */
.rad-body { fill: #eaeef4; stroke: #b3bcc9; stroke-width: 0.6; }
.rad-top { fill: #ffffff; opacity: 0.8; }
.rad-fin { stroke: #c3cbd6; stroke-width: 1.1; }
.rad-leg { fill: #9aa4b3; }

/* A door. Drawn against the back wall because that is the wall this projection shows - edge-on in
   a partition it would be a single line nobody could read. */
.door-leaf { fill: #dfe4ec; stroke: #a9b2c0; stroke-width: 0.7; }
.door-head { fill: #c9d0da; }
.door-panel { fill: none; stroke: #b6bec9; stroke-width: 0.7; }
.door-handle { fill: #8d99ab; }

/* Loft structure. The two outdoor duct runs cross this space, and it is the reason those two
   are the only runs that have to be lagged. */
/* Roof timbers. Softwood, seen on edge, with a darker line round each member so the assembly
   reads as separate pieces of wood rather than as one shape. The two outdoor duct runs cross this
   space, and it is the reason those two are the only ones that have to be lagged. */
.timber {
  fill: #c2a679;
  stroke: #8d7148;
  stroke-width: 0.55;
  stroke-linejoin: round;
}
.timber-rafter { fill: #cbaf83; }
.timber-collar { fill: #b89b6f; }
.timber-plate { fill: #b99a6b; }
/* The sawn end of a joist. In a section you are looking at the cut face of every one of them, and
   a flat rectangle is the one thing that never reads as timber. */
.timber-endgrain { fill: #dcc190; opacity: 0.75; }
/* Grain. Fine, low-contrast and ALONG the member - see the note in buildFabric. Heavy enough to
   see at full size and light enough to disappear at thumbnail, which is what grain does. */
.timber-grain {
  fill: none;
  stroke: #8d7148;
  stroke-width: 0.5;
  opacity: 0.42;
  stroke-linecap: round;
}

/* Loft insulation. 270mm of glass wool between and over the joists - the reason the loft is cold,
   and therefore the reason every duct crossing it is lagged. It is not scenery. */
.loft-insul { fill: #d8c69c; }
.loft-insul-lip { stroke: #f6efdc; stroke-width: 1.3; opacity: 0.85; }
.loft-hatch { fill: #b9a781; stroke: #8d7c5c; stroke-width: 0.7; }
/* The opening itself. You are looking down a trimmed hole into the landing, so it is the darkest
   thing in the loft floor. */
.hatch-hole { fill: #16202f; }
.hatch-lining { fill: #d8cdb4; stroke: #ab9d7f; stroke-width: 0.4; }
.loft-ladder line { stroke: #c2ab7c; stroke-width: 1.1; stroke-linecap: round; opacity: 0.92; }
/* Rafters going away from you. Same timber, further off, hazing toward the dark of the roof
   space - which is all aerial perspective is. */
.timber-back { fill: #7d6a4e; }

/* The contact shadow under every floor-standing piece. Nothing in the drawing cast anything, and
   furniture with no shadow under it is a sticker on a wall - this is most of the difference
   between "placed in a room" and "in a room". */
.furn-shadow { fill: #0B1220; opacity: 0.3; filter: blur(2.2px); }
/* Sarking felt, sagging between the rafters. */
.roof-felt { stroke: #3f3730; stroke-width: 1.5; opacity: 0.72; stroke-linejoin: round; }
.timber-binder { fill: #b89b6f; }
.fabric-ground-line { stroke: #6c7789; stroke-width: 1.2; }
.fabric-cavity { fill: #6f7889; opacity: 0.55; }
.fabric-cut-edge { stroke: #7d8798; stroke-width: 1.1; }

/* The stair is beyond the plane of the cut - you are seeing it through the hall doorway - so it
   is drawn back rather than forward. Push the contrast up and it starts to look like a staircase
   sitting in the middle of somebody's bedroom. */
/* A staircase you can see. At 38% of a pale grey it was invisible against the hall wall, which
   rather defeated the point of giving every property a hall to put it in. Treads read as timber
   with a painted string, which is what most of this housing stock has. */
.stairs-flight { fill: #cdb894; opacity: 0.95; stroke: #9c8560; stroke-width: 0.7; }
.stairs-nosing { stroke: #8a7350; stroke-width: 0.9; opacity: 0.85; }
.stairs-rail { stroke: #9c8560; stroke-width: 1.8; opacity: 0.95; stroke-linecap: round; }
.stairs-landing { stroke: #8d99ab; stroke-width: 1.4; opacity: 0.45; }

/* Loft insulation between the joists. It is also the surface the two outdoor duct runs cross,
   which is the whole reason those two have to be lagged. */
.loft-batt { fill: #c9c2ae; opacity: 0.5; }
.loft-line { stroke: #a9a390; stroke-width: 1; opacity: 0.5; }

/* --- Areas: the wider list ---------------------------------------------------------------
   The twelve cards above are places we can say something specific about. These are the rest of
   the run, set as chips because they are a list of names and pretending otherwise with twelve
   more paragraphs of filler is exactly what makes a coverage page look spun. */
.areas-more { max-width: 1160px; margin: var(--space-6) auto 0; text-align: center; }
.areas-more h3 {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: var(--space-3);
}
.areas-chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
}
.areas-chips li {
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.8);
}
.areas-note {
  max-width: 42rem;
  margin: var(--space-4) auto 0;
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.66);
  line-height: 1.7;
}
/* Twelve area names in one footer column is a very long column, so they run in two. */
.footer-areas { columns: 2; column-gap: var(--space-4); }


/* ===================== HOME: BEFORE / AFTER TEASER =====================
   Same shell as the old work teaser, with a draggable comparison where the static photograph was.
   The slider sits on a dark panel, so its caption and job buttons need the light treatment — they
   are styled for a white page on our-work.html. */
.ba-teaser {
  background: linear-gradient(160deg, var(--color-navy-dark) 0%, var(--color-navy) 100%);
  border-radius: var(--radius-lg);
  color: var(--color-white);
}
.ba-teaser-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: var(--space-7);
  align-items: center;
  padding: var(--space-8) var(--space-4);
}
.ba-teaser-copy h2 { text-align: left; color: var(--color-white); margin-bottom: var(--space-3); }
.ba-teaser-copy p { color: rgba(255, 255, 255, 0.78); margin-bottom: var(--space-5); }
.ba-teaser .cta-band-actions { justify-content: flex-start; }
.ba-teaser .ba-gallery { max-width: none; margin: 0; }
.ba-teaser .ba-caption { color: rgba(255, 255, 255, 0.72); text-align: left; }
.ba-teaser .ba-caption strong { color: var(--color-white); }
.ba-teaser .ba-thumbs { justify-content: flex-start; }
.ba-teaser .ba-thumb {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.2);
  color: rgba(255, 255, 255, 0.78);
}
.ba-teaser .ba-thumb[aria-selected="true"] {
  background: rgba(255, 255, 255, 0.14);
  border-color: #7fc4ff;
  color: var(--color-white);
}
.ba-teaser .work-empty p { color: rgba(255, 255, 255, 0.72); }

@media (max-width: 900px) {
  .ba-teaser-inner { grid-template-columns: 1fr; gap: var(--space-5); padding: var(--space-6) var(--space-4); }
}





/* ===================== CREDENTIALS STRIP =====================
   Above the footer on every page. Four cards, evenly weighted, sized so that a logo could drop
   into the icon slot the day one is legitimately available. See the note in the section markup
   for why there are no manufacturer or trade-body marks in here. */
.creds {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  padding: var(--space-7) var(--space-4);
}
.creds-inner { max-width: 1160px; margin: 0 auto; }
.creds-head { text-align: center; margin-bottom: var(--space-5); }
.creds-eyebrow {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-blue);
  margin-bottom: var(--space-2);
}
.creds-head h2 { font-size: var(--text-xl); margin: 0; }
.creds-lead {
  max-width: 40rem;
  margin: var(--space-3) auto 0;
  color: var(--color-ink-muted);
  font-size: var(--text-sm);
  line-height: 1.7;
}
.creds-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-3);
  align-items: start;
}
/* A certificate card leads with the document, because the document is the evidence. The thumbnail
   is deliberately tall — a certificate cropped to a letterbox stops looking like a certificate. */
.creds-card--cert { padding: var(--space-3); }
.creds-thumb {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.creds-thumb:hover, .creds-thumb:focus-visible { transform: translateY(-3px); box-shadow: var(--shadow-2); }
.creds-thumb img { display: block; width: 100%; height: auto; }
.creds-body { display: grid; gap: 0.3rem; padding: var(--space-3) var(--space-1) 0; }
.creds-link {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-blue);
  margin-top: 0.2rem;
}
.creds-card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  box-shadow: var(--shadow-1);
  display: grid;
  gap: 0.35rem;
}
.creds-icon {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--color-surface-tint-blue);
  color: var(--color-navy);
  margin-bottom: var(--space-1);
}
.creds-icon svg { width: 20px; height: 20px; }
.creds-card strong {
  font-family: var(--font-heading);
  font-size: var(--text-base);
  color: var(--color-navy-dark);
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.creds-card span { font-size: var(--text-sm); color: var(--color-ink-muted); line-height: 1.5; }
.creds-note {
  margin: var(--space-4) 0 0;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
}

/* ===================== ABOUT =====================
   Copy on the left, the facts that can actually be stood behind on the right. Every figure in the
   fact list is one the business can evidence - no accreditation, no insurance figure and no
   customer count, because none of those are recorded anywhere we can check. */
.about-section { background: var(--color-surface); border-radius: var(--radius-lg); }
.about-inner {
  max-width: 1160px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: var(--space-7);
  align-items: start;
}
.about-eyebrow { text-align: left; color: var(--color-blue); }
.about-copy h2 { text-align: left; margin-bottom: var(--space-4); }
.about-copy p { color: var(--color-ink-muted); line-height: 1.8; margin-bottom: var(--space-4); }
.about-quote {
  margin: var(--space-5) 0 0;
  padding: var(--space-4) var(--space-5);
  border-left: 3px solid var(--color-orange);
  background: var(--color-white);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  line-height: 1.6;
  color: var(--color-navy-dark);
  box-shadow: var(--shadow-1);
}
.about-facts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}
.about-facts li {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  box-shadow: var(--shadow-1);
}
.about-facts strong {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  color: var(--color-navy);
  letter-spacing: -0.01em;
}
.about-facts span { font-size: var(--text-sm); color: var(--color-ink-muted); }

@media (max-width: 900px) {
  .about-inner { grid-template-columns: 1fr; gap: var(--space-5); }
  .about-facts { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

/* ===================== PHOTO GALLERY =====================
   Finished work, one photograph each, opening into a lightbox. The caption sits on the image
   rather than under it so the grid stays even however long the titles are. */
.gallery-grid {
  list-style: none;
  margin: var(--space-6) auto 0;
  padding: 0;
  max-width: 1160px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}
.gallery-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-white);
  cursor: zoom-in;
  position: relative;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.gallery-btn:hover,
.gallery-btn:focus-visible { transform: translateY(-4px); box-shadow: var(--shadow-3); }
.gallery-btn img {
  display: block;
  width: 100%;
  /* height:auto matters. The width/height attributes on the img map to a presentational height,
     and aspect-ratio only gets to size the box while one dimension is auto - without this every
     tile rendered at its own intrinsic height and the grid came out ragged. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.gallery-cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: var(--space-5) var(--space-3) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-white);
  text-align: left;
  background: linear-gradient(to top, rgba(11, 18, 32, 0.86), rgba(11, 18, 32, 0));
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(6, 11, 20, 0.94);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4);
}
.lightbox[hidden] { display: none; }
.lightbox-figure { margin: 0; text-align: center; max-height: 100%; }
.lightbox-figure img {
  max-width: 100%;
  max-height: 74vh;
  border-radius: var(--radius-md);
  display: block;
  margin: 0 auto;
}
.lightbox-figure figcaption {
  margin-top: var(--space-3);
  color: rgba(255, 255, 255, 0.76);
  font-size: var(--text-sm);
  line-height: 1.6;
  max-width: 46rem;
  margin-inline: auto;
}
.lightbox-figure figcaption strong { display: block; color: var(--color-white); margin-bottom: 2px; }
.lightbox-close,
.lightbox-nav {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.24);
  color: var(--color-white);
  border-radius: 50%;
  width: 48px;
  height: 48px;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  flex: none;
}
.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(255, 255, 255, 0.2); }
.lightbox-close { position: absolute; top: var(--space-3); right: var(--space-3); }

@media (max-width: 700px) {
  .lightbox { grid-template-columns: 1fr; grid-template-rows: 1fr auto; padding: var(--space-3); }
  .lightbox-nav { position: absolute; bottom: var(--space-4); }
  .lightbox-prev { left: var(--space-4); }
  .lightbox-next { right: var(--space-4); }
  .lightbox-figure figcaption { padding-bottom: 64px; }
}

/* What each service actually covers. This list is where the specific search terms live. */
.service-list {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: var(--space-3) 0 0;
  border-top: 1px solid var(--color-border);
}
.service-list li {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: 0.3rem;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  line-height: 1.5;
}
.service-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--color-blue);
}
/* ===================== PHASE 22: THE EXPENSIVE PASS =====================
   Nothing here changes what the site says. It changes how carefully it looks like it was made,
   which is the difference between a site that quotes a good rate and one that gets away with it.

   Four things do most of that work, and none of them is decoration:
     1. Shadows in two layers. One tight contact shadow and one wide ambient — a single mid shadow
        is the commonest tell of a template, because real objects cast both.
     2. A hairline of light along the top edge of every raised surface. It costs one inset shadow
        and it is the difference between a card sitting ON the page and being printed on it.
     3. Optical tracking. Large type needs negative letter-spacing and small caps need positive;
        leaving both at zero is what makes headings look like a word processor.
     4. Grain over the dark sections, so a large flat navy reads as a surface rather than a fill. */
:root {
  --shadow-1: 0 1px 2px rgba(11, 18, 32, 0.06), 0 2px 8px rgba(11, 18, 32, 0.05);
  --shadow-2: 0 2px 4px rgba(11, 18, 32, 0.07), 0 10px 26px -6px rgba(11, 18, 32, 0.12);
  --shadow-3: 0 4px 8px rgba(11, 18, 32, 0.08), 0 22px 48px -12px rgba(11, 18, 32, 0.2);
  --edge-light: inset 0 1px 0 rgba(255, 255, 255, 0.85);
  --shadow-soft: var(--shadow-2);
}

h1 { letter-spacing: -0.025em; }
h2 { letter-spacing: -0.02em; }
h3 { letter-spacing: -0.01em; }
.hero h1 { letter-spacing: -0.03em; }

/* Raised light surfaces get the two-layer shadow and the lit top edge. */
.service-card,
.bento-cell,
.tool-card,
.picker-card,
.testimonial-card,
.area-card,
.plate-frame,
.faq-item,
.reviews-card {
  box-shadow: var(--shadow-2), var(--edge-light);
}
.service-card:hover,
.tool-card:hover,
.picker-card:hover,
.area-card:hover { box-shadow: var(--shadow-3), var(--edge-light); }

/* A slower, more considered curve than the default. Expensive things do not snap. */
.service-card, .tool-card, .picker-card, .area-card, .bento-cell {
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}

/* Grain. Large flat fills band and read as vector; a film of noise over them reads as a surface.
   Applied to the dark bands only, where the effect is strongest and the cost is invisible. */
.air-journey, .hero, .site-footer, .cta-band { position: relative; }
.air-journey::after,
.hero::after,
.site-footer::after,
.cta-band::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.09'/%3E%3C/svg%3E");
}
/* The hero's own content has to sit above its grain. */
.hero-content, .hero-scroll { position: relative; z-index: 4; }

@media (prefers-reduced-motion: reduce) {
  .service-card, .tool-card, .picker-card, .area-card, .bento-cell { transition: none; }
}

/* The exchanger's plates, and the shadow that stops the core reading as a flat white square. */
.aj-core-plate { stroke: #8d9db4; stroke-width: 0.9; opacity: 0.5; }
.aj-core-body { filter: drop-shadow(0 10px 26px rgba(0, 0, 0, 0.55)); }
.aj-core-hatch { stroke-opacity: 0.75; }

/* Air leaving and arriving, drawn as motes streaming between the room and its valve rather than
   the whole room's haze sliding sideways and shrinking. Stale is the same warm murk as the extract
   ducts; fresh is the cool bright blue of the supply side, so the two read at a glance. */
/* Room air, as a flow field rather than as objects.
   Two elements per streamline. The RIBBON carries the shape - soft, tapered, and the thing that
   makes it read as air with volume. The TRACE is a dashed line down its centre and carries the
   movement, on a CSS dash animation so it runs continuously for as long as the system is running
   rather than playing once and stopping. Every dash cycle is 36 units and the keyframe travels
   108, so all of them loop seamlessly off one keyframe. */
.airflow { pointer-events: none; }
.air-ribbon {
  stroke: none;
  opacity: 0.3;
  filter: blur(0.55px);
}
.air-trace {
  fill: none;
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-dasharray: 6 21;
  animation: airTrace 2.6s linear infinite;
}
/* 27 to a cycle and 108 to the keyframe, so every streamline loops seamlessly off one keyframe
   whatever delay it was given. */
@keyframes airTrace { to { stroke-dashoffset: -108; } }

/* Colour comes from a gradient laid along each path in userSpaceOnUse, not from a class - cool
   where the air arrives, warm where it leaves, so you can watch it pick the room up on the way
   through. The classes only carry weight and opacity. */
.air-ribbon { opacity: 0.26; }
.air-trace { opacity: 0.78; }

/* Stale air is thicker, slower and murkier than fresh air, and it should look it as it crosses
   the room on its way to the grille. Fresh air is thinner and quicker. */
.airflow--stale .air-ribbon { opacity: 0.42; filter: blur(1.1px); }
.airflow--stale .air-trace { opacity: 0.66; stroke-width: 1.45; animation-duration: 3.6s; }
.airflow--fresh .air-ribbon { opacity: 0.26; filter: blur(0.4px); }
.airflow--fresh .air-trace { opacity: 0.92; stroke-width: 1.15; animation-duration: 2.2s; }

@media (prefers-reduced-motion: reduce) {
  .air-trace { animation: none; }
}


/* ===================== PHASE 23: THE HERO =====================
   The hero was doing the least work of anything on the site: a photograph, a flat scrim over it,
   and type. Four changes, none of them an effect for its own sake.

   1. A very slow drift on the photograph. Six per cent over forty seconds is below the threshold
      where anyone notices it moving, and above the threshold where the page feels alive rather
      than printed. Anything faster reads as a screensaver.
   2. A vignette, so the corners fall away and the eye is taken to the headline instead of the
      brightest pixel in the frame.
   3. A hairline rule and better hierarchy on the trust row — three claims with no structure read
      as a caption; the same three with a rule and a size step read as credentials.
   4. The scrim carries a warm lift on the right, where the ductwork in the photograph is, so the
      picture has some temperature in it rather than being uniformly cold navy. */
.hero { overflow: hidden; }
.hero-media img {
  transform-origin: 62% 45%;
  animation: heroDrift 40s var(--ease-in-out) infinite alternate;
  will-change: transform;
}
@keyframes heroDrift {
  from { transform: scale(1.005); }
  to   { transform: scale(1.065); }
}

.hero-scrim {
  background:
    radial-gradient(120% 90% at 22% 42%, rgba(10, 20, 48, 0) 34%, rgba(6, 12, 30, 0.55) 100%),
    linear-gradient(105deg, rgba(10, 20, 48, 0.95) 0%, rgba(10, 20, 48, 0.8) 42%, rgba(10, 20, 48, 0.34) 100%),
    linear-gradient(180deg, rgba(10, 20, 48, 0.58) 0%, rgba(10, 20, 48, 0.08) 40%, rgba(8, 15, 36, 0.78) 100%),
    radial-gradient(70% 60% at 88% 62%, rgba(209, 65, 36, 0.16) 0%, rgba(209, 65, 36, 0) 70%);
}

/* The hero on a phone.

   Dark on the LEFT, because that is the side the copy is on at every width. The angle is the only
   thing that differs from the desktop wash above; the stops are heavier, because a phone shows the
   type over a much smaller slice of photograph. Vertical bands at both ends keep the header and
   the sticky bar readable over it.

   This rule has to stay AFTER the plain `.hero-scrim` above or it is silently overridden - which
   is exactly what happened to the version that used to live at line ~3234, so the photograph
   behind the headline had never once been visible on a phone. */
@media (max-width: 768px) {
  .hero-scrim {
    background:
      linear-gradient(100deg,
        rgba(10, 20, 48, 0.93) 0%,
        rgba(10, 20, 48, 0.80) 38%,
        rgba(10, 20, 48, 0.34) 78%,
        rgba(10, 20, 48, 0.22) 100%),
      linear-gradient(180deg,
        rgba(10, 20, 48, 0.72) 0%,
        rgba(10, 20, 48, 0.10) 30%,
        rgba(10, 20, 48, 0.16) 62%,
        rgba(8, 15, 36, 0.88) 100%);
  }
}

/* Credentials, not a caption. */
.hero-trust {
  gap: 0;
  border-top: 0;
  padding-top: var(--space-5);
  position: relative;
}
.hero-trust::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 220px;
  height: 1px;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0));
}
.hero-trust li {
  padding-right: var(--space-5);
  margin-right: var(--space-5);
  border-right: 1px solid rgba(255, 255, 255, 0.14);
}
.hero-trust li:last-child { border-right: 0; margin-right: 0; padding-right: 0; }
.hero-trust strong { font-size: var(--text-xl); letter-spacing: -0.01em; line-height: 1.1; }
.hero-trust span {
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.58);
  letter-spacing: 0.01em;
}

@media (max-width: 600px) {
  /* Stacked, the vertical rules become clutter — a thin rule between rows reads better. */
  .hero-trust li {
    border-right: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
    padding: 0 0 var(--space-3);
    margin: 0 0 var(--space-3);
    width: 100%;
  }
  .hero-trust li:last-child { border-bottom: 0; padding-bottom: 0; margin-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media img { animation: none; transform: scale(1.02); }
}

/* --- Landlords: damp, mould and Awaab's Law ---------------------------------------------------
   A distinct band rather than another white section, because this is a different audience with a
   different problem: a homeowner wants the room to stop going black, a landlord wants to be able
   to prove what happened and when. */
.landlords {
  background: linear-gradient(180deg, var(--color-surface) 0%, #eef1f6 100%);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}
.landlords-head { max-width: 760px; margin: 0 auto var(--space-7); text-align: center; }
.landlords-eyebrow {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-orange);
  margin-bottom: var(--space-3);
}
.landlords-head h2 { margin-bottom: var(--space-4); }
.landlords-head .section-intro { margin-bottom: var(--space-4); }

/* The duty on the left of each card, what we do about it underneath. The pairing IS the pitch -
   a list of services with no duty against them is just a list of services. */
.lawgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.lawcard {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-soft);
}
.lawcard h3 {
  font-size: 1.05rem;
  margin: 0 0 var(--space-3);
  color: var(--color-navy-dark);
}
.lawcard p { font-size: var(--text-sm); line-height: 1.6; color: var(--color-ink-muted); margin: 0; }
.lawcard-step,
.lawcard-us {
  display: inline-block;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: 999px;
  margin-bottom: var(--space-3);
}
.lawcard-step { background: #e7ebf3; color: var(--color-ink-muted); }
.lawcard-us { background: var(--color-navy); color: #fff; margin-top: var(--space-4); }
.lawcard-body { color: var(--color-ink); }

/* The portal, on the app's own dark ground so it reads as the connected product it is. */
.landlord-portal {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  align-items: start;
  background: #0D1826;
  color: #fff;
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  margin-top: var(--space-6);
}
@media (min-width: 900px) {
  .landlord-portal { grid-template-columns: 1fr 1.05fr; gap: var(--space-7); }
}
.landlord-portal h3 {
  color: #fff;
  font-size: 1.5rem;
  margin: 0 0 var(--space-4);
}
.landlord-portal p {
  color: rgba(255, 255, 255, 0.76);
  line-height: 1.65;
  margin-bottom: var(--space-4);
}
.landlord-portal .btn-primary { background: var(--color-blue); border-color: var(--color-blue); }

.landlord-benefits { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.landlord-benefits li {
  position: relative;
  padding-left: 30px;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.74);
}
.landlord-benefits li strong { display: block; color: #fff; margin-bottom: 2px; }
/* A tick, drawn rather than typed, so it lines up with the first line of the label. */
.landlord-benefits li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(61, 169, 222, 0.18);
  border: 1px solid rgba(61, 169, 222, 0.55);
}
.landlord-benefits li::after {
  content: '';
  position: absolute;
  left: 6.2px;
  top: 6.6px;
  width: 4px;
  height: 8px;
  border: solid #7fc9f2;
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}

.landlords-cta {
  margin-top: var(--space-6);
  text-align: center;
}
.landlords-cta p {
  max-width: 560px;
  margin: 0 auto var(--space-4);
  color: var(--color-ink-muted);
}
.service-more {
  display: inline-block;
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-blue);
}
.service-more::after { content: ' 92'; }

/* Three audiences, three problems. A letting agent and an estate agent are not buying the same
   thing, and a page that treats them as one reads as written for neither. */
.who-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-7);
}
.who-card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-orange);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.who-card h3 { font-size: 1.02rem; margin: 0 0 var(--space-3); color: var(--color-navy-dark); }
.who-card p { font-size: var(--text-sm); line-height: 1.6; color: var(--color-ink-muted); margin: 0 0 var(--space-3); }
.who-outcome {
  margin: 0 !important;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-navy) !important;
  font-weight: 600;
}
/* Surveyors are a referral relationship, not a client one - so a line rather than a card, and it
   says the one thing they want to hear, which is that we are not after their diagnosis work. */
.who-referral {
  max-width: 760px;
  margin: 0 auto var(--space-7);
  padding: var(--space-4) var(--space-5);
  background: var(--color-white);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--color-ink-muted);
  text-align: center;
}
.who-referral strong { color: var(--color-navy-dark); }

.landlords-sub {
  text-align: center;
  font-size: 1.15rem;
  color: var(--color-navy-dark);
  margin: 0 0 var(--space-5);
}

/* Awaab: what is coming, set apart from what is being sold. */
.awaab-note {
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-6);
  background: var(--color-white);
  border-left: 4px solid var(--color-navy);
  border-radius: var(--radius-md);
}
.awaab-note h3 { margin: 0 0 var(--space-3); font-size: 1.08rem; color: var(--color-navy-dark); }
.awaab-note p { font-size: var(--text-sm); line-height: 1.65; color: var(--color-ink-muted); margin: 0 0 var(--space-3); }
.awaab-note p:last-child { margin-bottom: 0; }

/* The portal tour. Hidden until there is a real screenshot to put in it. */
.portal-tour[hidden] { display: none; }
.portal-tour { margin-top: var(--space-7); }
.portal-tour-head { max-width: 620px; margin: 0 auto var(--space-5); text-align: center; }
.portal-tour-head h3 { margin: 0 0 var(--space-3); color: var(--color-navy-dark); }
.portal-tour-head p { font-size: var(--text-sm); color: var(--color-ink-muted); min-height: 2.6em; margin: 0; }
.portal-tour-stage { margin: 0; }
/* A browser chrome round it, so a screenshot reads as software rather than as a picture. */
.portal-tour-frame {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #0D1826;
  box-shadow: 0 24px 60px rgba(11, 18, 32, 0.28);
  border: 1px solid var(--color-border);
}
.portal-tour-bar {
  display: block;
  height: 26px;
  background: #16233a;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.portal-tour-bar::before {
  content: '';
  position: absolute;
  left: 12px; top: 10px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #47536b;
  box-shadow: 12px 0 0 #47536b, 24px 0 0 #47536b;
}
.portal-tour-frame img { display: block; width: 100%; height: auto; }
.portal-tour-tabs {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}
.portal-tour-tabs button {
  border: 1px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-ink-muted);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.portal-tour-tabs button[aria-selected="true"] {
  background: var(--color-navy);
  border-color: var(--color-navy);
  color: #fff;
}
@media (prefers-reduced-motion: reduce) { .portal-tour-tabs button { transition: none; } }

/* What lands in the account per job. Everything on this list is something the app already
   produces - it is not a list of accreditations and must never become one. */
.paperwork {
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-6);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.paperwork h3 { margin: 0 0 var(--space-4); font-size: 1.02rem; color: var(--color-navy-dark); }
.paperwork-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-4);
}
.paperwork-list li {
  padding-left: var(--space-4);
  border-left: 2px solid var(--color-blue);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.paperwork-list strong { display: block; color: var(--color-navy-dark); margin-bottom: 2px; }
.paperwork-list span { color: var(--color-ink-muted); }

/* --- Servicing, and trade first fix ----------------------------------------------------------
   The half of the year that is not damp season. Sold as a plan rather than as a callout, because
   the point of it is a recurring visit, not a one-off. */
.servicing { background: var(--color-white); }
.servicing-head { max-width: 740px; margin: 0 auto var(--space-7); text-align: center; }
.servicing-head h2 { margin-bottom: var(--space-4); }
.servicing-head .section-intro { margin-bottom: var(--space-4); }

.plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.plan-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.plan-card h3 {
  font-size: 1.02rem;
  margin: 0 0 var(--space-3);
  color: var(--color-navy-dark);
}
.plan-what { font-size: var(--text-sm); line-height: 1.6; color: var(--color-ink); margin: 0 0 var(--space-3); }
/* Why it matters, not what it is. The list of tasks is table stakes; the reason is the sale. */
.plan-why {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--color-ink-muted);
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
/* The F-Gas line. It is a compliance statement, not a footnote - an agent doing their job asks. */
.plan-note { border-top-color: var(--color-orange); }
.plan-note strong { color: var(--color-navy-dark); }

.plan-portfolio {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  align-items: start;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}
@media (min-width: 860px) {
  .plan-portfolio { grid-template-columns: 1.1fr 1fr; gap: var(--space-7); }
}
.plan-portfolio h3 { margin: 0 0 var(--space-3); font-size: 1.2rem; color: var(--color-navy-dark); }
.plan-portfolio p { font-size: var(--text-sm); line-height: 1.65; color: var(--color-ink-muted); margin-bottom: var(--space-4); }
.plan-points { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.plan-points li {
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-ink-muted);
  padding-left: var(--space-4);
  border-left: 2px solid var(--color-blue);
}
.plan-points strong { color: var(--color-navy-dark); }

/* Trade. A different customer who does not want educating, so a tighter, plainer band. */
.trade-band {
  margin-top: var(--space-6);
  background: var(--color-navy-dark);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  color: #fff;
}
.trade-band h3 { color: #fff; font-size: 1.35rem; margin: 0 0 var(--space-4); }
.trade-band > .trade-copy > p { color: rgba(255, 255, 255, 0.78); line-height: 1.65; margin-bottom: var(--space-5); }
.trade-list {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}
.trade-list li {
  font-size: var(--text-sm);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.74);
  padding-left: var(--space-4);
  border-left: 2px solid rgba(61, 169, 222, 0.6);
}
.trade-list strong { display: block; color: #fff; margin-bottom: 2px; }
.trade-band .btn-secondary {
  background: transparent;
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, 0.45);
}
.trade-band .btn-secondary:hover,
.trade-band .btn-secondary:focus-visible {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.75);
}

/* ===================== AREA PAGES AND THE ABOUT STORY =====================
   Shared by ventilation-beckenham / -bromley / -penge and about.html. Deliberately thin: these
   pages are prose, and everything structural they need (.section-pad, .areas-grid, .area-card,
   .areas-chips, .hero-ctas, .btn) already exists.

   NOTE .areas-grid / .area-card are defined twice in this file and the SECOND definition wins —
   it styles the cards for a dark ground. So a card grid on these pages has to sit inside a
   `tint-navy` section or the cards come out as translucent white on white. That is why every
   `.local-stock` block carries `tint-navy`. */

.local-inner,
.story-scope-inner {
  max-width: 720px;
  margin: 0 auto;
}
.local-inner > p,
.story-scope-inner > p {
  color: var(--color-ink-muted);
  font-size: 1.0625rem;
  line-height: 1.75;
  margin-bottom: var(--space-3);
}
.local-inner > h2,
.story-scope-inner > h2 {
  margin-bottom: var(--space-3);
}
.local-lead {
  padding-bottom: var(--space-5);
}
.local-inner strong {
  color: var(--color-ink);
}

/* The buttons at the foot of a prose block. .hero-ctas already handles the row; this is only the
   space above it, which the hero does not need because it has none beneath it. */
.local-ctas {
  margin-top: var(--space-5);
}

/* On a dark section the muted ink is unreadable, and the secondary button disappears entirely. */
.tint-navy .local-inner > p,
.tint-navy .story-scope-inner > p {
  color: rgba(255, 255, 255, 0.82);
}
.tint-navy .local-inner strong { color: #fff; }
.tint-navy .local-ctas .btn-secondary {
  background: transparent;
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, 0.45);
}
.tint-navy .local-ctas .btn-secondary:hover,
.tint-navy .local-ctas .btn-secondary:focus-visible {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.75);
}

/* --- How a job runs, on the About page --- */
.story-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  max-width: 1160px;
  margin: 0 auto;
}
.story-card {
  position: relative;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-soft);
}
.story-card h3 {
  margin: var(--space-2) 0 var(--space-2);
  font-size: 1.125rem;
}
.story-card p {
  color: var(--color-ink-muted);
  font-size: 0.9375rem;
  line-height: 1.7;
  margin: 0;
}
/* The number is the sequence, which is the whole point of the section — four stages in order —
   so it is a real element rather than a ::before, and it is readable rather than decorative. */
.story-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-navy);
  color: #fff;
  font-family: var(--font-heading, inherit);
  font-weight: 700;
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}
.story-chips {
  margin-bottom: var(--space-4);
}
.story-scope {
  padding-top: var(--space-6);
}

/* The three area cards that have a page behind them. On the navy ground a default link colour is
   unreadable, so the heading keeps its own colour and earns an underline on hover instead. */
.area-card h3 a {
  color: inherit;
  text-decoration: none;
}
.area-card h3 a::after {
  content: " \2192";
  opacity: 0.55;
}
.area-card h3 a:hover,
.area-card h3 a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.areas-more a {
  color: inherit;
  font-weight: 600;
  text-underline-offset: 3px;
}

/* Four cards in an auto-fit grid come out three-and-an-orphan on a wide screen. Where a section
   has exactly four, pair them instead. */
@media (min-width: 900px) {
  .areas-grid--pair { grid-template-columns: repeat(2, 1fr); max-width: 900px; }
}
