/* ==========================================================================
   Electrical Exchange - Design System
   Blend of the current EE brand (gold/ink/white, real job photography,
   accreditation badges) with Simple Solar's bolder layout language
   (condensed display type, angular section dividers, illustrated process
   steps, accordion FAQ, circular badge motifs).
   ========================================================================== */

:root {
  /* Brand palette - client brand gold, blended with Simple Solar's cream */
  --gold: #fab200;
  --gold-dark: #d59700;
  --gold-tint: #fef3d9;
  --ink: #121212;
  --ink-soft: #1e1e1e;
  --cream: #faedc3;
  --white: #ffffff;
  --grey-bg: #f6f4ee;
  --text: #1a1a1a;
  --text-muted: #56534b;
  --text-on-ink: #f4f1e8;
  --border: #e8e2d2;

  --font-display: "Outfit", "Segoe UI", "Helvetica Neue", sans-serif;
  --font-body: "Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --container: 1200px;
  --radius-sm: 8px;
  --radius-md: 16px;
  --radius-lg: 28px;
  --shadow-card: 0 12px 30px -14px rgba(18, 18, 18, 0.28);
  --shadow-btn: 0 10px 22px -10px rgba(240, 181, 62, 0.6);
}

/* ---------- Reset ---------- */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--text);
  background: var(--white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.05;
  margin: 0;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
p { margin: 0; }
button { font-family: inherit; cursor: pointer; }

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}

/* ---------- Type scale ---------- */
.h-display { font-size: clamp(2.1rem, 3.8vw, 3.5rem); }
.h-xl { font-size: clamp(2.1rem, 3.6vw, 3rem); }
.h-lg { font-size: clamp(1.6rem, 2.6vw, 2.1rem); }
.h-md { font-size: 1.3rem; }
.lede { font-size: 1.15rem; color: var(--text-muted); max-width: 46em; }

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--gold);
  padding: 7px 16px;
  border-radius: 999px;
  margin-bottom: 18px;
}
.kicker.on-dark { background: rgba(240, 181, 62, 0.16); color: var(--gold); }
.kicker.outline { background: transparent; border: 1.5px solid var(--ink); }
/* No pill shape at all - just coloured label text, for the hero on dark
   imagery where a solid pill reads as too heavy a first element. */
.kicker.plain { background: transparent; padding: 0; color: var(--gold); }

.section-head { max-width: 640px; margin-bottom: 44px; }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }

/* Closing CTA appended to sections that didn't have one of their own */
.section-cta { text-align: center; margin-top: 48px; margin-bottom: 48px; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 700;
  font-size: 0.98rem;
  padding: 15px 30px;
  border-radius: 10px;
  border: 2px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
  white-space: nowrap;
}
/* Keeps "Home Survey" together as a unit wherever the button text wraps
   (mobile sets the button itself to white-space:normal) - inherits nowrap
   here regardless of that. */
.btn-nowrap { white-space: nowrap;
}
.btn-primary {
  background: var(--gold);
  color: var(--ink);
  box-shadow: var(--shadow-btn);
}
.btn-primary:hover { background: var(--gold-dark); transform: translateY(-2px); }
.btn-dark {
  background: var(--ink);
  color: var(--text-on-ink);
}
.btn-dark:hover { background: #000; transform: translateY(-2px); }
.btn-outline {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}
.btn-outline:hover { background: rgba(255, 255, 255, 0.1); }
.btn-outline.on-light { border-color: var(--ink); }
.btn-outline.on-light:hover { background: var(--ink); color: var(--white); }
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; }

/* ==========================================================================
   Header / Navigation
   ========================================================================== */
.topbar {
  background: var(--ink);
  color: var(--text-on-ink);
  font-size: 0.85rem;
  font-weight: 600;
  border-bottom: 1px solid rgba(244, 241, 232, 0.1);
}
.topbar .container {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-top: 9px;
  padding-bottom: 9px;
  gap: 28px;
  flex-wrap: wrap;
}
.topbar-link { font-weight: 700; color: var(--text-on-ink); display: inline-flex; align-items: center; gap: 7px; }
.topbar-link:hover { color: var(--gold); }

/* Flat icon glyphs, used in place of emoji wherever contact details appear */
.icon { width: 14px; height: 14px; flex: none; }

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--ink);
  border-bottom: 1px solid rgba(244, 241, 232, 0.1);
}
.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 0;
  gap: 24px;
}
.logo img { height: 52.5px; width: auto; }
.nav-links {
  display: flex;
  align-items: center;
  gap: 22px;
  font-weight: 600;
  font-size: 0.96rem;
  color: var(--text-on-ink);
}
/* nowrap - since the Service Areas dropdown made this an 8-item nav, a
   shrinking flex item was letting "EV Chargers"/"Service Areas" break onto
   a second line inside their own link box before the row ever reaches the
   900px mobile breakpoint. nowrap + the tighter gap above reclaim enough
   width to keep every label on one line down to that breakpoint. */
.nav-links a, .nav-link-parent { white-space: nowrap; }
.nav-links a { padding: 6px 0; border-bottom: 2px solid transparent; }
.nav-links a:hover, .nav-links a.active { border-color: var(--gold); color: var(--gold); }

/* Between the mobile breakpoint and a full-width desktop, tighten further
   so all 8 nav items + the caret icons keep fitting on one line rather
   than overflowing the header. */
@media (max-width: 1240px) and (min-width: 901px) {
  .nav-links { gap: 14px; font-size: 0.88rem; }
  .nav-row { gap: 14px; }
}

/* ---------- Services nav: merged single dropdown, desktop only ----------
   Desktop replaces the separate Electrical/Solar/EV Chargers nav slots with
   one "Services" dropdown covering all 6 services - mobile keeps the
   original three items as-is (less cramped as a vertical accordion, so no
   need to collapse there). Same display:none swap technique as
   .split-cta-desktop/.split-cta-mobile elsewhere in this file. */
/* .nav-item.nav-service-solo (2 classes) rather than plain .nav-service-solo
   for the Electrical/Solar wrapper divs - this project's recurring
   specificity bug (see [[nav-dropdown-status]]/[[location-pages-status]]):
   the base ".nav-item { display: flex }" rule sits later in this file and
   is equal specificity to a plain ".nav-service-solo", so it was winning on
   source order and leaving both dropdowns visible on desktop regardless.
   The plain EV Chargers <a> has no such competing rule, which is why only
   it was actually hiding. */
.nav-service-solo { display: none; }
.nav-item.nav-service-solo { display: none; }
@media (max-width: 900px) {
  /* Same fix, mirrored: .nav-services-desktop is also a .nav-item, so it
     needs the 2-class bump too, or it loses to the later unscoped
     ".nav-item { display: flex }" and never actually hides on mobile. */
  .nav-services-desktop { display: none; }
  .nav-item.nav-services-desktop { display: none; }
  .nav-service-solo { display: flex; }
  .nav-item.nav-service-solo { display: flex; }
}
.nav-cta { display: flex; align-items: center; gap: 16px; }
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  padding: 6px;
}
.nav-toggle span { width: 26px; height: 3px; background: var(--text-on-ink); border-radius: 2px; }

@media (max-width: 900px) {
  .nav-links { position: fixed; inset: 68px 0 0 0; background: var(--ink); flex-direction: column; align-items: flex-start; padding: 28px 24px; gap: 6px; transform: translateX(100%); transition: transform 0.25s ease; overflow-y: auto; }
  .nav-links.open { transform: translateX(0); }
  .nav-links a { width: 100%; padding: 14px 0; font-size: 1.1rem; border-bottom: 1px solid rgba(244, 241, 232, 0.12); }
  .nav-toggle { display: flex; }
  /* Just logo + hamburger on mobile - the CTA button's nowrap text was also
     the cause of the horizontal-scroll bug below ~360px (it forced .nav-cta
     wider than the viewport), so this fixes both at once. */
  .nav-cta .btn { display: none; }
  .topbar { display: none; }
  .logo { margin-left: 8px; }
  /* Thin gold accent line under the header, "Simple Solar" reference -
     replaces the subtle 1px divider with a deliberate brand-coloured one. */
  .site-header { border-bottom: 4px solid var(--gold); }
}

/* ---------- Nav dropdowns (Electrical / Solar) ----------
   Desktop: hover/focus reveals a floating white card below the parent link
   (main.js's click-toggle is a fallback for touch/keyboard, not the primary
   mechanism). Mobile: the same markup becomes an inline accordion inside the
   dark slide-out menu instead - see the mobile override below. */
.nav-item { position: relative; display: flex; align-items: center; }
/* Measured with Playwright (getBoundingClientRect on the actual rendered
   caret <polyline>): the visible chevron ink ends 7.5px before .nav-item's
   own box edge (4px button padding + the polyline's empty margin inside its
   24x24 viewBox), so the flex `gap` - though genuinely uniform box-to-box -
   reads as ~7.5px wider after "Services"/"Service Areas" than after plain
   text. A prior +6px margin-right attempt guessed the wrong direction and
   made it visibly worse (confirmed by measurement + client feedback); this
   negative margin pulls the next item in to match the plain-text gap
   exactly. Desktop-only, since mobile's .nav-item is width:100% and a
   negative margin there would overflow the slide-out panel. */
@media (min-width: 901px) {
  .nav-item { margin-right: -7.5px; }
}
.nav-link-parent { display: flex; align-items: center; gap: 4px; }
.nav-caret-toggle { display: flex; align-items: center; background: none; border: none; padding: 4px; margin-left: -2px; color: inherit; cursor: pointer; }
.nav-caret { width: 14px; height: 14px; transition: transform 0.15s ease; }
.nav-item:hover .nav-caret, .nav-item:focus-within .nav-caret, .nav-item.open .nav-caret { transform: rotate(180deg); }

.nav-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 14px;
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 10px;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  z-index: 10;
}
.nav-item:hover .nav-dropdown, .nav-item:focus-within .nav-dropdown, .nav-item.open .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* Overrides .nav-links a (same specificity, wins on source order) - the
   floating card needs dark text on white, not the gold-on-dark nav styling. */
.nav-dropdown a { padding: 10px 12px; border-radius: var(--radius-sm); border-bottom: none; color: var(--text); font-weight: 600; font-size: 0.92rem; }
.nav-dropdown a:hover, .nav-dropdown a.active { border-color: transparent; background: var(--grey-bg); color: var(--gold-dark); }

@media (max-width: 900px) {
  /* Label + chevron sit on one row (like the FAQ accordion's question + "+"
     icon); the dropdown itself wraps to its own full-width row once open. */
  .nav-item { flex-direction: row; flex-wrap: wrap; align-items: center; width: 100%; }
  /* .nav-links a already sets width:100% (1 class + 1 element, higher
     specificity than a bare .nav-link-parent) - match it here or this loses
     the cascade despite appearing later, the same bug class flagged
     elsewhere in this file. */
  .nav-links a.nav-link-parent { width: auto; flex: 1 1 auto; }
  .nav-caret-toggle { flex: none; padding: 14px 4px; }
  /* Collapsed inline accordion instead of the desktop floating card - reset
     the desktop reveal mechanism (opacity/visibility/transform) to static
     safe values and gate visibility with max-height + .nav-item.open instead,
     same pattern as the FAQ accordion. */
  .nav-dropdown {
    flex-basis: 100%;
    order: 3;
    position: static;
    background: none;
    box-shadow: none;
    padding: 0 0 0 16px;
    margin: 0;
    min-width: 0;
    max-height: 0;
    overflow: hidden;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: max-height 0.25s ease;
  }
  .nav-item.open .nav-dropdown { max-height: 260px; }
  .nav-dropdown a { padding: 12px 0; border-bottom: none; color: rgba(244, 241, 232, 0.85); font-size: 1rem; }
  .nav-dropdown a:hover, .nav-dropdown a.active { background: none; color: var(--gold); }
}


/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  color: var(--white);
  /* Top padding matches the kicker-to-headline gap (18px kicker margin +
     14px h1 margin = 32px) so the pill sits dead centre between the header
     above and the headline below, instead of floating closer to one side. */
  padding: 32px 0 118px;
  overflow: hidden;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 94%);
}
.hero-media {
  position: absolute;
  inset: -12% 0;
  z-index: 0;
}
.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}
.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(100deg, rgba(10, 10, 10, 0.86) 10%, rgba(10, 10, 10, 0.45) 55%, rgba(10, 10, 10, 0.2) 100%);
}
.hero .container { position: relative; z-index: 2; }
.hero-body { max-width: 640px; }
/* Slim hero variant for service pages - title + subline only, no kicker
   pill/badges/button row, so the generous bottom padding sized for those
   on the homepage would just be excess whitespace here. */
.hero-slim { padding-bottom: 64px; clip-path: none; }
.hero-slim .lede { margin-bottom: 0; }
.hero h1 { margin: 14px 0 20px; }
.hero .lede { color: rgba(244, 241, 232, 0.96); margin-bottom: 34px; }
/* Desktop/mobile copy swap for the homepage hero's intro line - mobile gets
   a much shorter line instead of the full paragraph (see mobile breakpoint). */
.hero-lede-mobile { display: none; }
/* Generic desktop-prose / mobile-bullets swap - long paragraphs that read as
   a natural list (benefits, features) get a mobile-only bullet version
   instead, same display:none/block dual-markup technique as the hero's
   lede swap above. Desktop keeps the flowing paragraph untouched. */
.prose-mobile-list { display: none; }
.prose-mobile-list li { margin-bottom: 8px; padding-left: 20px; position: relative; }
.prose-mobile-list li::before { content: "";  position: absolute; left: 0; top: 0.6em; width: 6px; height: 6px; border-radius: 50%; background: var(--gold-dark); }
.hero-accred-line {
  margin: 0 0 40px;
  font-family: var(--font-body);
  font-size: 0.81rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.92);
}

/* ---------- Hero badge (Grants page only, desktop only) ----------
   Places a small logo card to the right of the hero headline (e.g. the OZEV
   grant logo). .hero-badge is hidden by default/on mobile - the tablet+
   breakpoint below turns .hero-with-badge's container into a row and reveals
   it. Scoped to whichever page opts in via .hero-with-badge; every other
   page's hero is untouched. */
.hero-badge { display: none; }
@media (min-width: 981px) {
  .hero-with-badge .container { display: flex; align-items: center; justify-content: space-between; gap: 48px; }
  .hero-with-badge .hero-body { margin: 0; }
  .hero-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 200px;
    height: 200px;
    background: var(--white);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 24px;
    box-sizing: border-box;
  }
  .hero-badge img { width: 100%; height: 100%; object-fit: contain; }
}

/* ---------- Badge strip (below hero) - brand/manufacturer logos only ---------- */
.badge-strip {
  background: var(--white);
  padding: 30px 0 46px;
  border-bottom: 1px solid var(--border);
}
.badge-strip .container { display: flex; flex-direction: column; gap: 28px; }
.strip-block { display: flex; flex-direction: column; gap: 16px; }
.strip-label {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Manufacturer / brand logos - secondary, greyscale until hovered */
.badge-strip .brand-logos { display: flex; align-items: center; gap: 33px; flex-wrap: wrap; }
.badge-strip .brand-logos img {
  height: 40px;
  width: auto;
  opacity: 0.75;
  filter: grayscale(100%);
  transition: opacity 0.15s ease, filter 0.15s ease;
}
.badge-strip .brand-logos img:hover { opacity: 1; filter: grayscale(0%); }

/* Logo marquee (brand-logos/"Approved installer for" only): continuous loop
   at every width, not just mobile - one line instead of a ragged wrapped
   row now that the strip carries more logos than fit on one static line.
   Both copies (real + clone) sit in one nowrap flex track; animating it
   -50% loops seamlessly since the clone is an exact duplicate of the real
   set. prefers-reduced-motion below swaps this for a manual swipe on the
   single (non-duplicated) row instead. */
.logo-marquee { overflow: hidden; }
.logo-marquee-track { display: flex; width: max-content; }
.logo-marquee-track > [aria-hidden] { display: flex; }
.logo-marquee-track .brand-logos { flex-wrap: nowrap; margin: 0; }
.brand-marquee .logo-marquee-track { gap: 33px; animation: marquee 32s linear infinite; }
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .brand-marquee .logo-marquee-track { animation: none; width: auto; }
  .logo-marquee-track > [aria-hidden] { display: none; }
  .logo-marquee { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}

/* ---------- Intro / trust band (short, sits right under the badge strip) ---------- */
.intro-band {
  padding: 84px 0;
  text-align: center;
}
.intro-band .kicker { letter-spacing: 0.14em; }
.intro-band h2 { max-width: 20ch; margin: 0 auto 24px; }
.intro-band-copy { max-width: 62ch; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; }
.intro-band-copy p { color: var(--text-muted); font-size: 1.05rem; }

/* ==========================================================================
   Sections / layout helpers
   ========================================================================== */
section { padding: 96px 0; }
/* Tight top padding for whichever section sits right after the brand-logo
   strip below the hero - currently Trusted Installer/Location. */
.section-tight-top { padding-top: 64px; }
.section-cream { background: var(--cream); }
.section-grey { background: var(--grey-bg); }
.section-ink { background: var(--ink); color: var(--text-on-ink); }
.tilt-top {
  clip-path: polygon(0 4%, 100% 0, 100% 100%, 0 100%);
  margin-top: -40px;
}
.tilt-bottom {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 96%);
}

/* ---------- Services scroller (Solar / Air Con lead by order, not size) ---------- */
.scroller-wrap { position: relative; }
.scroller {
  display: flex;
  gap: 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: 4px 4px 12px;
  margin: -4px -4px 0;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.scroller::-webkit-scrollbar { display: none; }
.scroller-item {
  flex: 0 0 calc((100% - 2 * 22px) / 3.15);
  scroll-snap-align: start;
}
.scroller-arrow {
  position: absolute;
  top: 42%;
  transform: translateY(-50%);
  z-index: 3;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--white);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: background 0.15s ease, opacity 0.15s ease;
}
.scroller-arrow:hover { background: var(--gold); color: var(--ink); }
.scroller-arrow:disabled { opacity: 0.35; cursor: default; background: var(--ink); color: var(--white); }
.scroller-arrow.prev { left: -23px; }
.scroller-arrow.next { right: -23px; }

/* ---------- Services grid (secondary services) ---------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 26px;
}
.service-card {
  background: var(--white);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform 0.2s ease;
  display: flex;
  flex-direction: column;
}
.service-card:hover { transform: translateY(-6px); }
.service-card .thumb { aspect-ratio: 4/3; overflow: hidden; }
.service-card .thumb img { width: 100%; height: 100%; object-fit: cover; }
.service-card .body { padding: 22px 22px 26px; flex: 1; display: flex; flex-direction: column; }
.service-card h3 { font-size: 1.15rem; margin-bottom: 8px; }
.service-card p { color: var(--text-muted); font-size: 0.95rem; flex: 1; }
.service-card .tag {
  align-self: flex-start;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 10px;
}
.card-link { margin-top: 16px; font-weight: 700; font-size: 0.9rem; display: inline-flex; align-items: center; gap: 6px; }
/* Icon variant of .service-card's .thumb, for grids where individual real
   photos per tile aren't available (e.g. Electrical's 6-tile service list) -
   same card shell, an icon badge instead of a photo. */
.service-card .thumb-icon { aspect-ratio: 4/3; display: flex; align-items: center; justify-content: center; background: var(--grey-bg); }
.service-card .thumb-icon .icon-circle {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: rgba(250, 178, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
}
.service-card .thumb-icon svg { width: 30px; height: 30px; color: var(--gold-dark); }

/* ---------- Feature grid (icon + title + description cards, no photo) ----------
   Base definition for the .feature-grid class already referenced in the
   980px breakpoint below but never defined - used for short benefit/advantage
   lists (e.g. Energy Diverters) and small product-comparison grids. */
.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; }
.feature-grid.cols-2 { grid-template-columns: repeat(2, 1fr); gap: 26px; }
.feature-grid.cols-3 { grid-template-columns: repeat(3, 1fr); gap: 24px; }
/* Inverted-pyramid layout for exactly 5 cards (the Christchurch "Other
   Services" cross-link blocks) - 3 centred on the first row, 2 centred
   beneath, rather than 4 cards + 1 stranded alone on its own row. 6-column
   grid, each card spans 2 columns: row 1's three cards fill it edge to
   edge, row 2's two cards sit offset by one column on each side so they
   land centred under the row above. Reset to source order (plain single
   column) on mobile - see the 980px breakpoint below. */
.feature-grid.pyramid { grid-template-columns: repeat(6, 1fr); }
.feature-grid.pyramid .feature-card:nth-child(1) { grid-column: 1 / span 2; }
.feature-grid.pyramid .feature-card:nth-child(2) { grid-column: 3 / span 2; }
.feature-grid.pyramid .feature-card:nth-child(3) { grid-column: 5 / span 2; }
.feature-grid.pyramid .feature-card:nth-child(4) { grid-column: 2 / span 2; }
.feature-grid.pyramid .feature-card:nth-child(5) { grid-column: 4 / span 2; }
/* display/color/text-decoration are no-ops on the usual <div class="feature-card">
   (already block, already inherits) - only needed so a.feature-card (the
   "Other Services" cross-link cards on location pages) renders as a proper
   clickable card instead of an inline default-styled link. */
.feature-card { display: block; color: inherit; text-decoration: none; background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 28px 24px; transition: transform 0.15s ease, box-shadow 0.15s ease; }
/* Was scoped to a.feature-card only (the cross-link cards on location
   pages) - widened to plain .feature-card (2026-08-26) so the same lift +
   shadow also covers the <div> cards that had no hover at all: Finance's
   "Why Apply" cards, Grants' "Who Qualifies" cards, and Energy Diverters'
   "Advantages"/"Choose Your Diverter" cards. Reuses the site's existing
   hover language rather than inventing a new one. */
.feature-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-card); }
.feature-card .icon-circle {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(250, 178, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  transition: background-color 0.15s ease;
}
.feature-card .icon-circle svg { width: 24px; height: 24px; color: var(--gold-dark); transition: color 0.15s ease; }
/* Colour-change hover (client ask, Energy Diverters section 2 specifically
   - "perhaps change of colour"): icon flips from tinted to solid gold on
   hover. Unscoped like the rule above, so it's a consistent enhancement
   everywhere .feature-card's hover already applies, not just Diverters. */
.feature-card:hover .icon-circle { background: var(--gold); }
.feature-card:hover .icon-circle svg { color: var(--white); }
/* Energy Diverters "Choose Your Diverter" only (client ask: hover the
   products "one at a time") - dims the non-hovered sibling so the hovered
   product reads as singled out, layered on top of the lift/shadow/icon
   hover above. Scoped to .product-showcase, not .feature-card/.feature-grid
   generally - those classes are shared with unrelated grids elsewhere. */
.product-showcase .feature-card { transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease; }
.product-showcase:hover .feature-card:not(:hover) { opacity: 0.55; }
.feature-card h3 { font-size: 1.05rem; margin-bottom: 8px; }
.feature-card p { color: var(--text-muted); font-size: 0.92rem; }

/* ---------- Team cards (About page) ---------- */
.team-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px; }
.team-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 36px 30px; text-align: center; box-shadow: var(--shadow-card); }
.team-avatar {
  width: 112px;
  height: 112px;
  border-radius: 50%;
  background: rgba(250, 178, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 20px;
  overflow: hidden;
}
/* Placeholder silhouette icon until real headshots are supplied - swap the
   inner <svg> for an <img src="..."> when photos are ready, no other markup
   or CSS changes needed, this rule already covers that case. */
.team-avatar img { width: 100%; height: 100%; object-fit: cover; }
.team-avatar-icon { width: 52px; height: 52px; color: var(--gold-dark); }
.team-card h3 { font-size: 1.15rem; margin-bottom: 4px; }
.team-role {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-dark);
  border: 1.5px solid var(--gold-dark);
  border-radius: 999px;
  padding: 4px 12px;
  margin-bottom: 16px;
}
.team-card p { text-align: left; color: var(--text-muted); font-size: 0.92rem; margin-top: 10px; }

/* ---------- Legal content (Privacy Policy / Terms of Service) ----------
   Long-form numbered-section prose - nothing else on the site is a plain
   article, so the sitewide reset (ul{list-style:none}, p{margin:0}, bare
   h1-h4 with no font-size) needs real typography scoped to this wrapper
   rather than touching those base rules for everyone else. */
.legal-content { color: var(--text-muted); }
.legal-content h2 { font-size: 1.25rem; color: var(--text); margin-top: 40px; margin-bottom: 12px; }
.legal-content h2:first-child { margin-top: 0; }
.legal-content p { margin-top: 12px; line-height: 1.65; }
.legal-content p:first-child { margin-top: 0; }
.legal-content ul { list-style: disc; padding-left: 22px; margin-top: 12px; }
.legal-content li { margin-top: 8px; line-height: 1.6; }
.legal-content a { color: var(--gold-dark); text-decoration: underline; }
.legal-content strong { color: var(--text); }

/* ---------- Job showcase (location pages' "Recent Installations" block) ----------
   Side-by-side cards, not a full split row - deliberately terse content (one
   short paragraph per the SOP) reads as padded-out/awkward stretched across a
   half-width split; a card keeps it feeling intentional at 2 items, and scales
   cleanly to 3+ later without a layout change. */
.job-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 26px; }
.job-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; box-shadow: var(--shadow-card); }
.job-card-media { position: relative; }
.job-card-media img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.job-card-tag {
  position: absolute;
  top: 14px;
  left: 14px;
  background: var(--gold);
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 6px;
}
.job-card-body { padding: 22px 24px; }
.job-card-body h3 { font-size: 1.05rem; margin-bottom: 8px; }
.job-card-body p { color: var(--text-muted); font-size: 0.92rem; margin-bottom: 14px; }
.job-card-meta { display: flex; align-items: center; gap: 6px; font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }
.job-card-meta svg { width: 14px; height: 14px; flex: none; color: var(--gold-dark); }

/* ---------- Area chips (location pages' "Areas We Cover" block) ---------- */
.area-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; max-width: 780px; margin: 0 auto; }
/* Matches the .kicker.on-dark pill sitting above it in the same black
   section - gold outline at rest, fills solid gold on hover. */
.area-chip {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--gold);
  background: rgba(250, 178, 0, 0.12);
  border: 1.5px solid var(--gold);
  border-radius: 999px;
  padding: 8px 18px;
  transition: background 0.15s ease, color 0.15s ease;
}
.area-chip:hover { background: var(--gold); color: var(--ink); }

/* ---------- Numbered process steps ---------- */
/* Palette scoped to this section, exposed as custom properties per spec
   rather than touching the sitewide --gold/--ink/--cream tokens (those are
   used across the whole page; this section wants its own slightly
   different, more muted set). */
.process-section {
  /* Referencing the sitewide tokens directly (not duplicate hex values) so
     this section is guaranteed to match the brand palette exactly - e.g.
     the badge fill is pixel-identical to the "Request a FREE Home Survey"
     button, not just a close approximation. */
  --steps-cream: var(--cream);
  --steps-gold: var(--gold);
  --steps-ink: var(--ink);
  --steps-white: var(--white);
  --steps-border: #EADFC4;
  --steps-gap: 24px;
  --steps-badge: 44px;
  background: var(--steps-cream);
}

.steps-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--steps-gap);
  /* Clearance so the badge row (which sits above the cards, see .step-num)
     doesn't collide with the subhead above: 44px .section-head margin +
     66px here, minus the 38px the badge itself extends upward past the
     grid's own top edge, nets exactly 72px from subhead to badge centre. */
  padding-top: 66px;
  position: relative;
}

/* Connector: a real (non-absolute) grid item spanning grid-column 1/5 (the
   full row), then inset by margin so its ends land exactly on badge 1's and
   badge 4's centres. Badges are left-aligned to their card's content column
   (28px padding + 22px badge radius = 50px from the card/column's own left
   edge), not centred on the column, so the two insets aren't symmetric:
   left is that same fixed 50px; right is "one column's width minus 50px",
   since badge 4 sits 50px in from column 4's left edge, not its right.
   Opacity ramps left-to-right to imply progression. */
.steps-connector {
  grid-column: 1 / 5;
  grid-row: 1;
  align-self: start;
  height: 2px;
  margin-top: -39px;
  margin-left: 50px;
  margin-right: calc((100% - 3 * var(--steps-gap)) / 4 - 50px);
  background: linear-gradient(to right, rgba(250, 178, 0, 0.35), rgba(250, 178, 0, 1));
  transform-origin: left center;
  z-index: 0;
  pointer-events: none;
}
/* Explicit columns: the connector above already claims an explicit grid
   area (row 1, all 4 columns), and auto-placed items route around cells an
   explicitly-placed item occupies - so without this the 4 cards would get
   pushed out of row 1 instead of deliberately sharing it with the line. */
.step-card:nth-child(2) { grid-column: 1; grid-row: 1; }
.step-card:nth-child(3) { grid-column: 2; grid-row: 1; }
.step-card:nth-child(4) { grid-column: 3; grid-row: 1; }
.step-card:nth-child(5) { grid-column: 4; grid-row: 1; }

.step-card {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  background: var(--steps-white);
  border-radius: 16px;
  border: 1px solid var(--steps-border);
  box-shadow: 0 1px 3px rgba(28, 28, 28, 0.04), 0 8px 24px rgba(28, 28, 28, 0.05);
  transition: transform 0.18s ease-out, border-color 0.18s ease-out;
}
/* Hover shadow is a separate layered pseudo-element whose OPACITY
   transitions, rather than animating box-shadow's blur/spread directly
   (expensive to animate; opacity crossfade between two pre-rendered shadows
   is cheap and GPU-friendly). */
.step-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.14);
  opacity: 0;
  transition: opacity 0.18s ease-out;
  pointer-events: none;
  z-index: -1;
}
.step-card:hover {
  transform: translateY(-3px);
  border-color: #DCC98A;
}
.step-card:hover::before { opacity: 1; }

/* Badge sits fully above the card, centred, overlapping the connector line;
   the ring uses the section's own background colour so the line reads as
   passing behind the badge rather than through it. */
.step-num {
  position: absolute;
  bottom: calc(100% + 16px);
  /* Left-aligned to the card's content column (matches .step-body's 28px
     padding) rather than centred over the card - same left edge as the
     icon, pill and title below it, matching the mobile layout's alignment. */
  left: 28px;
  width: var(--steps-badge);
  height: var(--steps-badge);
  border-radius: 50%;
  background: var(--steps-gold);
  color: var(--steps-ink);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 4px solid var(--steps-cream);
  z-index: 2;
}
/* Pulse ring - inert by default; only animated on desktop with motion
   allowed (see the animation block further down). */
.step-num::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--steps-gold);
  opacity: 0;
  pointer-events: none;
}

.step-body { display: flex; flex-direction: column; flex: 1; padding: 28px; }

/* Icon: unified 24px/1.5px outline set, each sat in its own 44px gold-tinted
   circle rather than a coloured header band. */
.step-icon-circle {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(250, 178, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  margin-bottom: 16px;
  transition: background-color 0.18s ease-out;
}
.step-card:hover .step-icon-circle { background: rgba(250, 178, 0, 0.22); }
.step-icon { width: 24px; height: 24px; color: var(--steps-gold); }

/* Tag pill demoted - outline only, so it doesn't compete with the gold
   badge for attention. */
.step-tag {
  display: inline-block;
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--steps-ink);
  background: transparent;
  border: 1px solid var(--steps-gold);
  padding: 4px 10px;
  border-radius: 999px;
  margin-bottom: 14px;
}
.step-card h4 { font-size: 1.05rem; margin-bottom: 10px; }
.step-card p { color: var(--text-muted); font-size: 0.9rem; line-height: 1.6; }

/* Centred variant (Grants' "How It Works") - other pages' steps-grid keep
   the default left-aligned card content. */
.steps-grid.center-cards .step-body { align-items: center; text-align: center; }
.steps-grid.center-cards .step-tag { align-self: center; }

/* ---------- Trusted installer / coverage area ---------- */
.coverage-split {
  display: grid;
  grid-template-columns: 1fr 0.9fr;
  gap: 56px;
  align-items: center;
}
/* Grid items default to min-width:auto, which respects a replaced element's
   (the map iframe's) intrinsic content size - without this, once the real
   Google Maps content loads, the iframe refuses to shrink below ~870px and
   forces this whole column (and the page) wider than the viewport once the
   grid stacks to one column on mobile. */
.coverage-split > div { min-width: 0; }
/* Accreditation logos, in place of the old bullet list - full colour, modest size */
.trust-accred { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; margin: 26px 0 32px; }
/* Fixed same-size box per logo (not just same height) - these badges have very
   different natural aspect ratios (MCS is near-square, TrustMark is a wide
   wordmark), so matching height alone still left MCS looking tiny next to it.
   object-fit: contain scales each logo to fill the same footprint instead. */
/* Height-only box, natural aspect ratio - a fixed width+height box here left
   dead transparent space around narrower/taller logos (worse for MCS's
   near-square ratio), which read as a pale rectangle/"pill" behind the logo. */
.trust-accred img { height: 40px; width: auto; object-fit: contain; display: block; }
.coverage-map {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1.5px solid var(--border);
}
.coverage-map iframe {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
}
.coverage-caption {
  text-align: center;
  margin-top: 16px;
  font-size: 0.76rem;
  color: var(--text-muted);
}
/* Mobile-only duplicate CTA (see the 620px breakpoint for its visible
   state) - hidden by default so desktop/tablet only ever show the one
   after the accreditations. */
.coverage-cta-mobile { display: none; }

/* Ecosystem section now ships as a self-contained widget (energy-ecosystem.html,
   inlined into index.html) with its own scoped .eco-widget styles - nothing
   needed here. */

/* ---------- Split media/content ---------- */
/* Image height-matches the full height of the text column (top of
   pill/kicker to bottom of buttons) by default, instead of sitting at its
   own natural height - keeps every split section's photo and text block
   the same height, whatever the source photo's own aspect ratio. Only
   meaningful once the grid is 2 columns - reset to natural height at the
   single-column mobile breakpoint below, where each item's row already
   auto-sizes to its own content. */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: stretch;
}
/* .split-media is position:relative with the img pulled out to
   position:absolute rather than a plain height:100%/height:100% pair on
   both. Height-matching had a real bug in short-text sections (Solar
   Christchurch's "Comprehensive Solar Solutions" / "Key System Components"
   splits): with the img still an in-flow child, its own height:100% against
   an indefinite grid row falls back to the image's *intrinsic aspect
   ratio* (a replaced-element percentage-height rule), which then became
   split-media's shrink-to-fit height and, in a row shorter on text than
   that, inflated the whole row - image ended up taller than the text
   column instead of matching it, the opposite of the intent. Taking the
   img out of flow (position:absolute, inset:0) removes it from that
   calculation entirely: split-media then has zero in-flow content, so the
   row's auto height comes from the text column alone, and align-items:
   stretch resizes split-media (and the absolutely-positioned img filling
   it) to match afterwards. Border-radius/shadow move to the container
   since clipping+shadow on the img itself would get cut by the
   overflow:hidden needed to round an absolutely-positioned image's
   corners. Radius/overflow/shadow are scoped with :has(> img) rather than
   applied to every .split-media - Energy Diverters' "Advantages" split
   puts a .feature-grid of cards (no img) in the media slot instead of a
   photo, and an unconditional rounded/clipped/shadowed box around that
   grid would visibly clip card hover states and add a border that was
   never there before. */
.split .split-media {
  position: relative;
  height: 100%;
}
.split .split-media:has(> img) {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.split .split-media img {
  position: absolute;
  inset: 0;
  height: 100%;
  width: 100%;
  object-fit: cover;
  display: block;
}
/* For product/diagram graphics on a white background (not a real photo) -
   cropping into them looks broken, so show the whole image letterboxed
   within the frame instead of covering/cropping it. */
.split .split-media img.contain { object-fit: contain; background: var(--white); padding: 24px; box-sizing: border-box; }
.split.reverse { grid-template-columns: 1fr 1fr; }
.split.reverse .split-media { order: 2; }
/* Mobile/desktop CTA swap for split sections, same pattern as
   .coverage-cta-desktop/.coverage-cta-mobile below - lets a section show a
   different button arrangement per breakpoint without duplicating markup
   logic. Default (desktop): show the "desktop" variant, hide "mobile". */
.split-cta-mobile { display: none; }

/* ---------- Stat / rating block ---------- */
.rating-block {
  display: flex;
  align-items: center;
  gap: 18px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 20px 26px;
}
.rating-score { font-family: var(--font-display); font-weight: 800; font-size: 2.2rem; }
.rating-stars { color: var(--gold); letter-spacing: 2px; font-size: 1.1rem; }
.rating-meta { font-size: 0.85rem; color: var(--text-muted); }

.review-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin-top: 32px;
}
.review-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px;
}
.review-card .stars { color: var(--gold); margin-bottom: 10px; letter-spacing: 2px; }
.review-card p { font-size: 0.92rem; color: var(--text); margin-bottom: 14px; }
.review-card .who { font-size: 0.82rem; color: var(--text-muted); font-weight: 700; }

/* ---------- Accordion FAQ ---------- */
.accordion { border-top: 1px solid var(--border); }
.accordion-item { border-bottom: 1px solid var(--border); }
.accordion-trigger {
  width: 100%;
  background: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 0;
  text-align: left;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--text);
}
.accordion-icon {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  font-weight: 800;
  transition: transform 0.2s ease;
}
.accordion-item.open .accordion-icon { transform: rotate(45deg); }
.accordion-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease;
}
.accordion-panel p { padding-bottom: 22px; color: var(--text-muted); max-width: 60ch; }

/* ---------- CTA banner ---------- */
/* Was clip-path'd into an angled top edge (client called it "angular"),
   with a negative margin-top pulling it up into the preceding section to
   hide the seam that wedge would otherwise reveal (body's white showing
   through wherever the preceding section wasn't plain white). Client
   wants a straight edge instead (2026-08-26, sitewide - one shared class,
   used unchanged on all 13 pages, so this one rule covers everywhere).
   With the wedge gone there's no seam to hide, so the negative margin
   goes too - it would otherwise just overlap the previous section instead
   of fixing anything. */
.cta-banner {
  background: var(--gold);
  color: var(--ink);
  text-align: center;
  padding: 80px 0;
}
.cta-banner .kicker { background: rgba(18, 18, 18, 0.1); color: var(--white); }
.cta-banner h2 { margin-bottom: 26px; color: var(--white); }
.cta-banner .btn-row { justify-content: center; }
.cta-banner .btn-outline { border-color: var(--white); color: var(--white); }

/* ---------- Gallery grid ---------- */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.gallery-grid a { position: relative; border-radius: var(--radius-sm); overflow: hidden; aspect-ratio: 4/3; display: block; }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; }
.gallery-grid a:hover img { transform: scale(1.06); }

/* Mobile-only large-photo carousel - hidden until the 620px breakpoint,
   where it swaps places with .gallery-grid. */
.gallery-scroller-wrap { display: none; }
.gallery-scroller-item {
  position: relative;
  flex: 0 0 88%;
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  display: block;
}
.gallery-scroller-item img { width: 100%; height: 100%; object-fit: cover; }
.gallery-tag {
  position: absolute;
  left: 12px;
  bottom: 12px;
  background: rgba(18, 18, 18, 0.78);
  color: var(--white);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 5px 10px;
  border-radius: 999px;
}

/* ---------- Contact layout & form (Contact page) ---------- */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: start; }
.social-icon {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(250, 178, 0, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
}
.social-icon svg { width: 20px; height: 20px; color: var(--gold-dark); }

.contact-form-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); padding: 36px 32px; }
.site-form { margin-top: 24px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.form-group label { font-weight: 700; font-size: 0.85rem; color: var(--text); }
.form-group input,
.form-group textarea {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--text);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  width: 100%;
  transition: border-color 0.15s ease;
}
.form-group input:focus,
.form-group textarea:focus { border-color: var(--gold-dark); outline: 2px solid rgba(250, 178, 0, 0.25); outline-offset: 1px; }
.form-checkbox { display: flex; align-items: flex-start; gap: 10px; font-size: 0.82rem; color: var(--text-muted); margin-bottom: 22px; }
.form-checkbox input { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--gold-dark); flex: none; }
.form-submit { width: 100%; justify-content: center; }
/* Hidden by default - main.js reveals this on submit (UI-only for now, no
   backend wired yet; see the guarded #contactForm block in main.js). */
.form-status { margin-top: 16px; padding: 12px 16px; border-radius: var(--radius-sm); background: var(--gold-tint); color: var(--text); font-size: 0.88rem; }
.form-legal { margin-top: 18px; font-size: 0.78rem; color: var(--text-muted); }
.form-legal a { text-decoration: underline; }

/* ---------- Finance / regulatory small print (Finance page) ---------- */
/* No top margin - the wrapping <section class="section-tight-top"> already
   provides the breathing room above; stacking a margin on top of that plus
   the process-section's own bottom padding was leaving ~230px of dead space
   before "Powered By" even started. */
.finance-powered-by { text-align: center; margin: 0 0 40px; }
.finance-powered-by span {
  display: block;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 14px;
}
/* The sitewide reset sets img { display: block }, so the parent's
   text-align: center (which only affects inline content) doesn't centre a
   block-level image - needs its own auto margins. */
.finance-powered-by img { height: 44px; width: auto; object-fit: contain; margin: 0 auto; }
/* Font-size/colour matched to .coverage-caption so all sitewide small-print
   reads consistently. */
.legal-disclaimer { max-width: 780px; margin: 0 auto; font-size: 0.76rem; line-height: 1.6; color: var(--text-muted); text-align: center; }

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { background: var(--ink); color: var(--text-on-ink); padding: 74px 0 28px; }
.footer-top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 48px;
  border-bottom: 1px solid rgba(244, 241, 232, 0.12);
}
.footer-logo img { height: 36px; margin-bottom: 16px; }
.footer-top p { color: rgba(244, 241, 232, 0.7); font-size: 0.9rem; max-width: 30ch; }
.footer-col h5 {
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 16px;
}
.footer-col li { margin-bottom: 10px; font-size: 0.92rem; }
.footer-col a:hover { color: var(--gold); }
.footer-contact li { display: flex; align-items: flex-start; gap: 8px; color: rgba(244, 241, 232, 0.85); font-size: 0.9rem; margin-bottom: 10px; }
.footer-contact li .icon { margin-top: 3px; color: var(--gold); }
.footer-kanda { margin-top: 22px; }
.footer-kanda img { height: 22px; width: auto; filter: brightness(0) invert(1); margin-bottom: 10px; }
.footer-kanda em { display: block; font-size: 0.8rem; font-style: italic; color: rgba(244, 241, 232, 0.7); }
/* Mobile/desktop duplicate, same pattern as .coverage-cta-desktop/mobile -
   desktop shows the original (nested inside the Contact column); mobile
   swaps to this direct .footer-top child instead, so it can be reordered
   to sit below Services independently of Contact's own column order. */
.footer-kanda-mobile { display: none; }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 24px;
  font-size: 0.8rem;
  color: rgba(244, 241, 232, 0.55);
  flex-wrap: wrap;
  gap: 12px;
}

/* ---------- Placeholder containers for later dynamic embeds ---------- */
.embed-placeholder {
  border: 2px dashed rgba(18, 18, 18, 0.25);
  border-radius: var(--radius-md);
  padding: 32px;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.88rem;
  background: repeating-linear-gradient(
    135deg, rgba(18,18,18,0.03), rgba(18,18,18,0.03) 10px, transparent 10px, transparent 20px
  );
}
.embed-placeholder strong { display: block; color: var(--text); font-size: 0.95rem; margin-bottom: 4px; }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 980px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
  .feature-grid, .feature-grid.cols-2, .feature-grid.cols-3, .feature-grid.pyramid { grid-template-columns: 1fr; }
  /* Matches the nth-child selectors' specificity (.feature-grid.pyramid
     .feature-card:nth-child(N), 4 class-level selectors) so this reset
     actually wins - a plain ".feature-grid.pyramid .feature-card" reset
     (3 class-level selectors) loses to those regardless of source order,
     leaving the explicit column-span placement in effect and forcing the
     grid to fall back to implicit columns instead of collapsing to one. */
  .feature-grid.pyramid .feature-card:nth-child(1),
  .feature-grid.pyramid .feature-card:nth-child(2),
  .feature-grid.pyramid .feature-card:nth-child(3),
  .feature-grid.pyramid .feature-card:nth-child(4),
  .feature-grid.pyramid .feature-card:nth-child(5) { grid-column: auto; }
  .team-grid { grid-template-columns: 1fr; }
  .job-grid { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; gap: 40px; }
  .form-row { grid-template-columns: 1fr; }
  .split, .split.reverse { grid-template-columns: 1fr; gap: 32px; }
  /* Text always leads on mobile, image follows - regardless of which side
     the image sits on at desktop width (plain .split vs .split.reverse). */
  .split-media { order: 1; }
  /* The height-matching above only makes sense in the 2-column desktop
     layout - at a single column, forcing the media box to 100% height
     fought its own content (e.g. a button below the image) for space and
     pushed it outside the box. Selector specificity has to match the base
     ".split .split-media" rule (two classes) or this reset silently loses
     the cascade despite being inside the narrower media query - the same
     bug class hit earlier with .split-fill. Let it size to its content
     naturally here instead. */
  .split .split-media { height: auto; }
  /* Recurring specificity bug, again: the radius/overflow/shadow that make
     a photo look like a card live on ".split .split-media:has(> img)" (see
     the base rule), which is MORE specific than the plain ".split
     .split-media" reset above - so overflow/box-shadow were surviving onto
     mobile and wrapping the image *and* the .split-cta-mobile button below
     it in one shared rounded, shadowed box ("white bubble" behind the
     button, reported on solar.html sections 1 and 3, same issue on every
     other service page using this pattern). Match the :has() selector's
     own specificity to actually win the reset. The image gets its own
     dedicated radius/shadow again below - only the shared container-level
     one needed removing. */
  .split .split-media:has(> img) { overflow: visible; box-shadow: none; border-radius: 0; }
  /* A tall portrait source photo at its natural aspect ratio still reads as
     "very long" filling most of a narrow phone screen - cap it to a fixed
     landscape-ish height and let object-fit:cover crop it, same as every
     other photo treatment on the page rather than a one-off per image.
     position:static undoes the desktop position:absolute/inset:0 (the fix
     for the height-matching bug above only applies once the grid is 2
     columns) - back to a normal in-flow image at a fixed height, with its
     own radius/shadow restored since the container no longer clips it. */
  .split .split-media img {
    position: static;
    height: 260px;
    object-fit: cover;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
  }
  /* Finance's intro section only - image is desktop-only per the client's
     request, removed entirely on mobile rather than just reflowed. */
  .finance-intro-split .split-media { display: none; }
  .footer-top { grid-template-columns: 1fr 1fr; }
  .review-grid { grid-template-columns: 1fr; }
  .coverage-split { grid-template-columns: 1fr; gap: 36px; }
  .scroller-item { flex-basis: calc((100% - 22px) / 2.15); }
}
@media (max-width: 620px) {
  .card-grid { grid-template-columns: 1fr; }
  .scroller-item { flex-basis: 78%; }
  .scroller-arrow { display: none; }
  .gallery-grid { display: none; }
  .gallery-scroller-wrap { display: block; }
  /* Footer reads logo -> description -> contact -> nav/services links on
     mobile - everything stays left-aligned as normal, only the logo block
     itself is centred and sized up. Top padding trimmed so the logo sits
     closer to the section above it. */
  .site-footer { padding-top: 48px; }
  .footer-top { grid-template-columns: 1fr; }
  .footer-logo { order: 1; display: flex; flex-direction: column; align-items: center; text-align: center; }
  .footer-logo img { height: 63px; margin-bottom: 18px; }
  /* Description reads left-aligned while the logo image above it stays
     centred - override just the paragraph's own alignment within the
     centred flex column. */
  /* max-width:none overrides the sitewide .footer-top p { max-width: 30ch }
     rule (same specificity, so it wouldn't otherwise lose the cascade) -
     the paragraph fills the column's full width (inset naturally by
     .container's side padding), left-aligned rather than centred like the
     logo image above it. */
  .footer-logo p { margin: 0; text-align: left; align-self: flex-start; width: 100%; max-width: none; }
  /* Every footer-top child needs an explicit order once any of them has
     one - unordered items default to 0, which would jump them ahead of
     the logo (order:1) instead of falling in after it. */
  .footer-col { order: 3; }
  .footer-col:has(.footer-contact) { order: 2; }
  /* Kanda/Credit Cards relocates below Services on mobile - hide the
     original copy nested in Contact, show the standalone mobile copy
     instead (order 4, after the Navigation/Services pair at order 3). */
  .footer-col .footer-kanda { display: none; }
  .footer-kanda-mobile { display: block; order: 4; }
  .hero { padding: 32px 0 88px; }
  /* Homepage hero only - equal top/bottom padding so the content block
     sits centred (shifted down from the top-heavy default) instead of the
     generic .hero value shared with the service-page slim hero. */
  .hero-home { padding: 45px 0 86px; }
  .hero-slim { padding-bottom: 0; }
  /* Section immediately following a slim service-page hero - tighter top
     padding than the sitewide default so the pill sits close under the
     hero instead of floating in a large gap (mirrors .section-tight-top's
     purpose for the same problem after the homepage's full hero). Matches
     the kicker's own 18px margin-bottom so hero->pill and pill->heading
     read as equal gaps either side of the pill. */
  .hero-follow { padding-top: 18px; }
  section { padding: 64px 0; }
  .section-tight-top { padding-top: 42px; }
  .hero-stats { gap: 22px; }
  .hero-badges { margin-top: 40px; gap: 20px; }

  /* Homepage hero decluttered on mobile ("Simple Solar" reference): short
     one-line intro instead of the full paragraph, and the certifications
     line dropped - the same credentials already show elsewhere on the page
     (Your Local Team, footer), so nothing is actually lost. The kicker
     text is back (plain, no pill) above the headline - .hero-home's top
     padding is reduced by the kicker's own height so the headline itself
     stays exactly where it was without it. */
  .hero-lede-desktop { display: none; }
  .hero-lede-mobile { display: block; }
  .prose-desktop { display: none; }
  .prose-mobile-list { display: block; }
  .hero-accred-line { display: none; }

  /* Top padding was inheriting the generic 64px `section` rule while the
     bottom was trimmed to 40px, so the kicker sat noticeably further from
     the top of the section than the buttons sat from the bottom. Matching
     both to 40px makes the gap equal top and bottom. */
  .cta-banner { padding: 40px 0; }
  /* Sized so each half of the heading's manual <br> break fits on its own
     line (2 lines total) at every mobile width, instead of each half
     wrapping again into 4 lines total. */
  .cta-banner h2 { font-size: clamp(0.95rem, 4.5vw, 1.15rem); }

  /* Hero + CTA-banner button pairs sit side by side rather than stacking -
     tighter padding/font is what buys the room for both to fit with the
     full phone number showing. */
  .hero .btn-row, .cta-banner .btn-row { flex-wrap: nowrap; }
  .hero .btn-row .btn, .cta-banner .btn-row .btn {
    flex: 1;
    padding: 12px 10px;
    font-size: 0.78rem;
    white-space: normal;
    text-align: center;
    line-height: 1.3;
    gap: 4px;
  }
  .btn-call-icon { margin-right: -2px; }
  /* .btn defaults to white-space: nowrap, fine for short button text - but a
     longer .section-cta button (e.g. Grants' "Ready to claim your £350? Get
     a FREE Survey") overflows the container at mobile widths with no wrap
     allowed. Let it wrap instead; buttons short enough to already fit on one
     line are unaffected. */
  .section-cta .btn { white-space: normal; text-align: center; }

  /* "Your Local Team": accreditation logos shrink and tighten up so all
     four fit on one static line (no carousel needed here), and the three
     gaps in this column - paragraph, accreditations, map - match at 28px.
     The CTA button relocates to after the map instead of after the
     accreditations (see .coverage-cta-desktop/.coverage-cta-mobile).
     Height (not gap) is what has to give here - NAPIT/TrustMark are wide
     wordmarks at their natural ratio, so at 33px tall the row runs past a
     360-390px viewport and wraps to 2 lines; 26px keeps all four on one
     line with room to spare down to ~360px. */
  .trust-accred { gap: 6px; margin: 28px 0 0; }
  .trust-accred img { width: auto; height: 26px; }
  .coverage-split { gap: 28px; }
  .coverage-cta-desktop { display: none; }
  .coverage-cta-mobile { display: flex; width: fit-content; margin: 28px auto 0; }

  /* Split sections: same desktop/mobile CTA swap - mobile shows a single
     button left-aligned under the image (inside .split-media, so it's
     already positioned there in the markup), desktop shows whatever
     buttons the section defines (see .split-cta-desktop usage per page). */
  .split-cta-desktop { display: none; }
  /* Gap above the button matches the section's own bottom padding (64px)
     below it, so photo -> button -> section end reads as three equal
     steps rather than a tight gap followed by a loose one. */
  .split-cta-mobile { display: flex; width: fit-content; margin: 64px auto 0; }

  /* Our Services / Help & Answers use the sitewide .section-head.center
     utility (centred at every width) - left-align it here for mobile only,
     without touching the shared class itself (desktop keeps it centred).
     The CTA buttons in these sections are separate .section-cta siblings,
     not nested inside .section-head, so they stay centred untouched. */
  .section-head.center { text-align: left; margin-left: 0; margin-right: 0; }
  /* Energy Ecosystem heading/intro left-align is set inside index.html's own
     inline <style> block (the widget is self-contained per its own comments,
     and its unscoped .eco-widget h2 / .eco-intro rules there load after this
     stylesheet, so an equal-specificity override here would silently lose
     the cascade). See the @media (max-width: 640px) block in index.html. */
}

/* ---------- Process steps: responsive (own breakpoints - 1100px / 700px) ---------- */
@media (max-width: 1100px) {
  /* Tablet: 2x2. The connector only reads as "step 1 through step 4" as a
     single row - once cards wrap to two rows it would either span the
     wrong pair or float meaninglessly, so it's dropped rather than adapted. */
  .steps-grid { grid-template-columns: repeat(2, 1fr); }
  .steps-connector { display: none; }
  .step-card:nth-child(n) { grid-column: auto; grid-row: auto; }
}
@media (max-width: 700px) {
  /* Mobile: vertical timeline rather than a plain stack - line runs down
     the left at x=18px (badge centre), badges stacked along it, cards
     offset right to clear them. Same grid-span technique as desktop,
     rotated: span rows 1-3 (+1 gap) instead of columns 1-3 (+1 gap).
     Gutter/badge trimmed down from the original 60px/44px so the card text
     column isn't fighting the timeline for room on a 320-375px screen. */
  .steps-grid {
    grid-template-columns: 1fr;
    gap: var(--steps-gap);
    padding-left: 44px;
    /* The 66px top clearance above is only needed to keep the badge row
       (floating above the card row) clear of the subhead - the mobile
       timeline's badge 1 sits inside/beside card 1 instead, so it doesn't
       need the extra room. */
    padding-top: 0;
  }
  .step-card:nth-child(n) { grid-column: 1; }
  .step-card:nth-child(2) { grid-row: 1; }
  .step-card:nth-child(3) { grid-row: 2; }
  .step-card:nth-child(4) { grid-row: 3; }
  .step-card:nth-child(5) { grid-row: 4; }
  .step-body { padding: 22px 18px; }

  .steps-connector {
    display: block;
    grid-column: 1;
    grid-row: 1 / 4;
    justify-self: start;
    align-self: start;
    width: 2px;
    height: calc(100% + var(--steps-gap));
    margin: 0;
    background: linear-gradient(to bottom, rgba(250, 178, 0, 0.35), rgba(250, 178, 0, 1));
    /* -26px: shifts the line from the column's start (x=44, the grid's
       padding-left) back to x=17 (centres a 2px line on x=18). 42px: down
       to each badge's vertical CENTRE (top:24px + half its own 36px height),
       so the line's ends land exactly on badge 1 and badge 4's centres, not
       their top edges. */
    transform: translate(-26px, 42px);
  }

  .step-num {
    bottom: auto;
    left: -44px;
    top: 24px;
    width: 36px;
    height: 36px;
    font-size: 0.95rem;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-media img { transform: none !important; }
}

/* ---------- Process steps: scroll-reveal + pulse, desktop only ---------- */
@media (min-width: 1101px) and (prefers-reduced-motion: no-preference) {
  .step-card { opacity: 0; transform: translateY(16px); }
  .step-num { transform: scale(0.8); }
  .steps-connector { transform: scaleX(0); }

  .step-card {
    transition: opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1), transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .step-num { transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1); }
  .steps-connector { transition: transform 0.9s ease-out; }

  .steps-grid.is-visible .step-card { opacity: 1; transform: translateY(0); }
  .steps-grid.is-visible .step-num { transform: scale(1); }
  .steps-grid.is-visible .steps-connector { transform: scaleX(1); }

  /* Card N starts 100ms after the connector, staggered 90ms apart; each
     card's own badge starts 60ms ahead of its card, so the badge feels
     like it's placing the card down rather than arriving with it. */
  .steps-grid.is-visible .step-card:nth-child(2) { transition-delay: 0.1s; }
  .steps-grid.is-visible .step-card:nth-child(3) { transition-delay: 0.19s; }
  .steps-grid.is-visible .step-card:nth-child(4) { transition-delay: 0.28s; }
  .steps-grid.is-visible .step-card:nth-child(5) { transition-delay: 0.37s; }
  .steps-grid.is-visible .step-card:nth-child(2) .step-num { transition-delay: 0.04s; }
  .steps-grid.is-visible .step-card:nth-child(3) .step-num { transition-delay: 0.13s; }
  .steps-grid.is-visible .step-card:nth-child(4) .step-num { transition-delay: 0.22s; }
  .steps-grid.is-visible .step-card:nth-child(5) .step-num { transition-delay: 0.31s; }

  /* Badge pulse - a signal travelling down the line, once the reveal has
     settled. Card 4 finishes at 370ms delay + 500ms duration = 870ms;
     pulses start 150ms after that, 140ms apart, 550ms each, so the whole
     sequence (first movement to end of pulse) lands at ~1.99s. */
  .steps-grid.is-visible .step-num::after {
    animation: steps-pulse 0.55s ease-out 1 both;
  }
  .steps-grid.is-visible .step-card:nth-child(2) .step-num::after { animation-delay: 1.02s; }
  .steps-grid.is-visible .step-card:nth-child(3) .step-num::after { animation-delay: 1.16s; }
  .steps-grid.is-visible .step-card:nth-child(4) .step-num::after { animation-delay: 1.3s; }
  .steps-grid.is-visible .step-card:nth-child(5) .step-num::after { animation-delay: 1.44s; }
}
@keyframes steps-pulse {
  from { transform: scale(1); opacity: 0.5; }
  to { transform: scale(1.6); opacity: 0; }
}
