/* ==========================================================================
   CLEAN POWER AUSTRALIA — THEME STYLES
   Layout, components and utilities for the coded rebuild.
   Depends on cpa-tokens.css. Never hard-code a hex here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1 · RESET + BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg-page);
  color: var(--fg-primary);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
a { color: var(--cpa-blue-deep); }
/* Hover darkens rather than brightens: brand blue on white is 3.37:1 and a
   link is body-sized text. */
a:hover { color: var(--cpa-blue-hover); }

h1, h2, h3, h4, h5, h6 { margin: 0; text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------------------------------
   2 · LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--wide { max-width: var(--wrap-wide); }
.wrap--text { max-width: calc(var(--wrap-text) + (var(--gutter) * 2)); }

/* Long-form content container.
   Full container width, so headings and blocks start on exactly the same left
   edge as the split sections and galleries either side of them. Running text
   is capped separately: a 1440px paragraph is around 150 characters a line,
   which is unreadable. Left edge governs alignment, measure governs
   readability, and they are different problems. */
.wrap--prose {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--prose > * { max-width: 100%; margin-inline: 0; }
/* Headings are short by nature and do not need a cap. Capping them made
   "The jobs, as they actually happened." wrap onto two lines for no reason. */
.wrap--prose .cpa-h1,
.wrap--prose .cpa-h2,
.wrap--prose .cpa-h3 { max-width: 100%; }
.wrap--prose p,
.wrap--prose li,
.wrap--prose .cpa-lead { max-width: 82ch; }

/* Running prose gets its own cap regardless of container width. Roughly
   70 characters a line, which is where readability sits. */
.wrap--prose .prose { max-width: 100%; }
.prose,
.measure { max-width: var(--wrap-text); }

/* Section heading block: one consistent measure and rhythm, so headings that
   are centred and headings that are left-aligned still share a shape.

   Centred heads get a wider measure than the 56ch default. A centred block
   constrained to 56ch wraps a short headline onto three lines while the
   container beside it sits half empty, which reads as cramped rather than
   composed. The lead paragraph keeps the narrower measure, because that is
   running text and 56ch is where it stays readable. */
.section__head { max-width: 56ch; }
.wrap--prose .section__head { max-width: 100%; }
.section__head.center { margin-inline: auto; max-width: 72ch; }
.section__head.center .cpa-h2,
.section__head.center .cpa-h1 { max-width: 22ch; margin-inline: auto; }
.section__head.center .cpa-lead { max-width: 58ch; margin-inline: auto; }
.section__head > * + * { margin-top: var(--space-4); }

/* Stat items align to the top of their cell and share a baseline for the
   number, so a one-line label and a two-line label do not shunt the figures
   out of line with each other. */
.stat {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.stat__label { margin-top: var(--space-2); }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) * 0.6); }
/* Two body backgrounds only: white and a single quiet tint. The stronger
   blue wash is reserved for heroes and CTA bands, so a colour change in the
   body of a page always means the same thing. */
.section--cream,
.section--band { background: var(--cpa-band); }
.section--paper { background: var(--cpa-paper); }
.section--mist  { background: var(--cpa-band); }
.section--wash  { background: var(--cpa-blue-wash); }
.section--ink   { background: var(--cpa-ink); color: var(--cpa-paper); }
.section--ink h1, .section--ink h2, .section--ink h3 { color: var(--cpa-paper); }

.grid { display: grid; gap: var(--gap-grid); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 1024px) {
  .grid--3, .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
}

.stack { display: flex; flex-direction: column; }
.stack > * + * { margin-top: var(--space-4); }
.stack--lg > * + * { margin-top: var(--space-8); }

.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.center { text-align: center; margin-inline: auto; }

/* --------------------------------------------------------------------------
   3 · TYPOGRAPHY
   -------------------------------------------------------------------------- */
.cpa-display, .cpa-h1, .cpa-h2, .cpa-h3 {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: var(--tracking-display);
  color: inherit;
}
.cpa-display { font-size: var(--fs-display); line-height: var(--lh-display); }
.cpa-h1 { font-size: var(--fs-h1); line-height: 1.05; }
.cpa-h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); }
.cpa-h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); }

.cpa-h4 {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-h4);
  line-height: var(--lh-tight);
}

.cpa-eyebrow {
  display: block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--cpa-blue-deep);
  margin-bottom: var(--space-3);
}

.cpa-lead { font-size: var(--fs-lead); line-height: 1.55; color: var(--fg-secondary); }
/* Ink-3 (#787A78) measures 4.33:1 on white and fails AA at body size.
   Meta text uses ink-2 (6.45:1) instead. Ink-3 is reserved for decorative
   placeholder captions where it is not conveying information. */
.cpa-meta { font-size: var(--fs-meta); line-height: 1.5; color: var(--cpa-ink-2); }
/* Accent words sit inside display headings, sometimes on white and sometimes
   on the blue wash. #0096CC managed only 2.93:1 on the wash. */
.cpa-accent--blue { color: var(--cpa-blue-action); }
.cpa-accent--yellow { color: var(--cpa-yellow); }

/* Signature underline. Drawn in CSS so it needs no image asset. */
.cpa-underline {
  position: relative;
  display: inline-block;
  padding-bottom: 0.08em;
}
.cpa-underline::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 0.14em;
  border-radius: 999px;
  background: var(--cpa-yellow);
}

/* --------------------------------------------------------------------------
   4 · BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 19px;
  line-height: 1;
  letter-spacing: -0.01em;
  padding: 15px 32px;
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition-base), color var(--transition-base), transform 120ms ease-in;
}
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: 2px solid var(--cpa-blue); outline-offset: 3px; }

/* Specificity note: Elementor's kit stylesheet ships `.elementor-kit-6 a`
   (0,1,1), which outranks a single `.btn--primary` class (0,1,0) and was
   repainting button labels in body ink. Doubling the class to
   `.btn.btn--primary` (0,2,0) puts these back in front without !important. */
.btn.btn--primary { background: var(--cpa-blue-action); color: var(--fg-on-blue); }
.btn.btn--primary:hover,
.btn.btn--primary:focus { background: var(--cpa-blue-hover); color: var(--fg-on-blue); }

.btn.btn--secondary { background: transparent; color: var(--cpa-ink); border-color: var(--cpa-blue); }
.btn.btn--secondary:hover,
.btn.btn--secondary:focus { background: rgba(0, 150, 204, 0.08); color: var(--cpa-ink); }

.btn.btn--white { background: var(--cpa-paper); color: var(--cpa-ink); box-shadow: var(--shadow-cta); }
.btn.btn--white:hover,
.btn.btn--white:focus { background: var(--cpa-blue-wash); color: var(--cpa-ink); }

.btn--sm { font-size: 16px; padding: 12px 24px; }

.link-arrow,
a.link-arrow {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-weight: 700; text-decoration: none; color: var(--cpa-blue-deep);
}
.link-arrow:hover,
a.link-arrow:hover { color: var(--cpa-blue-hover); }
.link-arrow svg { transition: transform var(--transition-base); }
.link-arrow:hover svg { transform: translateX(3px); }

@media (max-width: 1024px) { .btn { padding: 14px 28px; } }
@media (max-width: 767px)  { .btn { padding: 12px 24px; font-size: 17px; } }

/* --------------------------------------------------------------------------
   5 · CARDS, CHIPS, MEDIA
   -------------------------------------------------------------------------- */
/* Cards fill their grid cell and lay out as a column, so a row of cards with
   different amounts of text still has its buttons and links on one line
   rather than stepping up and down with the copy above them. */
.card {
  background: var(--bg-card);
  border: 1px solid var(--cpa-line);
  border-radius: var(--radius-card);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  height: 100%;
}
.card > .cluster:last-child,
.card > .stack > .cluster:last-child,
.card > div:last-child:has(.btn),
.card__body > div:last-child:has(.btn) { margin-top: auto; padding-top: var(--space-5); }

.card--link { transition: box-shadow var(--transition-base), transform var(--transition-base); }
.card--link:hover { box-shadow: var(--shadow-card); transform: translateY(-2px); }
.card--flush { padding: 0; overflow: hidden; }
.card__media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--cpa-blue-mist); flex: none; }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__body { padding: var(--space-6); display: flex; flex-direction: column; flex: 1; }

/* Grid children stretch by default, but only if nothing overrides it. */
.grid > * { min-width: 0; }

.chip {
  display: inline-flex; align-items: center;
  padding: 5px 13px; border-radius: var(--radius-pill);
  background: var(--cpa-blue-wash); color: var(--cpa-blue-deep);
  font-size: 12.5px; font-weight: 700; line-height: 1.4;
}
.chip--outline { background: transparent; border: 1px solid rgba(0, 150, 204, 0.4); }

.step {
  width: 64px; height: 64px; border-radius: 50%; flex: none;
  background: var(--cpa-blue-action); color: var(--fg-on-blue);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 500; font-size: 26px;
}

.stars { color: var(--cpa-yellow); letter-spacing: 2px; font-size: 18px; }

/* Radio and checkbox groups rebuilt as <fieldset> by cpa.js so the grouping is
   native rather than ARIA-only. A fieldset carries a browser default border,
   padding and margin, and legend has its own indent, none of which the <div>
   it replaces had. Reset them and restore the flex layout Elementor's column
   classes expect, so the rebuild is invisible.
   min-width:0 is not optional: fieldset defaults to min-content sizing, which
   would stop the field group shrinking inside the form grid. */
.cpa-fieldset {
  display: flex;
  flex-wrap: wrap;
  border: 0;
  margin: 0 0 15px;
  padding: 0 7.5px;
  min-width: 0;
}
.cpa-fieldset > legend {
  display: block;
  float: none;
  width: 100%;
  padding: 0;
  margin: 0;
}
/* --cpa-yellow is tuned for dark surfaces (hero rating badge, form trust row)
   where it passes comfortably. On the pale mist band it renders at 1.56:1
   against #EDF4F7, which WAVE reports as a very low contrast error on
   /about-us/ and /case-studies/. Star glyphs are real text nodes, so the 4.5:1
   text bar applies even though the block is aria-hidden and the rating is
   carried by the adjacent quote. #8F5E00 measures 4.70:1 on #EDF4F7.
   Scoped to the mist band so the dark-surface stars keep the brighter gold. */
.section--mist .stars { color: #8F5E00; }

.stat__num {
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--fs-h2); line-height: 1.1; color: var(--cpa-blue);
  letter-spacing: var(--tracking-display);
  display: flex; align-items: baseline; gap: 2px;
  font-variant-numeric: tabular-nums;
}
/* Suffix inherits --cpa-blue from .stat__num, but 0.55em of --fs-h2 lands at
 * 23.1px, which is 0.9px under the 24px WCAG large-text threshold. The parent
 * clears 3:1 as large text; the suffix needs 4.5:1 and #0096CC gives 3.36:1.
 * Action blue is 7.10:1. The number itself stays brand blue deliberately. */
.stat__suffix { font-size: 0.55em; line-height: 1; align-self: flex-start; margin-top: 0.18em; color: var(--cpa-blue-action); }
.stat__label { font-size: 15px; color: var(--fg-secondary); max-width: 24ch; }

/* Stat cards sit on a hairline rather than the heavy underline bars the old
   site used, which read as progress meters and pulled the eye off the number. */
.stat-band .stat {
  padding: var(--space-6) var(--space-5);
  background: var(--cpa-paper);
  border: 1px solid var(--cpa-line);
  border-radius: var(--radius-card);
}
@media (max-width: 767px) {
  .stat-band .stat { padding: var(--space-5); }
}

/* Product renders are portrait on white, so they need containing rather than
   cropping. Photography still uses cover. */
.card__media--product {
  background: var(--cpa-blue-mist);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}
.card__media--product img { width: 100%; height: 100%; object-fit: contain; }

.review-badge { display: flex; flex-direction: column; gap: 2px; }

.logo-strip { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-10); }
.logo-strip img { max-height: 44px; width: auto; opacity: 0.85; }

/* --------------------------------------------------------------------------
   6 · HEADER
   -------------------------------------------------------------------------- */
/* Announcement bar.
   Pale wash rather than a saturated fill. At this tint the bar reads as a
   quiet notice instead of a second CTA, and ink on #D8F4FF measures 11.4:1
   so the type can sit at 14px with plenty of headroom. The link uses deep
   blue (5.2:1 on the wash) so it is obviously clickable without shouting.

   Amber was trialled and rejected: it works, but yellow does precise work
   elsewhere on the site (underlines, star rows) and a full-width band spends
   that accent. */
.announce {
  background: var(--cpa-blue-wash);
  color: var(--cpa-ink);
  text-align: center;
  padding: 10px var(--gutter);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.45;
  border-bottom: 1px solid rgba(0, 107, 144, 0.14);
}
.announce__inner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 9px;
}
.announce__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--cpa-blue);
  flex: none;
}
.announce a {
  display: inline-block;
  padding-block: 4px;                 /* 19px -> 27px, WCAG 2.5.8 */
  color: var(--cpa-blue-deep);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  white-space: nowrap;
}
.announce a:hover { color: var(--cpa-ink); }

@media (max-width: 767px) {
  .announce { font-size: 13px; padding: 9px var(--gutter); }
  .announce__dot { display: none; }
}

.site-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--cpa-paper);
  border-bottom: 1px solid var(--cpa-line);
}

/* Logged-in admins get a 32px WordPress bar pinned above everything. Without
   this the sticky header slides underneath it and the CTA is half covered. */
body.admin-bar .site-header { top: 32px; }
@media screen and (max-width: 782px) {
  body.admin-bar .site-header { top: 46px; }
}

/* The embedded Elementor form template carries its own padded, bordered
   container. Inside .form-panel that renders as a box within a box, so the
   inner chrome is stripped and the panel supplies the frame. */
.form-panel .elementor .e-con,
.form-panel .elementor .e-con-inner {
  background: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  padding: 0 !important;
  border-radius: 0 !important;
  --padding-top: 0; --padding-bottom: 0;
  --padding-left: 0; --padding-right: 0;
}
.form-panel .elementor-widget-heading .elementor-heading-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 26px;
  line-height: 1.25;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-ink);
}
.site-header__inner {
  display: flex; align-items: center; flex-wrap: nowrap; gap: 24px;
  min-height: 96px; padding-block: 16px;
}
.site-header__logo { flex: none; display: flex; margin-left: var(--edge-indent); }
.site-header__logo img { height: 52px; width: auto; }

/* Logo left, nav centred in the space between, phone and CTA right.
   The auto margins on the nav are what spread things out: without them
   everything after the logo bunches against the right edge.

   Nav links, the phone number and the CTA all share weight 600. Previously
   the phone was 700 and the nav 600, which made the number shout slightly
   louder than the navigation for no reason. */
.site-header__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: 26px;
  /* Matches the hero form panel's inner padding, so the CTA's right edge
     lands on the same axis as the submit button inside the panel below. */
  margin-right: var(--panel-pad);
}
.site-header__phone {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  font-size: 16px; font-weight: 600; color: var(--cpa-ink);
  text-decoration: none; white-space: nowrap;
}
.site-header__phone:hover { color: var(--cpa-blue); }
.site-header__cta {
  white-space: nowrap; flex: none;
  font-size: 16px; font-weight: 600; padding: 13px 26px;
}

/* Primary nav */
.nav { min-width: 0; flex: none; margin-inline: auto; }
.nav ul { list-style: none; display: flex; align-items: center; flex-wrap: nowrap; gap: 34px; }
.nav li { position: relative; }
.nav a,
.nav .nav-parent {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 16px; font-weight: 600; color: var(--cpa-ink);
  text-decoration: none; padding-block: 8px; white-space: nowrap;
  transition: color var(--transition-base);
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-body); line-height: inherit;
}
.nav .nav-parent:hover { color: var(--cpa-blue); }
.nav .menu-item-has-children > .nav-parent::after {
  content: ""; width: 9px; height: 9px; flex: none;
  border-right: 2.4px solid var(--cpa-blue);
  border-bottom: 2.4px solid var(--cpa-blue);
  transform: rotate(45deg) translateY(-2px);
}
/* Current-page nav items are 16px regular, so they need 4.5:1, not 3:1.
   Hover is included because it is a persistent state a mouse user reads. */
.nav a:hover,
.nav .current-menu-item > a,
.nav .current-menu-ancestor > a { color: var(--cpa-blue-deep); }
.nav .menu-item-has-children > a::after {
  content: ""; width: 9px; height: 9px; flex: none;
  border-right: 2.4px solid var(--cpa-blue);
  border-bottom: 2.4px solid var(--cpa-blue);
  transform: rotate(45deg) translateY(-2px);
}
/* Items without a dropdown reserve the same space the chevron occupies
   (9px arrow plus the 5px flex gap), so every top-level item has an equal
   footprint and the childless one does not read as smaller. */
.nav > ul > li:not(.menu-item-has-children) > a { padding-right: 14px; }
/* Dropdown panels.
   Visibility rather than display: with three nested levels and a grid on the
   Locations panel, display toggling means a base rule, a hover rule and a
   third-level rule all competing for the same property, which is how panels
   end up stuck open. visibility/opacity separates "is it laid out" from
   "can you see it", so nothing has to fight.

   The ::before strip is an invisible bridge that keeps the panel open while
   the pointer travels down from the nav item. */
.nav .sub-menu {
  position: absolute; top: 100%; left: 0; z-index: 70;
  display: flex; flex-direction: column; gap: 2px;
  min-width: 250px; padding: 10px;
  background: var(--cpa-paper);
  border: 1px solid var(--cpa-line);
  border-top: 3px solid var(--cpa-blue);
  border-radius: 0 0 14px 14px;
  box-shadow: 0 18px 40px rgba(48, 49, 48, 0.16);
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px);
  transition: opacity 140ms ease-out, transform 140ms ease-out, visibility 140ms;
}
.nav > ul > li > .sub-menu::before {
  content: ""; position: absolute; left: 0; right: 0; top: -14px; height: 14px;
}

/* Only top-level items open on hover. */
.nav > ul > li:hover > .sub-menu,
.nav > ul > li:focus-within > .sub-menu,
.nav > ul > li.is-open > .sub-menu {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

.nav .sub-menu a {
  display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; border-radius: 9px; width: 100%;
  font-weight: 500; font-size: 15px; color: var(--cpa-ink);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.nav .sub-menu a:hover { background: var(--cpa-blue-mist); color: var(--cpa-blue-deep); }

/* --- Locations mega panel -------------------------------------------------
   Structure is Locations > NSW > Greater Sydney, so each state becomes a
   column: heading on top, its regions beneath.

   Positioning note: `.nav li { position: relative }` is 0,1,1, so a bare
   `.menu-item-704 { position: static }` (0,1,0) loses to it and the panel
   anchors to the narrow menu item instead, running off the left of the
   screen. The override has to match on specificity, and `.nav` becomes the
   containing block so the panel lines up with the right edge of the nav. */
.nav { position: relative; }
.nav > ul > li.menu-item-704 { position: static; }
.nav > ul > li.menu-item-704 > .sub-menu {
  left: auto; right: 0;
  width: min(780px, calc(100vw - 48px));
  padding: var(--space-6) var(--space-5) var(--space-5);
  border-radius: 0 0 16px 16px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-2) var(--space-5);
  align-items: start;
}

/* State headings: grouping labels, not destinations.

   The selectors below carry `.nav-group` as well as `a`. They used to match
   only `a`, but the walker renders a non-destination heading as
   `<span class="nav-group">NSW</span>`, so none of this ever applied: NSW,
   ACT, QLD and SA rendered as unstyled spans inheriting body colour at body
   size, indistinguishable from the region links under them.

   Colour is --cpa-blue-deep (#005F82) on --cpa-paper (#FFFFFF), which
   measures 7.1:1. That matters because these are 11px and bold-but-small,
   so they need the 4.5:1 normal-text threshold rather than the 3:1 large
   text one. The brand blue --cpa-blue (#0096CC) only reaches 3.0:1 on white
   and cannot be used for text at this size. */
.nav .sub-menu > .menu-item-has-children > a,
.nav .sub-menu > .menu-item-has-children > .nav-group {
  pointer-events: none;
  cursor: default;
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cpa-blue-deep);
  padding: 0 10px var(--space-3);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--cpa-line);
  border-radius: 0;
}
.nav .sub-menu > .menu-item-has-children > a:hover { background: transparent; }
.nav .sub-menu > .menu-item-has-children > a::after { display: none; }

/* Region links sit inline under their state rather than as a second flyout.
   They inherit the parent panel's visibility, so they are always "shown"
   within it and never toggle independently. */
.nav .sub-menu .sub-menu {
  position: static;
  visibility: inherit;
  opacity: 1;
  pointer-events: auto;
  transform: none;
  transition: none;
  gap: 1px;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}
.nav .sub-menu .sub-menu::before { display: none; }

/* Long region names -------------------------------------------------------
   September 2026. NSW went from four regions to nine, and three latent bugs
   surfaced at once.

   1. `.nav ul` sets `align-items: center` so the top-level bar sits on a
      line. The dropdown lists inherit it, because `.nav .sub-menu` only
      overrides `flex-direction`, so every link box was centred in its column
      rather than filling it. Short names looked fine. "Newcastle, Maitland &
      Lake Macquarie" overflowed symmetrically and bled out both sides of the
      panel, including off its left edge into the page behind.
   2. `.nav a` sets `white-space: nowrap`. Correct for the top-level bar,
      wrong here: a long region name has nowhere to go but sideways.
   3. Four equal columns gave NSW, with nine regions, the same width as SA,
      which has one.

   Stretch the items so they fill the column and read from the left, let them
   wrap, and give NSW the room it actually needs. The mega panel keeps its own
   `align-items: start` because that rule is more specific, which is what we
   want: grid columns should still align to their tops. */
.nav .sub-menu { align-items: stretch; }
.nav .sub-menu .sub-menu a {
  white-space: normal;
  justify-content: flex-start;
  text-align: left;
}
@media (min-width: 1001px) {
  /* NSW holds nine of the thirteen destinations. ACT, QLD and SA hold four
     between them. Giving each state a column therefore produced a panel nine
     rows deep with three almost empty columns beside it, and it got worse
     with every region added.

     So NSW spans the full width and flows its own list into three
     sub-columns, and the other three states sit underneath in a row. Three
     rows deep instead of nine, and the width is actually used. The state
     grouping still reads, it just no longer dictates the geometry. */
  .nav > ul > li.menu-item-704 > .sub-menu {
    width: min(860px, calc(100vw - 48px));
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-6) var(--space-5);
  }
  .nav > ul > li.menu-item-704 > .sub-menu > .menu-item-705 {
    grid-column: 1 / -1;
  }
  .nav > ul > li.menu-item-704 > .sub-menu > .menu-item-705 > .sub-menu {
    display: block;
    columns: 3;
    column-gap: var(--space-5);
  }
  /* Without this a link can be split across the column boundary. */
  .nav > ul > li.menu-item-704 > .sub-menu > .menu-item-705 > .sub-menu > li {
    break-inside: avoid;
  }
}
.nav .sub-menu .sub-menu a {
  padding: 9px 10px;
  font-size: 14.5px;
  font-weight: 400;
  color: var(--cpa-ink-2);
  line-height: 1.35;
}
.nav .sub-menu .sub-menu a:hover { color: var(--cpa-blue-deep); background: var(--cpa-blue-mist); }

/* Mobile and tablet: the panels become plain nested lists inside the burger
   menu. The mega-menu grid is undone so nothing is squeezed into columns. */
@media (max-width: 1000px) {
  .nav { position: static; }
  .nav > ul > li.menu-item-704 { position: relative; }
  .nav > ul > li.menu-item-704 > .sub-menu {
    width: auto;
    display: flex;
    grid-template-columns: none;
    padding: 0 0 var(--space-3) var(--space-4);
  }
  .nav .sub-menu {
    position: static;
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
    transform: none;
    transition: none;
    box-shadow: none;
    border: 0;
    border-radius: 0;
    padding: 0 0 var(--space-3) var(--space-4);
    min-width: 0;
    background: transparent;
  }
  .nav > ul > li > .sub-menu::before { display: none; }
  .nav .sub-menu > .menu-item-has-children > a,
  .nav .sub-menu > .menu-item-has-children > .nav-group {
    padding: var(--space-4) 0 var(--space-2);
    margin-bottom: 0;
  }
  .nav .sub-menu a { padding: 11px 0; }
  .nav .sub-menu .sub-menu a { padding: 10px 0 10px var(--space-4); }
}

/* Burger */
.nav-toggle {
  display: none; margin-left: auto;
  width: 46px; height: 46px; flex: none;
  align-items: center; justify-content: center;
  background: transparent; border: 1px solid var(--cpa-line);
  border-radius: 10px; cursor: pointer;
}
.nav-toggle span, .nav-toggle span::before, .nav-toggle span::after {
  display: block; width: 20px; height: 2px; border-radius: 2px;
  background: var(--cpa-ink); position: relative;
}
.nav-toggle span::before, .nav-toggle span::after { content: ""; position: absolute; left: 0; }
.nav-toggle span::before { top: -6px; }
.nav-toggle span::after { top: 6px; }

/* The nav carries five top-level items plus a phone number and a CTA. Below
   1180px the phone drops, then the whole nav collapses to the burger at
   1000px. Both moved down after "Get a Quote" was removed from the menu. */
@media (max-width: 1180px) {
  .site-header__phone { display: none; }
  .site-header__actions { margin-right: 0; }
  .nav ul { gap: 24px; }
  .nav a { font-size: 15px; }
}

@media (max-width: 1000px) {
  .nav-toggle { display: inline-flex; }
  .site-header__logo { margin-left: 0; }
  .site-header__phone, .site-header__cta { display: none; }
  .nav {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--cpa-paper); border-bottom: 1px solid var(--cpa-line);
    box-shadow: 0 12px 32px rgba(48, 49, 48, 0.14);
    margin: 0; padding: var(--space-4) var(--gutter) var(--space-6);
    display: none;
  }
  .site-header.is-open .nav { display: block; }
  .nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .nav > ul > li { border-bottom: 1px solid var(--cpa-line); }
  .nav a { padding: 14px 0; font-size: 17px; }
  .nav .menu-item-has-children > a::after { margin-left: auto; }
}

/* --------------------------------------------------------------------------
   6a · HERO
   The opening view has to fit the form and the headline together without the
   submit button falling below the fold. Vertical padding is tighter than a
   standard section and the headline steps down a size on shorter screens.
   -------------------------------------------------------------------------- */
/* Hero.
   The opening view has to fit the form and the headline together without the
   submit button falling below the fold. Sizing is fluid rather than stepped:
   the headline scales continuously, and the only breakpoints left are the
   two that change *layout* rather than size. */
/* Hero fills the screen below the site chrome.
   svh rather than vh: on mobile browsers vh is the *largest* viewport, which
   includes space behind the address bar, so a 100vh hero is taller than what
   you can actually see on first paint. svh is the smallest viewport, which is
   exactly the visible area when the page loads.

   --chrome-h is the measured height of the announcement bar plus the header,
   set by cpa.js on load and resize. The fallback covers the moment before
   the script runs and the case where JavaScript is off. */
.hero {
  min-height: calc(100vh - var(--chrome-h, 130px));
  min-height: calc(100svh - var(--chrome-h, 130px));
  display: flex;
  align-items: center;
  padding-block: clamp(2rem, 1.2rem + 3.4vw, 3.5rem) clamp(2.5rem, 1.6rem + 3.9vw, 4rem);
}
.hero > .wrap { width: 100%; }

/* Short viewports: phones held sideways, and small laptops with a lot of
   browser chrome. Forcing a full screen here squeezes the content into a
   letterbox, so the hero reverts to sizing itself to its contents. */
@media (max-height: 560px) {
  .hero { min-height: 0; padding-block: var(--space-10); }
}

/* Very tall screens: on a portrait tablet or a rotated monitor a full-height
   hero leaves the copy stranded in the middle of a large empty area, so it is
   capped and the next section starts to show. */
@media (min-height: 1100px) {
  .hero { min-height: 760px; }
}

/* The copy block sits in from the container edge by --edge-indent. The header
   logo uses the same token, so the headline and the logo share a left axis. */
.hero .stack { max-width: 100%; margin-left: var(--edge-indent); }
.hero .cpa-h1 { font-size: clamp(2.5rem, 1.6rem + 3.8vw, 4.5rem); line-height: 1.02; max-width: 18ch; }
.hero .cpa-lead { max-width: 46ch; }
/* Spacing scales with viewport *height*, not width. The hero fills the screen,
   so on a tall display the copy block was floating in the middle with dead
   space above and below while its own elements sat tight against each other.
   Using vh here lets the block breathe into the space it actually has. */
.hero .stack > * + * { margin-top: clamp(1.25rem, 2.4vh, 2.5rem); }
.hero .cpa-lead { line-height: 1.6; max-width: 48ch; }
.hero .form-panel { padding: var(--panel-pad); }
.hero .form-panel .elementor-field-group { margin-bottom: var(--space-4); }

/* The copy column carries a headline, a line of text and a trust stack; the
   form column carries a panel of fixed-ish width. Equal columns left the form
   stretched and the copy floating, so the split is weighted to the form. */
/* Weighted toward the copy: the headline is large and needs the room, and a
   three-field form does not need half the screen. Held at 42% rather than a
   third, below which the panel starts to read as squeezed in rather than as
   the point of the page. */
/* Three children, not two: copy, form, trust. The form sits between the copy
   and the trust stack in source order so that when the grid collapses to one
   column the phone reads headline, form, proof without any use of `order` or
   flex-reverse. DOM order and visual order stay in step at every width.

   Four rows, not two. The obvious version places copy in row 1 and the trust
   stack in row 2 with the form spanning both, but a grid item that spans two
   auto tracks distributes its own height across them. The form is much taller
   than the copy, so both rows grew and the trust stack was pushed to the foot
   of the hero with a large gap under the lead paragraph.

   The two 1fr spacer tracks absorb that surplus instead. Copy and trust sit in
   the middle two auto tracks, separated by exactly one row gap, and the pair is
   centred against the form as a group. Gaps are symmetric top and bottom so
   the centring holds. */
.hero__grid {
  grid-template-columns: minmax(0, 1.38fr) minmax(0, 1fr);
  grid-template-rows: 1fr auto auto 1fr;
  column-gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  row-gap: clamp(1.25rem, 2.4vh, 2.5rem);
}
.hero__copy  { grid-column: 1; grid-row: 2; }
.hero__aside { grid-column: 1; grid-row: 3; }
.hero__form  { grid-column: 2; grid-row: 1 / -1; align-self: center; }
/* The trust stack shares the copy column's left axis. */
.hero .hero__aside { margin-left: var(--edge-indent); }

/* Service pages run copy and form with no trust stack, so row 3 is empty and
   the copy ends up sitting one row gap below true centre. Let it span both
   middle tracks when there is no aside to share them with. */
.hero__grid:not(:has(.hero__aside)) .hero__copy {
  grid-row: 2 / 4;
  align-self: center;
}

/* Get a Quote runs the same three-part grid but on a pale wash rather than a
   photo, and the form is the whole point of the page, so the columns are even
   rather than weighted to the copy.

   Declared here, before the breakpoints below, deliberately. It is a single
   class and so carries the same specificity as the .hero__grid rules inside
   those media queries: placed after them it would win on source order and
   re-impose two columns on a phone. */
.hero__grid--even {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* Top-aligned rather than centred: this hero has no photo and no fixed
     height, so the copy should start level with the top of the form panel.
     One flexible track at the foot soaks up the form's surplus height and
     keeps the promise pair sitting directly under the lead. */
  grid-template-rows: auto auto 1fr;
  column-gap: var(--space-16);
}
.hero__grid--even .hero__copy  { grid-row: 1; }
.hero__grid--even .hero__aside { grid-row: 2; align-self: start; }
.hero__grid--even .hero__form  { grid-row: 1 / -1; align-self: start; }

/* Short laptop screens: the vertical budget matters more than the width, so
   the headline gives ground to keep the form's submit button in view. */
@media (min-width: 1025px) and (max-height: 860px) {
  .hero .cpa-h1 { font-size: clamp(2.5rem, 1.6rem + 2.6vw, 3.5rem); }
}

/* --- Photographic hero ----------------------------------------------------
   The photo is a real <img> rather than a CSS background so it can carry a
   srcset, a separate mobile crop, and fetchpriority="high" as the LCP element.
   A gradient scrim sits over it: heaviest on the left where the copy is,
   clearing towards the right so the roof is still visible behind the form. */
/* The section's own background-colour must match what actually renders behind
   the hero copy, which is the dark scrim, not the pale blue wash used by the
   rest of the page bands. Two reasons:
     1. If the WebP fails to load, white text would otherwise sit on #D8F4FF.
     2. Automated checkers (WAVE, and axe when it can resolve a background)
        cannot see through the <img> or the ::before scrim, so they compare
        the text colour against this declaration. With the wash they reported
        1.15:1 for the h1/lead and 1.85:1 for the eyebrow on every photo hero.
   Value matches the scrim base rgb(30,31,30). Do not set this on .hero
   generally: only photo heroes carry light-on-dark copy. */
.hero--photo { position: relative; isolation: isolate; background-color: #1E1F1E; }
.hero__bg { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; object-position: center 62%; }
/* Scrim stops are measured, not eyeballed.
   Method: sample the hero image's actual pixels behind each text run, composite
   this gradient over them, and take the worst pixel. Two cautions for anyone
   editing this:
     1. axe reports hero text as "incomplete", never as a failure, because it
        cannot compute a ratio against a photograph. A clean automated scan
        says nothing about this gradient.
     2. Measure the TEXT RUNS (Range.getClientRects), not the element box.
        .cpa-eyebrow is display:block and spans ~1187px while its glyphs occupy
        ~134px, so measuring the box samples a thousand pixels of empty
        background out where the scrim thins, and invents failures that are not
        there.
   The binding case is the h1 on /vpp/, whose headline runs furthest right into
   the thin zone: 2.70:1 at the old 0.42@62%, against a 3:1 bar for 72px text.
   Raising the mid-stops to 0.55/0.36 puts it at ~3.75:1 and every other photo
   hero above 4.4:1, while keeping 0.88 at the left and a bright right edge.
   Re-measure if the hero images are ever swapped. */
.hero--photo::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(
    90deg,
    rgba(30, 31, 30, 0.88) 0%,
    rgba(30, 31, 30, 0.78) 38%,
    rgba(30, 31, 30, 0.55) 62%,
    rgba(30, 31, 30, 0.36) 100%
  );
}
.hero--photo .cpa-h1 { color: var(--cpa-paper); }
.hero--photo .cpa-lead { color: rgba(255, 255, 255, 0.92); }
.hero--photo .hero__link { color: var(--cpa-paper); }
.hero--photo .hero__link:hover { color: var(--cpa-blue-bright); }

@media (max-width: 1024px) {
  /* One column from tablet down. Source order already reads copy, form, trust,
     so no reversal is needed: this replaces an older flex column-reverse that
     put the form above the headline visually while leaving it second in the
     DOM. */
  .hero__grid {
    grid-template-columns: minmax(0, 1fr);
    /* Explicitly collapse the desktop row template. Both .hero__grid and
       .hero__grid--even declare flexible spacer tracks for the vertical
       centring, and left in place they would hand a stacked phone layout an
       empty 1fr track to stretch into. */
    grid-template-rows: auto;
    row-gap: var(--space-8);
  }
  .hero__copy,
  .hero__form,
  .hero__aside { grid-column: 1; grid-row: auto; align-self: auto; }
  /* Repeated at .hero__grid--even's specificity. The single-class resets above
     lose to the two-class Get a Quote rules, which would leave that page's form
     still spanning every row and sitting on top of the stack. */
  .hero__grid--even .hero__copy,
  .hero__grid--even .hero__form,
  .hero__grid--even .hero__aside { grid-column: 1; grid-row: auto; align-self: auto; }
  .hero .hero__aside { margin-left: 0; }
  /* Copy sits under the form, so the scrim goes even rather than side-weighted.
     Lifted from 0.62/0.86: at the old values the eyebrow measured 2.88:1 on
     /vpp/ at 420px, against a 4.5:1 bar. Paired with the 19px eyebrow below
     (which moves it to the 3:1 large-text bar) this clears every photo hero at
     420px and 900px with margin. The scrim alone would have needed 0.80 to do
     the same job, which crushed the photography on mobile. */
  .hero--photo::before {
    background: linear-gradient(180deg, rgba(30, 31, 30, 0.72) 0%, rgba(30, 31, 30, 0.90) 100%);
  }
  /* 19px at weight 700 clears WCAG's large-text threshold (18.66px bold), which
     drops the requirement from 4.5:1 to 3:1. max() so it still scales up with
     the user's root font size but can never fall below the threshold and
     silently revert to the stricter bar. */
  .hero--photo .cpa-eyebrow {
    font-size: max(19px, 1.1875rem);
    font-weight: 700;
    letter-spacing: 0.04em;
  }
}

/* Phones: the form is shown, and shown early.
   It previously carried display:none here, because at full size the panel is
   roughly a screen on its own and pushed the headline and the accreditation
   marks out of the opening view. That is now solved by source order plus the
   compaction below rather than by hiding the form: the phone reads headline,
   form, then proof, and the panel is trimmed enough that the heading and the
   first fields sit inside the opening screen on a 390x844 viewport.

   Short landscape (a rotated phone) is kept as a second query: there is no
   vertical budget there for a headline and a form together, so the hero sizes
   to its contents and the form simply follows the copy. */
@media (max-width: 767px),
       (max-height: 520px) and (orientation: landscape) {
  .hero__grid { display: grid; grid-template-columns: minmax(0, 1fr); }
  .hero .stack { margin-left: 0; }
  .hero-trust { max-width: none; }
}

/* Mobile form compaction.
   Trims the panel so the headline and the first fields share the opening
   screen. The intro line is dropped because the panel heading directly above
   it already says the same thing in fewer words. The privacy line is kept: it
   carries the Privacy Policy link and sits below the submit button, so it
   costs nothing above the fold. */
@media (max-width: 767px) {
  .hero { padding-block: var(--space-8) var(--space-10); }
  .hero .cpa-h1 { font-size: clamp(2rem, 1.4rem + 3vw, 2.5rem); }
  .hero__grid .form-panel { padding: var(--space-5); }
  .hero__grid .form-panel .elementor-widget-heading .elementor-heading-title {
    font-size: 20px;
    line-height: 1.25;
  }
  .hero__grid .form-panel .elementor-3734 .elementor-element-ec3f259 { display: none; }
  .hero__grid .form-panel .elementor-field-group { margin-bottom: var(--space-3); }
  .hero__grid .form-panel .elementor-field-label { margin-bottom: 2px; }
}


/* --- Hero trust stack -----------------------------------------------------
   Accreditation marks, one real customer quote, and the live Google rating.
   Sits beside the form rather than inside it, so the proof is working while
   the visitor is still deciding whether to start filling anything in. */
/* One row: the Google badge and the two accreditation marks sit together as a
   single band of proof. Every item is centred on the same horizontal axis.

   Alignment is by centre point, not by baseline or bottom edge: the Trustindex
   card, the square roundel and the wide wordmark are all different heights, so
   anything other than centre alignment leaves them stepping up and down. */
.hero-trust {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-5) var(--space-8);
  margin-top: clamp(2rem, 4.5vh, 3.75rem);
}
.hero-trust > * {
  display: flex;
  align-items: center;
  align-self: center;
  line-height: 0;
}
.hero-trust .badges { justify-content: flex-start; align-items: center; }

/* Trustindex nests its card several levels deep and some of those carry their
   own vertical margin, which shifts the card off the row's centre line. */
.hero-trust__widget .ti-widget,
.hero-trust__widget .ti-widget > *,
.hero-trust__widget [data-src] { margin-block: 0 !important; }

.hero-trust__quote {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin: 0;
  padding: var(--space-5);
  border-radius: var(--radius-card);
  border: 1px solid var(--cpa-line);
  background: var(--cpa-paper);
}
.hero-trust__avatar {
  flex: none;
  width: 46px; height: 46px;
  border-radius: 50%;
  background: var(--cpa-blue-tint);
  color: var(--cpa-blue-deep);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 18px;
}
.hero-trust__quote blockquote {
  margin: var(--space-2) 0 0;
  font-size: 15.5px;
  line-height: 1.45;
  font-weight: 600;
  color: var(--cpa-ink);
}
.hero-trust__quote figcaption {
  margin-top: var(--space-2);
  font-size: 13px;
  color: var(--cpa-ink-2);
}

/* No plate here: the Trustindex badge ships its own white card with rounded
   corners and a shadow, so wrapping it added a second background around the
   first. Width and height are left to the widget. */
.hero-trust__widget { display: inline-flex; align-items: center; }
.hero-trust__widget .ti-widget { margin: 0 !important; }

/* Static rating badge. Same data source as the review carousel, no script. */
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 18px 10px 14px;
  border-radius: var(--radius-pill);
  background: var(--cpa-paper);
  border: 1px solid var(--cpa-line);
}
.rating-badge__score {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 30px;
  line-height: 1;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-ink);
}
.rating-badge__body { display: flex; flex-direction: column; gap: 3px; text-align: left; }
.rating-badge .stars { font-size: 15px; letter-spacing: 2px; line-height: 1; }
.rating-badge__label { font-size: 13px; line-height: 1.3; color: var(--cpa-ink-2); }

/* On the hero the badge has no plate: white type straight onto the scrim. */
.hero-trust--dark .rating-badge {
  background: transparent;
  border: 0;
  padding: 0;
  gap: var(--space-4);
}
.hero-trust--dark .rating-badge__score { color: var(--cpa-paper); font-size: 34px; }
.hero-trust--dark .rating-badge__label { color: rgba(255, 255, 255, 0.85); font-size: 13.5px; }

.hero-trust__rating {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 14px;
  color: var(--fg-secondary);
}
.hero-trust__rating strong { color: var(--cpa-ink); font-weight: 700; font-size: 16px; }

/* On the photographic hero the stack sits on a dark scrim, so the quote card
   becomes translucent glass rather than a solid white block, which would
   compete with the form panel opposite it. */
.hero-trust--dark .hero-trust__quote {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.22);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.hero-trust--dark .hero-trust__quote blockquote { color: var(--cpa-paper); }
.hero-trust--dark .hero-trust__quote figcaption { color: rgba(255, 255, 255, 0.72); }
.hero-trust--dark .hero-trust__avatar {
  background: rgba(255, 255, 255, 0.18);
  color: var(--cpa-paper);
}
.hero-trust--dark .hero-trust__rating { color: rgba(255, 255, 255, 0.86); }
.hero-trust--dark .hero-trust__rating strong { color: var(--cpa-paper); }

@media (max-width: 1024px) {
  .hero-trust { max-width: none; margin-top: var(--space-6); }
}
@media (max-width: 480px) {
  .hero-trust { gap: var(--space-5); }
  .hero-trust__quote { padding: var(--space-4); }
  .hero-trust__avatar { width: 38px; height: 38px; font-size: 15px; }
}

/* --------------------------------------------------------------------------
   7 · CTA BAND + FOOTER
   -------------------------------------------------------------------------- */
/* Band background is action blue, not brand blue. The lead paragraph here is
   var(--fs-lead), which tops out at 19px regular: below the 24px large-text
   threshold, so it needs the full 4.5:1. White on #0096CC only reaches
   3.37:1, and no text colour fixes that while the fill stays bright, so the
   fill is what moved. White on #005F82 is 7.10:1. */
.cta-band {
  background: var(--cpa-blue-action);
  color: var(--fg-on-blue);
  padding-block: var(--space-16);
}
.cta-band__inner {
  display: grid;
  grid-template-columns: 1.4fr auto;
  gap: var(--space-10);
  align-items: center;
}
.cta-band .cpa-h2 { color: var(--fg-on-blue); max-width: 22ch; }
/* Was rgba(255,255,255,0.9), which composited to #E6F5FA over the band and
   measured 3.02:1 across 48 pages. Solid white on the new fill is 7.10:1.
   The 10% knock-down bought almost nothing visually and cost the AA pass. */
.cta-band .cpa-lead { color: #ffffff; max-width: 52ch; }
.cta-band__actions { flex-wrap: nowrap; }
.btn.btn--ghost {
  background: transparent;
  color: var(--fg-on-blue);
  border-color: rgba(255, 255, 255, 0.6);
}
.btn.btn--ghost:hover {
  background: rgba(255, 255, 255, 0.12);
  color: var(--fg-on-blue);
  border-color: var(--fg-on-blue);
}
@media (max-width: 1024px) {
  .cta-band__inner { grid-template-columns: minmax(0, 1fr); gap: var(--space-8); }
  .cta-band__actions { flex-wrap: wrap; }
}

.site-footer {
  background: var(--cpa-ink);
  color: var(--cpa-paper);
  padding-block: var(--space-20) var(--space-8);
}
/* Elementor's kit sets heading and paragraph colours globally. Everything
   inside the footer sits on ink, so it is pinned here rather than inheriting. */
.site-footer h2,
.site-footer h3,
.site-footer p { color: inherit; }

.site-footer__top {
  display: grid;
  grid-template-columns: 1.6fr repeat(4, minmax(0, 1fr));
  gap: var(--space-10) var(--space-8);
  align-items: start;
}

/* Specificity note: Elementor's kit ships `.elementor-kit-6 h2 { color: … }`
   set to the ink colour, which is the same as this footer's background. A
   single `.site-footer__title` class loses to it and the headings render
   invisible. Doubling up puts them back. */
/* AAA contrast, footer only.
   The footer sits on --cpa-ink (#303130) and was the single biggest source of
   AAA gaps in the August 2026 sweep: roughly 20 of every page's ~31 enhanced
   contrast findings came from these two colours, across all 44 pages.
     --cpa-blue-bright  #3DBFF0  6.16:1  → #66CEF6  7.29:1  (column headings)
     --cpa-ink-4        #ABA9AA  5.59:1  → #C4C2C3  7.38:1  (links, legal line)
   Rebound as scoped custom properties rather than edited in cpa-tokens.css,
   because both tokens are also used on light surfaces, where these lighter
   values would fail badly. Everything inside .site-footer inherits the
   override; nothing outside it changes.
   Chosen over the remaining AAA gaps because a footer is where someone hunts
   for a phone number in bad light, and because it costs no brand colour. */
.site-footer {
  --cpa-blue-bright: #66CEF6;
  --cpa-ink-4: #C4C2C3;
}

.site-footer .site-footer__title {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cpa-blue-bright);
  margin: 0 0 var(--space-5);
  min-height: 2.6em; /* keeps one and two line headings on the same baseline */
}

/* Brand column */
.site-footer__logo { display: inline-flex; }
/* The logo artwork is dark, so it is inverted to sit on the ink background
   rather than maintaining a second white asset. */
.site-footer__logo img {
  height: 38px; width: auto;
  filter: brightness(0) invert(1);
  opacity: 0.95;
}
.site-footer__blurb {
  margin-top: var(--space-4);
  max-width: 34ch;
  font-size: 15px;
  line-height: 1.6;
  color: var(--cpa-ink-4);
}
.site-footer__creds {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-5);
}
.site-footer__creds li {
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255, 255, 255, 0.22);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--cpa-blue-bright);
  white-space: nowrap;
}
.site-footer__contact {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
}
.site-footer__contact a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 15px;
  font-weight: 600;
  color: var(--cpa-paper);
  text-decoration: none;
  word-break: break-word;
}
.site-footer__contact a:hover { color: var(--cpa-blue-bright); }
.site-footer__contact svg { flex: none; color: var(--cpa-blue-bright); }

/* Link columns */
.site-footer__col ul { list-style: none; }
/* WCAG 2.2 AA 2.5.8 (Target Size, Minimum). The anchor was an inline box just
   20px tall across ~24 links on every page: the largest single target-size
   failure on the site. Padding it into a block target clears the 24px floor and
   makes the full column width tappable. The list gap drops from 12px to 4px to
   absorb the extra height, so row pitch moves 33px -> 35px and the footer's
   visual rhythm is effectively unchanged. */
.site-footer__col li + li { margin-top: var(--space-1); }
.site-footer__col a {
  display: block;
  padding-block: 5px;                 /* 20px -> 30px */
  font-size: 15px;
  line-height: 1.4;
  color: var(--cpa-ink-4);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.site-footer__col a:hover { color: var(--cpa-paper); }

/* Legal bar */
.site-footer__legal {
  margin-top: var(--space-16);
  padding-top: var(--space-6);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  color: var(--cpa-ink-4);
}
.site-footer__legal nav { display: flex; flex-wrap: wrap; gap: var(--space-6); }
.site-footer__legal a {
  display: inline-block;
  padding-block: 4px;                 /* 21px -> 29px, WCAG 2.5.8 */
  color: var(--cpa-ink-4);
  text-decoration: none;
}
.site-footer__legal a:hover { color: var(--cpa-paper); }

@media (max-width: 1024px) {
  .site-footer__top { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .site-footer__brand { grid-column: 1 / -1; }
}
@media (max-width: 768px) {
  .site-footer { padding-block: var(--space-12) var(--space-8); }
  .site-footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8) var(--space-6); }
  .site-footer__legal { margin-top: var(--space-10); }
}
@media (max-width: 480px) {
  .site-footer__top { grid-template-columns: minmax(0, 1fr); }
  .site-footer .site-footer__title { min-height: 0; }
}

/* --------------------------------------------------------------------------
   8 · ELEMENTOR FORM EMBEDS
   Forms remain Elementor widgets so form_name and the Make webhooks are
   untouched. This restyles the markup only.
   -------------------------------------------------------------------------- */
.form-panel {
  background: var(--cpa-paper);
  border-radius: var(--radius-soft);
  padding: var(--space-10);
  box-shadow: var(--shadow-card);
}

/* Proof sits inside the panel, directly above the fields, because that is
   where the visitor is being asked to hand over a phone number. It replaces
   the trust cluster that used to sit under the hero buttons, at a fraction
   of the visual weight. */
.form-trust {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--cpa-line);
  font-size: 13.5px;
  color: var(--cpa-ink-2);
}
.form-trust .stars { font-size: 15px; letter-spacing: 1px; }
.form-trust strong { color: var(--cpa-ink); font-weight: 700; }
.form-panel .elementor-field-group { margin-bottom: var(--space-5); }
.form-panel .elementor-field-label { font-size: 14px; font-weight: 700; color: var(--cpa-ink); margin-bottom: var(--space-2); }

.elementor-field-textual {
  width: 100%;
  border-radius: var(--radius-input);
  border: 1px solid rgba(120, 122, 120, 0.6);
  background: var(--cpa-paper);
  padding: 14px 20px; font-size: 16px; color: var(--cpa-ink);
  box-shadow: var(--shadow-card-sm);
  transition: border-color var(--transition-base), box-shadow var(--transition-base);
}
.elementor-field-textual::placeholder { color: var(--cpa-ink-3); }
.elementor-field-textual:focus {
  border: 2px solid var(--cpa-blue); padding: 13px 19px;
  box-shadow: 0 0 0 3px rgba(0, 150, 204, 0.22); outline: none;
}
.elementor-error .elementor-field-textual { border: 2px solid var(--cpa-error); padding: 13px 19px; }
.elementor-message-danger { color: var(--cpa-error-text); font-size: 13px; font-weight: 600; margin-top: 7px; }
.elementor-message-success {
  background: var(--cpa-blue-wash); color: var(--cpa-ink);
  border-radius: var(--radius-card); padding: 16px 20px; font-size: 15px;
}
/* Submit button.
   Elementor generates per-template CSS for form 3734 using element-scoped
   selectors (.elementor-3734 .elementor-element-xxxx .elementor-button),
   which is three levels of specificity and beats anything reasonable written
   here. Those declarations previously leaned on the kit stylesheet for their
   appearance, and the kit is now dequeued, so the button rendered as bare
   text. !important is the correct tool against generated CSS we do not own. */
.elementor-field-type-submit .elementor-button,
.elementor-form .elementor-button[type="submit"],
.form-panel .elementor-button,
.form-panel button.elementor-button {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100% !important;
  background: var(--cpa-blue-action) !important;
  color: var(--fg-on-blue) !important;
  border: 0 !important;
  border-radius: var(--radius-pill) !important;
  font-family: var(--font-body) !important;
  font-weight: 700 !important;
  font-size: 18px !important;
  line-height: 1 !important;
  letter-spacing: -0.01em;
  padding: 16px 32px !important;
  min-height: 54px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  box-shadow: none !important;
  text-shadow: none;
  transition: background var(--transition-base), transform 120ms ease-in;
}
.elementor-field-type-submit .elementor-button:hover,
.form-panel .elementor-button:hover { background: var(--cpa-blue-hover) !important; color: var(--fg-on-blue) !important; }
.elementor-field-type-submit .elementor-button:active,
.form-panel .elementor-button:active { transform: scale(0.99); }
.form-panel .elementor-button:focus-visible {
  outline: 2px solid var(--cpa-blue-deep);
  outline-offset: 3px;
}
.form-panel .elementor-button span,
.form-panel .elementor-button .elementor-button-text,
.form-panel .elementor-button .elementor-button-content-wrapper {
  color: inherit !important;
  font: inherit !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.elementor-field-type-honeypot { position: absolute !important; left: -9999px !important; }

@media (max-width: 767px) { .form-panel { padding: var(--space-6); border-radius: var(--radius-card); } }

/* --------------------------------------------------------------------------
   9 · ACCORDION (native details/summary — no JS)
   -------------------------------------------------------------------------- */
.faq { border: 1px solid var(--cpa-line-blue); border-radius: var(--radius-card); background: var(--cpa-paper); margin-bottom: var(--space-3); overflow: hidden; }
.faq > summary {
  list-style: none; cursor: pointer;
  padding: var(--space-5) var(--space-6);
  font-weight: 700; font-size: 17px; color: var(--cpa-ink);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}
.faq > summary::-webkit-details-marker { display: none; }
.faq > summary::after {
  content: "+"; flex: none;
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--cpa-blue-mist); color: var(--cpa-blue-deep);
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; font-weight: 400; line-height: 1;
  transition: transform var(--transition-fast);
}
.faq[open] > summary::after { transform: rotate(45deg); }
.faq > summary:focus-visible { outline: 2px solid var(--cpa-blue); outline-offset: -2px; }
.faq__body { padding: 0 var(--space-6) var(--space-5); color: var(--fg-secondary); }

/* --------------------------------------------------------------------------
   10 · PROSE (blog + generic pages)
   -------------------------------------------------------------------------- */
.prose { max-width: 760px; margin-inline: auto; }
.prose > * + * { margin-top: var(--space-5); }
.prose p { font-size: 17px; line-height: 1.7; }
.prose h2 { font-family: var(--font-display); font-weight: 500; font-size: 30px; line-height: 1.3; letter-spacing: var(--tracking-display); margin-top: var(--space-10); }
.prose h3 { font-size: 23px; margin-top: var(--space-8); }
.prose ul, .prose ol { padding-left: 22px; }
.prose li { margin-bottom: var(--space-2); line-height: 1.65; }
.prose li::marker { color: var(--cpa-blue); }
.prose img { border-radius: var(--radius-image); }
.prose a { text-decoration: underline; text-underline-offset: 3px; }
/* Buttons inside post content keep the button look, not the link underline. */
.single-post .prose a.btn { text-decoration: none; }
/* Mobile: the featured image is pulled up by --space-10, which covered the
   date and read-time line. Give the post header enough bottom padding to clear it. */
@media (max-width: 767px) {
  .single-post header.section--tight { padding-bottom: calc(var(--space-10) + 16px); }
}
.prose blockquote {
  background: var(--cpa-blue-wash); border-radius: var(--radius-card);
  padding: var(--space-8); margin-inline: 0;
  font-family: var(--font-display); font-weight: 500; font-size: 22px;
  line-height: 1.45; letter-spacing: -0.01em;
}

/* --------------------------------------------------------------------------
   10a · BLOG ARCHIVE
   -------------------------------------------------------------------------- */
.blog-hero {
  background: linear-gradient(180deg, var(--cpa-blue-wash) 0%, var(--cpa-blue-paper) 100%);
}
.blog-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-8);
}
.blog-filters .chip {
  text-decoration: none;
  font-size: 13px;
  padding: 7px 15px;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.blog-filters .chip.is-current {
  background: var(--cpa-blue-action);
  border-color: var(--cpa-blue-action);
  color: var(--fg-on-blue);
}

.post-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-8);
  margin-top: var(--space-10);
}

.post-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  overflow: hidden;
}
.post-card__media {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--cpa-blue-mist);
}
.post-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: 0;
  transition: transform 400ms ease-out;
}
.post-card:hover .post-card__media img { transform: scale(1.04); }

.post-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  flex: 1;
}
.post-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: 13px;
  color: var(--cpa-ink-2);
}
.post-card__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-ink);
}
.post-card:hover .post-card__title { color: var(--cpa-blue-deep); }
.post-card__excerpt {
  font-size: 15px;
  line-height: 1.6;
  color: var(--fg-secondary);
}
.post-card__cta { margin-top: auto; padding-top: var(--space-2); font-size: 14px; }

/* Featured post: image beside the text rather than above it, so the first
   item reads as the lead story instead of just a taller card. */
.post-card--feature {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: stretch;
}
.post-card--feature .post-card__media { aspect-ratio: auto; height: 100%; min-height: 340px; }
.post-card--feature .post-card__body {
  justify-content: center;
  padding: var(--space-12);
  gap: var(--space-4);
}
.post-card--feature .post-card__title { font-size: clamp(1.5rem, 1.2rem + 1.25vw, 2rem); }
.post-card--feature .post-card__excerpt { font-size: 17px; }

.post-pagination { margin-top: var(--space-16); }
.post-pagination ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
}
.post-pagination a,
.post-pagination span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  height: 44px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  border: 1px solid var(--cpa-line);
  font-size: 15px;
  font-weight: 600;
  color: var(--cpa-ink);
  text-decoration: none;
}
.post-pagination a:hover { background: var(--cpa-blue-mist); color: var(--cpa-blue-deep); }
.post-pagination .current {
  background: var(--cpa-blue-action);
  border-color: var(--cpa-blue-action);
  color: var(--fg-on-blue);
}

@media (max-width: 1024px) {
  .post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .post-card--feature { grid-template-columns: minmax(0, 1fr); }
  .post-card--feature .post-card__media { aspect-ratio: 16 / 9; min-height: 0; }
  .post-card--feature .post-card__body { padding: var(--space-8); }
}
@media (max-width: 768px) {
  .post-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
}

/* --------------------------------------------------------------------------
   10b · VIDEO
   -------------------------------------------------------------------------- */
.cpa-video { margin: 0; }
/* Specificity note: Elementor's kit styles bare `button` elements with the
   accent colour and its own padding, which showed as a blue frame around the
   poster and clipped the title. Doubling the class outranks it. */
.cpa-video .cpa-video__frame {
  display: block;
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--cpa-ink);
  cursor: pointer;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
}
/* Absolute rather than height:100% — a percentage height inside a button with
   aspect-ratio does not resolve reliably, which left the poster letterboxed. */
.cpa-video .cpa-video__frame img,
.cpa-video .cpa-video__frame picture {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0;
  transition: transform 500ms ease-out;
}
.cpa-video__frame::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(48,49,48,0) 45%, rgba(48,49,48,0.72) 100%);
  pointer-events: none;
}
.cpa-video .cpa-video__frame:hover img { transform: scale(1.03); }

.cpa-video__play {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 74px; height: 74px;
  border-radius: 50%;
  background: var(--cpa-blue-action);
  color: var(--fg-on-blue);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 28px rgba(48, 49, 48, 0.35);
  transition: transform var(--transition-base), background var(--transition-base);
  z-index: 2;
}
.cpa-video__play svg { margin-left: 3px; }
.cpa-video__frame:hover .cpa-video__play {
  transform: translate(-50%, -50%) scale(1.08);
  background: var(--cpa-blue-hover);
}
.cpa-video__frame:focus-visible { outline: 3px solid var(--cpa-blue); outline-offset: 3px; }

.cpa-video__label {
  position: absolute;
  left: var(--space-6); right: var(--space-6); bottom: var(--space-5);
  z-index: 2;
  text-align: left;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-paper);
}
.cpa-video__caption {
  margin-top: var(--space-4);
  font-size: 15px;
  line-height: 1.6;
  color: var(--fg-secondary);
}

.cpa-video__iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius-card);
  display: block;
}

@media (max-width: 767px) {
  .cpa-video__play { width: 58px; height: 58px; }
  .cpa-video__label { font-size: 16px; left: var(--space-4); right: var(--space-4); bottom: var(--space-4); }
}

/* --------------------------------------------------------------------------
   10c · BEFORE / AFTER COMPARISON
   -------------------------------------------------------------------------- */
.compare {
  margin-top: var(--space-10);
  border: 1px solid var(--cpa-line);
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--cpa-paper);
}
.compare__head,
.compare__row {
  display: grid;
  grid-template-columns: 1fr 1.3fr 1.3fr;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
}
.compare__head {
  background: var(--cpa-blue-mist);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cpa-blue-deep);
}
.compare__row + .compare__row,
.compare__head + .compare__row { border-top: 1px solid var(--cpa-line); }
.compare__label { font-weight: 700; font-size: 15px; }
.compare__before { color: var(--cpa-ink); font-size: 15px; font-weight: 600; }
.compare__after { color: var(--cpa-ink-2); font-size: 15px; }
.compare__mobile-label { display: none; }

@media (max-width: 767px) {
  .compare__head { display: none; }
  .compare__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2);
    padding: var(--space-5);
  }
  .compare__mobile-label {
    display: block;
    /* Was 11px, the only sub-12px text on the site. At 0.08em tracking on
       uppercase it stays visually subordinate to the row value at 12px. */
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--cpa-blue-deep);
    margin-bottom: 2px;
  }
  .compare__before { padding-top: var(--space-2); }
}

/* --------------------------------------------------------------------------
   10d · REVIEWS WIDGET
   Trustindex ships its own styling, so this only handles how the widget sits
   on the page: constrained to the container, with breathing room above and
   below rather than colliding with the section edges.
   -------------------------------------------------------------------------- */
.reviews-band { padding-block: var(--space-12); }
.reviews-band .ti-widget { margin: 0 !important; }
.reviews-band .ti-widget-container { max-width: 100%; }

/* --------------------------------------------------------------------------
   10e · ACCREDITATION BADGES
   Both marks are wordmarks on white, so they sit on a light card rather than
   floating. Heights are matched by eye rather than by file dimensions, since
   the two logos have very different aspect ratios.
   -------------------------------------------------------------------------- */
.badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6) var(--space-10);
}
.badges__item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-width: 0;
}
/* The two marks have very different aspect ratios: NETCC is a square roundel,
   SAA a wide wordmark. Matching by height alone makes the roundel dominate,
   so each gets its own box and is contained within it. */
.badges__item img {
  height: 46px;
  width: auto;
  max-width: 190px;
  flex: none;
  object-fit: contain;
  object-position: left center;
}
.badges__item:first-child img { max-width: 62px; }
.badges__label {
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--fg-secondary);
  max-width: 26ch;
}

/* Compact: logos only, for tight spaces like form panels. */
.badges--compact { gap: var(--space-6); }
.badges--compact .badges__item img { height: 34px; }

/* Light: for the ink footer. The marks are dark navy, so they need a white
   plate rather than an invert, which would turn them grey mush. */
.badges--light .badges__item {
  background: var(--cpa-paper);
  border-radius: 10px;
  padding: 10px 14px;
}
/* On dark grounds the SAA wordmark is knocked out to white. The NETCC
   roundel is NOT monochrome, so inverting it produced a solid white disc with
   no detail; it already sits on its own white circle, so it is left alone.

   Sizes are set against the Trustindex badge that sits beside them, which is
   a card roughly 100px tall. Matching by raw pixel height would leave the
   wordmark looking small, so these are balanced optically instead. */
/* Alignment approach: rather than matching heights per logo and hoping the
   centres line up, every item gets an identical fixed-height box and the
   artwork is contained inside it. Each mark has different internal padding
   and a different aspect ratio, so setting heights individually could never
   guarantee a shared centre line. */
.badges.badges--light { gap: var(--space-6); align-items: center; }
.badges.badges--light .badges__item {
  background: transparent;
  padding: 0;
  align-items: center;
  justify-content: center;
  height: 62px;
  line-height: 0;
}
.badges.badges--light .badges__item img {
  height: auto;
  width: auto;
  max-height: 100%;
  max-width: 200px;
  object-fit: contain;
  object-position: center;
  display: block;
  filter: brightness(0) invert(1);
  opacity: 0.94;
}
/* The roundel is square, so it fills the box height; the wordmark is wide and
   is held back a little so the two read as equal weight rather than equal
   height. */
.badges.badges--light .badges__item:first-child img {
  max-width: 62px;
  filter: none;
  opacity: 1;
}
.badges.badges--light .badges__item:last-child img { max-height: 82%; }
.badges.badges--light .badges__label { display: none !important; }

@media (max-width: 480px) {
  .badges { gap: var(--space-5); }
  .badges__item img { height: 38px; }
  .badges__label { font-size: 13px; }
}

/* Feature band: logos only, large, centred. No explanation. */
.badges--feature { display: flex; justify-content: center; }
.badges--feature .badges__item img { height: 78px; max-width: 260px; }
.badges--feature .badges__item:first-child img { max-width: 88px; }
.badges--feature .badges__label { display: none; }
.badges--feature .badges { gap: clamp(2rem, 5vw, 5rem); }
@media (max-width: 480px) {
  .badges--feature .badges__item img { height: 54px; }
  .badges--feature .badges__item:first-child img { max-width: 62px; }
}

/* Finance partner logos */
.finance-logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin-top: var(--space-8);
}
.finance-logos img { height: 48px; width: auto; object-fit: contain; }
@media (max-width: 480px) { .finance-logos img { height: 36px; } }

/* Closing CTA with a form: the copy column narrows and the panel sits beside
   it, rather than the two-button layout used on every other page. */
.cta-band--form .cta-band__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr); align-items: center; }
.cta-band--form .form-panel { padding: var(--panel-pad); }

/* The band sets white type for its own copy. Inside the panel that would be
   white on white, which is why the form read as blank. Everything within the
   panel is reset to normal ink. */
.cta-band .form-panel,
.cta-band .form-panel p,
.cta-band .form-panel label,
.cta-band .form-panel .elementor-field-label,
.cta-band .form-panel .elementor-heading-title,
.cta-band .form-panel h2,
.cta-band .form-panel h3 { color: var(--cpa-ink); }
.cta-band .form-panel .elementor-widget-text-editor p { color: var(--fg-secondary); }
.cta-band .form-panel a { color: var(--cpa-blue-deep); }

@media (max-width: 1024px) {
  .cta-band--form .cta-band__inner { grid-template-columns: minmax(0, 1fr); }
}

/* Steps: numbered cards with a consistent internal rhythm, and a connecting
   line across the row on wide screens so they read as a sequence. */
.step-card { background: var(--cpa-paper); position: relative; gap: var(--space-3); }
.step-card .cpa-h4 { margin-top: var(--space-5); }
.step-card p { color: var(--fg-secondary); }
.steps { counter-reset: none; position: relative; }
@media (min-width: 1025px) {
  .steps::before {
    content: "";
    position: absolute;
    top: calc(var(--space-6) + 32px);
    left: 12%;
    right: 12%;
    height: 2px;
    background: repeating-linear-gradient(90deg, var(--cpa-line) 0 8px, transparent 8px 16px);
    z-index: 0;
  }
  .step-card { z-index: 1; }
}
.steps-video { max-width: 940px; margin-inline: auto; width: 100%; }

/* Finance: copy on the left, partner logo on the right, rather than a centred
   block of text that had nothing to anchor it. */
.finance-card {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 4vw, 4rem);
  padding: clamp(2rem, 3vw, 3.5rem);
  border-radius: var(--radius-card);
  background: var(--cpa-blue-wash);
}
.finance-card__copy > * + * { margin-top: var(--space-4); }
.finance-card__logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-8);
  background: var(--cpa-paper);
  border-radius: var(--radius-card);
  text-align: center;
}
.finance-card__logo img { height: 52px; width: auto; object-fit: contain; }
@media (max-width: 900px) {
  .finance-card { grid-template-columns: minmax(0, 1fr); }
}

/* Partner and accreditation marquee.
   The logo set is output twice and the track animates by exactly -50%, so the
   second set arrives where the first began and the loop is seamless. Edges are
   masked so logos fade in and out rather than being cut off. */
.partner-band {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.partner-band__track {
  display: flex;
  width: max-content;
  animation: cpa-marquee 42s linear infinite;
}
.partner-band:hover .partner-band__track,
.partner-band:focus-within .partner-band__track { animation-play-state: paused; }

.partner-band__set {
  display: flex;
  align-items: center;
  gap: clamp(2.5rem, 5vw, 5rem);
  padding-right: clamp(2.5rem, 5vw, 5rem);
  flex: none;
}
.partner-band__item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 64px;
  flex: none;
  line-height: 0;
}
.partner-band__item img {
  height: auto;
  width: auto;
  max-height: 100%;
  max-width: 190px;
  object-fit: contain;
  display: block;
}
/* Square marks would otherwise fill the box height and dominate the wordmarks
   sitting beside them. */
.partner-band__item--square img { max-height: 88%; max-width: 72px; }
/* Vertical lockups (mark stacked over wordmark) need more height than a
   wordmark to keep the type legible, so the box grows for them alone. */
.partner-band__item--tall { height: 84px; }
.partner-band__item--tall img { max-width: 110px; }
.partner-band--dark .partner-band__item img { filter: brightness(0) invert(1); opacity: 0.94; }
.partner-band--dark .partner-band__item--square img { filter: none; opacity: 1; }

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

/* Reduced motion: no scroll. The set wraps and centres instead, and the
   duplicate is removed so nothing is repeated on screen. */
@media (prefers-reduced-motion: reduce) {
  .partner-band { -webkit-mask-image: none; mask-image: none; }
  .partner-band__track { animation: none; width: 100%; }
  .partner-band__set { flex-wrap: wrap; justify-content: center; padding-right: 0; }
  .partner-band__set[aria-hidden="true"] { display: none; }
}

@media (max-width: 900px) {
  .partner-band__item { height: 46px; }
  .partner-band__track { animation-duration: 30s; }
}

/* Finance: one line, one CTA, no disclaimers. The conditions live on
   /payment-options/ where someone is actually reading them. */
.finance-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-5);
  padding: clamp(2rem, 3vw, 3rem);
  border-radius: var(--radius-card);
  background: var(--cpa-blue-wash);
}
.finance-strip .cpa-h3 { flex: 1 1 100%; }
.finance-strip .cpa-lead { flex: 1 1 100%; max-width: 56ch; margin-inline: auto; }

/* Alternating feature rows: the replacement for the six-tile grid. Media on
   one side, copy on the other, flipping each row. Six equal tiles of body copy
   read as a wall and get skipped; this gives each point room and lets real
   photography do some of the work. */
.feature-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 4vw, 4.5rem);
}
.feature-row + .feature-row { margin-top: clamp(2.5rem, 5vw, 5rem); }
.feature-row--flip .feature-row__media { order: 2; }
.feature-row__copy > * + * { margin-top: var(--space-4); }
.feature-row__index {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2rem, 1.3rem + 2.8vw, 3.25rem);
  line-height: 1;
  letter-spacing: 0.04em;
  /* Was brand blue at 0.5, composited to #80CBE6 at 1.81:1. */
  color: var(--cpa-blue-numeral);
  margin-bottom: var(--space-3);
}
.feature-row__copy .cpa-h3 { font-size: clamp(1.75rem, 1.25rem + 2.2vw, 2.75rem); }
.feature-row__copy .cpa-lead {
  font-size: clamp(1.0625rem, 0.96rem + 0.5vw, 1.375rem);
  line-height: 1.55;
  max-width: 44ch;
}

/* The process block sits inside the benefits section, so it needs its own
   separation rather than relying on the section padding. */
.steps-block { margin-top: clamp(3rem, 6vw, 5rem); padding-top: clamp(2.5rem, 5vw, 4rem); border-top: 1px solid var(--cpa-line); }
.steps-block .section__head { margin-bottom: var(--space-10); }

/* Cross-sell card media. One fixed height for every card so a photograph and
   a product render sit as a matched pair, and no empty band between the image
   and the card body. */
.card__media--crosssell {
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(220px, 22vw, 300px);
  overflow: hidden;
  background: var(--cpa-band);
}
.card__media--crosssell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Product renders are transparent PNGs and should not be cropped. */
.card__media--crosssell img[src$=".png"] {
  width: 82%;
  height: 82%;
  object-fit: contain;
}

/* Product renders. Contained, not cropped, and normalised to one height so
   two images with different native aspect ratios still read as a matched
   pair. Photographs use the cropping treatment; renders do not. */
.card__media--product {
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(240px, 26vw, 340px);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: var(--cpa-paper);
}
.card__media--product img {
  /* Fill the frame and let object-fit do the scaling. Sizing with max-height
     alone left a small render displaying at its natural size while a larger
     one filled the card, so the two read as different scales. */
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.products-note {
  margin-top: var(--space-8);
  text-align: center;
  font-size: 15px;
  line-height: 1.6;
  color: var(--fg-secondary);
  max-width: 72ch;
  margin-inline: auto;
}
/* Product renders inside a feature row. Contained on a light ground at 60%
   of the frame, so a transparent PNG reads as a product shot rather than
   being cropped to fill like a photograph. */
.feature-row__product {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4/3;
  border-radius: var(--radius-card);
  background: var(--cpa-band);
}
.feature-row__product img {
  width: 70%;
  height: 70%;
  object-fit: contain;
  display: block;
}

.feature-row__media img,
.feature-row__media > div { border-radius: var(--radius-card); }
/* Top gap matched to the .feature-row + .feature-row rhythm. Without it the
   accordion falls back to .stack--lg's 32px, so the last feature row and the
   accordion sat at less than half the spacing of the rows above them. */
.feature-extra { max-width: 900px; margin-inline: auto; width: 100%; margin-top: clamp(2.5rem, 5vw, 5rem); }

@media (max-width: 900px) {
  .feature-row { grid-template-columns: minmax(0, 1fr); }
  .feature-row--flip .feature-row__media { order: 0; }
}

/* Federal rebate block */
.rebate-block {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 4rem);
  align-items: start;
  padding: clamp(2rem, 3.5vw, 3.5rem);
  border-radius: var(--radius-card);
  background: var(--cpa-blue-wash);
}
.rebate-block__copy > * + * { margin-top: var(--space-4); }
.rebate-block__figure {
  font-size: var(--fs-h4);
  line-height: 1.4;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-card);
  background: var(--cpa-paper);
}
.rebate-block__facts { list-style: none; display: grid; gap: var(--space-4); }
.rebate-block__facts li {
  padding: var(--space-5);
  border-radius: var(--radius-card);
  background: var(--cpa-paper);
  font-size: 15px;
  line-height: 1.55;
  color: var(--fg-secondary);
}
.rebate-block__facts strong {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--cpa-ink);
  font-size: 16px;
}
.rebate-block__note {
  margin-top: var(--space-5);
  font-size: 13px;
  line-height: 1.55;
  color: var(--cpa-ink-2);
  max-width: 80ch;
}
@media (max-width: 900px) {
  .rebate-block { grid-template-columns: minmax(0, 1fr); }
}

/* Vertical timeline. A connecting rule runs down the left with a marker per
   step, so a seven-stage growth story reads as a sequence rather than as a
   row of cards squeezed to fit. */
.timeline { list-style: none; position: relative; padding-left: var(--space-8); }
.timeline::before {
  content: "";
  position: absolute;
  left: 7px; top: 6px; bottom: 6px;
  width: 2px;
  background: var(--cpa-line);
}
.timeline__item { position: relative; }
.timeline__item + .timeline__item { margin-top: var(--space-10); }
.timeline__marker {
  position: absolute;
  left: calc(var(--space-8) * -1);
  top: 4px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--cpa-blue);
  box-shadow: 0 0 0 4px var(--cpa-paper);
}
.timeline__when {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cpa-blue-deep);
  margin-bottom: var(--space-2);
}
.timeline__body p { margin-top: var(--space-3); color: var(--fg-secondary); }

/* Steps as a single horizontal flow rather than three equal cards. Three
   boxes of body copy in a row read as a wall; a numbered flow with a rule
   between the items reads as a sequence you can follow. */
.steps-flow {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0;
  counter-reset: none;
}
.steps-flow__item {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  padding: var(--space-6) var(--space-8);
}
.steps-flow__item + .steps-flow__item { border-left: 1px solid var(--cpa-line); }
.steps-flow__num {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 40px;
  line-height: 1;
  /* aria-hidden, but still visible text, so SC 1.4.3 applies. 0.4 opacity on
     brand blue composited to #99D5EB at 1.61:1. */
  color: var(--cpa-blue-numeral);
  flex: none;
}
.steps-flow__body p { margin-top: var(--space-3); color: var(--fg-secondary); font-size: 15px; }
@media (max-width: 900px) {
  .steps-flow { grid-template-columns: minmax(0, 1fr); }
  .steps-flow__item { padding: var(--space-5) 0; }
  .steps-flow__item + .steps-flow__item { border-left: 0; border-top: 1px solid var(--cpa-line); }
}

/* Rebate: one short strip, centred, no supporting columns. */
.rebate-strip {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-5);
  padding: clamp(2.5rem, 4vw, 4rem) clamp(1.5rem, 3vw, 3rem);
  border-radius: var(--radius-card);
  background: var(--cpa-blue-wash);
}
.rebate-strip .cpa-h2 { max-width: 22ch; }
.rebate-strip .cpa-lead { max-width: 56ch; }

/* Video cue: sized as a heading so the video has a visible invitation rather
   than sitting there unexplained. */
.video-cue {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.25;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-blue-deep);
  transition: color var(--transition-fast);
}
.video-cue:hover { color: var(--cpa-blue); }
.video-cue svg { flex: none; }

/* Rebate headline: larger than a section heading, because the point of the
   strip is the announcement rather than the explanation. */
.rebate-strip__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2rem, 1.3rem + 3vw, 3.5rem);
  line-height: 1.05;
  letter-spacing: var(--tracking-display);
  max-width: 20ch;
}

/* Breathing room around the partner carousel. The section wrapper supplies
   its own padding, so the space is set on the section rather than the band. */
.partner-band { margin-block: var(--space-6); }
.section--mist:has(.partner-band) { padding-block: var(--space-16); }

/* Breadcrumbs hidden by request.
   The markup stays in place: it is a labelled <nav>, so screen reader users
   keep the landmark, and the BreadcrumbList structured data in inc/schema.php
   is separate, so Google can still show the trail in search results. Delete
   this rule to bring them back. */
nav[aria-label="Breadcrumb"] { display: none; }

/* Gallery: image strips inside long-form content. Captions are optional and
   sit outside the frame so they do not obscure the photograph. */
.gallery-item { margin: 0; }
.gallery-item figcaption {
  margin-top: var(--space-3);
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--cpa-ink-2);
}
.gallery-item > div { border-radius: var(--radius-card); overflow: hidden; }

/* Case study result: the figure is the argument, so it is set at display size
   and everything else supports it. Specs run as a metadata row rather than a
   sentence, because they signal competence without needing to be read. */
.result { align-items: center; gap: clamp(2rem, 4vw, 4.5rem); }
.result--flip .result__media { order: 2; }
.result__body > * + * { margin-top: var(--space-5); }
.result__body p { color: var(--fg-secondary); max-width: 46ch; }

.result__figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-block: var(--space-2);
}
.result__value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.5rem, 1.6rem + 3.6vw, 4.25rem);
  line-height: 1;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-blue);
  white-space: nowrap;
}
.result__label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cpa-ink-2);
}

.result__spec {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
}
.result__spec li {
  font-size: 13px;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--cpa-line);
  color: var(--cpa-ink-2);
  white-space: nowrap;
}

@media (max-width: 900px) {
  .result { grid-template-columns: minmax(0, 1fr); }
  .result--flip .result__media { order: 0; }
}

/* Service page hero: photographic, single column, no competing CTA buttons.
   The header already carries the quote button.

   Note: .hero--photo is shared with the home page hero, so nothing here may
   change the look of an existing hero. A page that needs a heavier scrim
   because its background is bright opts in with .hero--scrim instead. */
.hero--scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    rgba(4, 26, 36, 0.82) 0%,
    rgba(4, 26, 36, 0.68) 34%,
    rgba(4, 26, 36, 0.3) 62%,
    rgba(4, 26, 36, 0.18) 100%
  );
  pointer-events: none;
  z-index: 1;
}
.hero--scrim > .wrap { position: relative; z-index: 2; }
/* Service hero copy column. Wider than the running-text measure because a
   34rem column pushed short headings onto four lines. The heading gets a
   tighter line height than body copy, so three lines sit as a block rather
   than a loose stack. */
.hero__copy { max-width: 44rem; }
.hero__copy .cpa-h1 { line-height: 1.06; }
.hero__copy .cpa-lead { max-width: 38rem; }
@media (max-width: 900px) {
  .hero__copy { max-width: 100%; }
}

/* Service hero copy column. Wider than the running-text measure because a
   34rem column pushed short headings onto four lines. The heading gets a
   tighter line height than body copy, so three lines sit as a block rather
   than a loose stack. */
.hero__copy { max-width: 44rem; }
.hero__copy .cpa-h1 { line-height: 1.06; }
.hero__copy .cpa-lead { max-width: 38rem; }
@media (max-width: 900px) {
  .hero__copy { max-width: 100%; }
}

.hero--photo .hero-trust { margin-top: var(--space-6); }
.hero--photo .cpa-meta,
.hero--photo .cpa-meta a { color: rgba(255, 255, 255, 0.85); }
.hero--photo .cpa-eyebrow { color: var(--cpa-blue-bright); }

/* Illustrative cost chart. Bars are CSS heights rather than an image so they
   scale, theme correctly and stay legible at any width. The y-axis is labelled
   without numbers, and the x-axis says "indicative trend", so the chart
   describes what it is without needing a disclaimer under it. */
.cost-chart { margin: 0; }
.cost-chart__plot { display: flex; gap: var(--space-3); align-items: stretch; }
.cost-chart__y-axis {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  align-self: center;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Was --cpa-ink-3 (#787A78, 4.32:1). The token file marks ink-3 as
     decorative only; this is a 12px axis label, which is text. */
  color: var(--cpa-ink-2);
  white-space: nowrap;
}
.cost-chart__x-axis {
  display: block;
  margin-top: var(--space-4);
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* Same as the y-axis: ink-3 is decorative-only and fails at 12px. */
  color: var(--cpa-ink-2);
}
.cost-chart__bars {
  flex: 1;
  display: flex;
  align-items: flex-end;
  gap: clamp(0.5rem, 1.2vw, 1rem);
  height: clamp(220px, 26vw, 320px);
  padding: var(--space-6) var(--space-5) 0;
  border-radius: var(--radius-card);
  background: var(--cpa-band);
}
.cost-chart__col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  gap: var(--space-3);
}
.cost-chart__bar {
  width: 100%;
  border-radius: 6px 6px 0 0;
  background: var(--cpa-blue-tint);
}
.cost-chart__bar.is-now { background: var(--cpa-blue); }
.cost-chart__year {
  font-size: 12px;
  font-weight: 600;
  color: var(--cpa-ink-2);
  padding-bottom: var(--space-4);
}

/* Trust band: the review badge and accreditation marks as their own strip,
   rather than crowding the hero.

   Sizing is balanced by eye rather than by a shared height. The Trustindex
   badge is a wide card with its own padding, the NETCC mark is a small
   roundel, and the wordmarks are wide and short. Matching them on height
   alone left the badge dominating and the roundel almost invisible. */
.trust-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(1.75rem, 4vw, 3.5rem);
}
.trust-band .hero-trust__widget { transform: scale(0.88); transform-origin: center; }
.trust-band .badges { gap: clamp(1.75rem, 3.5vw, 3rem); align-items: center; }
.trust-band .badges__item { height: 64px; }
.trust-band .badges__item img { max-height: 100%; max-width: 220px; }
.trust-band .badges__item:first-child img { max-width: 64px; max-height: 64px; }
@media (max-width: 600px) {
  .trust-band .hero-trust__widget { transform: scale(0.8); }
  .trust-band .badges__item { height: 46px; }
}

/* Product specs. Figures large enough to scan, points and awards beneath,
   and a note stating where the numbers come from. */
/* Specs intro. With a product image or awards supplied it runs as two columns,
   so the heading block does not leave the right half of the section empty.
   The left column is weighted heavier: a product name and its argument should
   dominate the credential list beside it, not match it. */
.specs__intro { margin-bottom: var(--space-12); }
.specs__intro--split {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
.specs__intro .cpa-h2 {
  font-size: clamp(2.25rem, 1.5rem + 3.2vw, 3.75rem);
  max-width: 100%;
}
.specs__intro .cpa-lead {
  font-size: clamp(1.0625rem, 0.98rem + 0.45vw, 1.3125rem);
  max-width: 44ch;
}
.specs__logo { height: 40px; width: auto; margin-bottom: var(--space-5); display: block; }

/* Product names are short and read badly broken across two lines in the
   narrower split column. Balance the break rather than forcing nowrap, so a
   longer name still wraps instead of overflowing on a phone. */
.specs__intro--split .section__head h2 { text-wrap: balance; }
@media (min-width: 1100px) {
  .specs__intro--split .section__head h2 { white-space: nowrap; }
}

/* Optional diagram inside a checklist block. Copy left, image right. The image
   is centred against the list rather than pinned to the top, because the list
   column is much taller than the image; it stays put on scroll like every other
   image on the site. Collapses to a single column below 900px. */
.checklist--split {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: var(--space-12);
  align-items: center;
}
.checklist--split .checklist__body { min-width: 0; }
.checklist__figure {
  margin: 0;
}
.checklist__figure img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-card);
}
.checklist__figure figcaption {
  margin-top: var(--space-3);
  font-size: 14px;
  color: var(--fg-secondary);
}
/* Key facts, Home Energy Saver loan page. The numbers are the offer, so they
   lead the page. Stacked as a single ticked list rather than tiles: it reads
   faster on a phone, keeps all four facts in one scannable column at every
   width, and avoids the two-up grid that forced "No fees on the loan" and
   "Up to $15,000 per property" into cramped half-width boxes. */
.keyfacts {
  list-style: none;
  padding: 0;
  margin: 0 auto;
  max-width: 34rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.keyfacts li {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.45rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--cpa-ink);
  font-synthesis-weight: none;
}
/* Tick drawn in CSS so the markup stays a plain list and the facts are read
   as text by screen readers, not preceded by an image announcement. */
.keyfacts li::before {
  content: "";
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--cpa-blue-wash) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23005F82' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center / 16px no-repeat;
}

.keyfacts__note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-6);
  text-align: center;
  font-size: 15px;
  line-height: 1.5;
  color: var(--fg-secondary);
}
.keyfacts__logo { height: 22px; width: auto; }

/* ==========================================================================
   HOME ENERGY SAVER LOAN PAGE, ON A PHONE
   Scoped to page-id-5631 so no other page moves. At 390px wide the page ran
   to roughly 13,850px, about sixteen screens. Nothing overflowed; it was
   simply long, because every block that sits side by side on desktop stacked
   into its full-width form. These rules tighten the four worst offenders.
   ========================================================================== */
@media (max-width: 700px) {

  /* Split sections. The inline gap between image and copy is --space-16, which
     is right side by side and far too much once they stack. The image wrapper
     also carries an inline 4:3 ratio; 16:9 is enough to show an install on a
     phone. Both are inline styles, hence !important. */
  .page-id-5631 .grid--2[style*="gap"] { gap: var(--space-6) !important; }
  .page-id-5631 .grid--2 > div > div[style*="aspect-ratio"] { aspect-ratio: 16 / 9 !important; }

  /* Case study cards: thumbnail beside the copy instead of a full-width photo
     above it. Cuts each card to roughly a third of its stacked height. */
  .page-id-5631 .grid--3 > .card--flush {
    display: grid;
    grid-template-columns: 108px minmax(0, 1fr);
    align-items: stretch;
  }
  .page-id-5631 .grid--3 > .card--flush .card__media {
    /* The media wrapper carries an inline aspect-ratio:16/10. Left in force,
       it derived the width from the row height (222px x 1.6 = 355px), so the
       photo burst out of its 108px column and sat over the text. !important
       because the ratio is inline. */
    aspect-ratio: auto !important;
    width: auto;
    min-width: 0;
    height: 100%;
    min-height: 108px;
  }
  .page-id-5631 .grid--3 > .card--flush .card__body { padding: var(--space-4); }

  /* How it works: step number beside the title rather than a 64px disc on a
     line of its own above it. */
  .page-id-5631 ol.grid--4 > li.card {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    column-gap: var(--space-4);
    align-items: start;
    padding: var(--space-5);
  }
  .page-id-5631 ol.grid--4 > li.card .step {
    width: 40px;
    height: 40px;
    font-size: 15px;
    grid-row: span 2;
  }
  .page-id-5631 ol.grid--4 > li.card > h3 { margin-top: 0 !important; }

  /* Stats: two up instead of four stacked. The figure size comes down so
     "250,000+" still fits a half-width card. */
  .page-id-5631 .stat-band { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  .page-id-5631 .stat-band .stat { padding: var(--space-4); }
  .page-id-5631 .stat-band .stat__num { font-size: 1.6rem; }
}

@media (max-width: 900px) {
  .checklist--split { grid-template-columns: minmax(0, 1fr); gap: var(--space-9); align-items: start; }
}

/* Checklist items with their own thumbnail. A media row rather than a card
   grid: on a phone four cards means four screens of scrolling, where four
   rows stay scannable and the picture still does its job. The tile is a
   fixed square so mixed source aspect ratios line up down the left edge. */
.checklist__item--media {
  display: flex;
  gap: var(--space-5);
  align-items: center;
}
.checklist__thumb {
  flex: none;
  width: 88px;
  height: 88px;
  border-radius: var(--radius-input);
  overflow: hidden;
  background: var(--cpa-blue-wash);
}
.checklist__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
@media (min-width: 700px) {
  .checklist__thumb { width: 112px; height: 112px; }
}
@media (max-width: 380px) {
  .checklist__thumb { width: 72px; height: 72px; }
  .checklist__item--media { gap: var(--space-4); }
}

/* Visible branch address on location pages. Mirrors the PostalAddress in the
   LocalBusiness schema, which Google requires to be visible to readers. */
.hub-nap {
  margin-top: var(--space-8);
  padding: var(--space-6);
  background: var(--cpa-blue-wash);
  border-radius: var(--radius-card);
}
.hub-nap .cpa-h4 { margin: 0 0 var(--space-3); }
.hub-nap__address { margin: 0; line-height: 1.7; }
.hub-nap__contact { margin: var(--space-3) 0 0; }

/* ==========================================================================
   FOOTNOTES
   Required disclaimers moved out of the running copy. The marker stays small
   but keeps a 24px tap target via padding, and the target note is briefly
   highlighted so you can see where you landed.
   ========================================================================== */
.fn { font-size: 0.7em; line-height: 0; vertical-align: super; }
.fn a {
  display: inline-block;
  color: var(--cpa-blue-text);
  text-decoration: none;
  /* Negative margin keeps the visual size of the marker while giving it a
     tappable box: measured at 15px tall before this, which is under the 24px
     minimum and fiddly on a phone. */
  padding: 13px 9px;
  margin: -13px -5px;
  border-radius: 3px;
}
.fn a:hover, .fn a:focus-visible { text-decoration: underline; }

.notes__back {
  display: inline-block;
  padding: 6px 8px;
  margin: -6px -4px;
}

.notes {
  margin: var(--space-6) 0 0;
  padding-left: 1.4em;
  font-size: 14px;
  line-height: 1.65;
  color: var(--fg-secondary);
}
.notes li { margin-bottom: var(--space-4); }
.notes li:last-child { margin-bottom: 0; }

/* Symbol markers (*, dagger) instead of numbers, matching the inline
   markers and the printed direct mail. The symbol sits in its own column so
   the note text aligns down the left edge. */
.notes--symbols {
  list-style: none;
  padding-left: 0;
}
.notes--symbols li {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
}
.notes__mark {
  flex: none;
  min-width: 1em;
  font-weight: 700;
  color: var(--fg-secondary);
}
.notes__text { min-width: 0; }

.notes li:target {
  background: var(--cpa-blue-wash);
  box-shadow: 0 0 0 6px var(--cpa-blue-wash);
  border-radius: 3px;
}
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Product renders vary wildly in aspect ratio: the cropped SigenStor is close
   to square, the full 1+6 stack is 1500x2500. Without a cap the tall one renders
   ~900px high beside ~300px of heading and intro, which reads as a tower of
   whitespace. Capping the height and centring keeps both shapes usable in the
   same slot. A tall thin object survives downscaling better than a wide one. */
.specs__hero img { max-height: 520px; width: auto; margin-inline: auto; display: block; }
@media (max-width: 900px) {
  .specs__hero img { max-height: 380px; }
}

/* ==========================================================================
   SPECTABLE
   Two-column label/value table for manufacturer spec sheets and the finance
   at-a-glance panel. Collapses to stacked rows on narrow screens rather than
   scrolling sideways, because a spec sheet is read top to bottom.
   ========================================================================== */
.spectable {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--space-8);
  font-size: 16px;
  line-height: 1.6;
}
.spectable thead th {
  text-align: left;
  font-family: var(--font-body);
  font-size: var(--fs-meta);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--fg-secondary);
  padding: 0 0 var(--space-3);
  border-bottom: 2px solid var(--cpa-line);
}
.spectable tbody th,
.spectable tbody td {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--cpa-line);
  vertical-align: top;
  text-align: left;
}
.spectable tbody th {
  font-weight: 600;
  color: var(--fg-primary);
  width: 38%;
  padding-right: var(--space-6);
}
.spectable tbody td { color: var(--fg-secondary); }
.spectable tbody tr:last-child th,
.spectable tbody tr:last-child td { border-bottom: 0; }
.spectable__note {
  margin-top: var(--space-5);
  font-size: 15px;
  color: var(--fg-secondary);
}

@media (max-width: 700px) {
  .spectable thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
  .spectable tbody tr { display: block; padding: var(--space-4) 0; border-bottom: 1px solid var(--cpa-line); }
  .spectable tbody tr:last-child { border-bottom: 0; }
  .spectable tbody th,
  .spectable tbody td { display: block; width: auto; padding: 0; border-bottom: 0; }
  .spectable tbody th { margin-bottom: var(--space-1); }
}

/* ==========================================================================
   BRANDCOMPARE
   Brand vs Hinen vs Sigenergy table on the brand capture pages, with the two
   recommended columns tinted. Collapses to one card per row below 760px,
   with the column name printed beside each value, so nothing scrolls
   sideways on a phone.
   ========================================================================== */
.brandcmp {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--cpa-paper);
  border: 1px solid var(--cpa-line);
  border-radius: var(--radius-card);
  overflow: hidden;
  font-size: 15px;
  line-height: 1.5;
}
.brandcmp th,
.brandcmp td {
  padding: var(--space-4) var(--space-5);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--cpa-line);
}
.brandcmp tbody tr:last-child th,
.brandcmp tbody tr:last-child td { border-bottom: 0; }
.brandcmp thead th {
  background: var(--cpa-blue-mist);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--cpa-ink-2);
}
.brandcmp thead th.is-pick {
  background: var(--cpa-blue-wash);
  color: var(--cpa-blue-deep);
  box-shadow: inset 0 3px 0 var(--cpa-blue);
}
.brandcmp tbody th { font-weight: 700; color: var(--fg-primary); width: 22%; }
.brandcmp tbody td { color: var(--fg-secondary); }
.brandcmp tbody td.is-pick { background: var(--cpa-blue-paper); color: var(--fg-primary); font-weight: 600; }

/* Fixed height, not aspect-ratio: an aspect-ratio box grows to fit a tall
   product render (the SigenStor stack is 1500x2500), which blew the tiles
   up to full-screen height. The image is contained inside the box. */
.brandcmp__media {
  height: 240px;
  overflow: hidden;
  background: var(--cpa-band);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.brandcmp__media img {
  display: block;
  width: auto;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}
.brandcmp__pick .card__body { gap: var(--space-3); }
@media (max-width: 760px) {
  .brandcmp__media { height: 200px; }
}

@media (max-width: 760px) {
  .brandcmp thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
  .brandcmp,
  .brandcmp tbody,
  .brandcmp tr,
  .brandcmp th,
  .brandcmp td { display: block; width: 100%; }
  .brandcmp tbody tr { border-bottom: 1px solid var(--cpa-line); padding: var(--space-4) var(--space-4) var(--space-3); }
  .brandcmp tbody tr:last-child { border-bottom: 0; }
  .brandcmp tbody th { width: auto; padding: 0 0 var(--space-2); border: 0; font-size: 16px; }
  .brandcmp tbody td {
    display: grid;
    grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: 6px;
  }
  .brandcmp tbody td::before {
    content: attr(data-label);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--cpa-blue-deep);
    padding-top: 2px;
  }
}

/* ==========================================================================
   LANDING TEMPLATE
   Single-purpose pages for offline campaigns. The site header and footer are
   reused for the logo, phone and legal line; everything that offers a way off
   the page is hidden, because the only action here is the form.
   ========================================================================== */
/* Everything that offers a route off the page goes: the primary nav and its
   toggle, the header quote button, the announcement strip, the footer link
   columns and the sitewide CTA band. The logo, the phone number, the legal
   line and the sticky mobile call bar all stay, because they either help the
   visitor convert or are required. */
.cpa-landing .nav,
.cpa-landing .nav-toggle,
.cpa-landing .site-header__cta,
.cpa-landing .announce,
.cpa-landing .site-footer__col,
.cpa-landing .reviews-band,
.cpa-landing .cta-band { display: none !important; }

/* With the nav gone the header row has a gap where the menu sat. */
.cpa-landing .site-header__inner { justify-content: space-between; }
.specs__hero {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1.5rem, 3vw, 3rem);
  border-radius: var(--radius-card);
  background: var(--cpa-paper);
}
.specs__hero img {
  width: 100%;
  max-width: 420px;
  height: auto;
  display: block;
}
@media (max-width: 900px) {
  .specs__intro--split { grid-template-columns: minmax(0, 1fr); }
}

.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-5);
}
.specs__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6);
  border-radius: var(--radius-card);
  /* White, not the band tint: on a banded section the card would otherwise be
     invisible and the figures would float with no containment. */
  background: var(--cpa-paper);
}
.specs__value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem);
  line-height: 1;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-blue);
}
.specs__label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cpa-ink-2);
}

.specs__points {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-4) var(--space-8);
  margin-top: var(--space-10);
  max-width: none;
}
.specs__points li { display: flex; gap: var(--space-3); align-items: flex-start; }
.specs__points span { font-size: 15px; line-height: 1.55; }

.specs__awards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-6);
  margin-top: var(--space-12);
  padding: var(--space-8);
  border-top: 0;
  border-radius: var(--radius-card);
  background: var(--cpa-paper);
}
.specs__award { display: flex; align-items: center; gap: var(--space-4); }
.specs__award img { height: 56px; width: auto; flex: none; object-fit: contain; }
.specs__award > div { display: flex; flex-direction: column; gap: 2px; }
.specs__award strong { font-size: 15px; color: var(--cpa-ink); }
.specs__award span { font-size: 14px; color: var(--fg-secondary); }

/* Independent certifications. Linked out to the manufacturer's own documents,
   because a claim you can check is worth more than one you cannot. */
.specs__awards--intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-top: 0;
  padding: 0;
  background: none;
}
.specs__awards--intro .specs__award { gap: var(--space-4); }
.specs__awards--intro .specs__award img { height: 52px; }
.specs__awards--intro .specs__award strong { font-size: 14.5px; line-height: 1.3; }
.specs__awards--intro .specs__award span { font-size: 13.5px; }

.specs__certs { margin-top: var(--space-10); }
.specs__certs-label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cpa-ink-2);
  margin-bottom: var(--space-4);
}
.specs__certs ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
}
.specs__certs li {
  font-size: 14px;
  font-weight: 600;
  padding: 9px 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--cpa-line);
  color: var(--cpa-ink-2);
  background: var(--cpa-paper);
}

.specs__note {
  margin-top: var(--space-8);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--cpa-ink-2);
  max-width: 80ch;
}

/* Badge captions suppressed where the marks speak for themselves. */
.hero-trust--bare .badges__caption,
.hero-trust--bare .badges__label,
.hero-trust--bare figcaption { display: none; }

/* Track record cards. White on the band so the figures read as claims with
   evidence behind them rather than floating numbers.

   Padding is a clamp rather than a spacing token: --space-7 is not defined in
   cpa-tokens.css, so the declaration was invalid and the cards rendered with
   no padding at all, letting the numbers sit outside the card edge. */
.stat-card {
  padding: clamp(1.5rem, 2.5vw, 2rem);
  border-radius: var(--radius-card);
  background: var(--cpa-paper);
}
.stat-card__num {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.875rem, 1.3rem + 2.2vw, 2.75rem);
  line-height: 1;
  letter-spacing: var(--tracking-display);
  color: var(--cpa-blue);
}
.stat-card__plus { font-size: 0.6em; vertical-align: super; margin-left: 1px; }
.stat-card__label {
  margin-top: var(--space-4);
  font-size: 15px;
  line-height: 1.45;
  color: var(--fg-secondary);
}

/* Quote page reassurance. Sets expectations about the call rather than
   selling the product: at the point of submitting a form the live question is
   who rings, when, and whether they will be hassled. */
.quote-promise { display: grid; gap: var(--space-6); }
.quote-promise > div { padding-left: var(--space-5); border-left: 3px solid var(--cpa-blue); }
.quote-promise .cpa-h4 { font-size: 17px; line-height: 1.4; }
.quote-promise p { margin-top: var(--space-2); color: var(--fg-secondary); font-size: 15px; line-height: 1.55; }

/* The location heroes reuse this block over a photo and scrim, where the ink
   heading and secondary body colour are both unreadable. Scoped to
   .hero--photo so the get-a-quote page, which has no photo, is unaffected. */
.hero--photo .quote-promise .cpa-h4 { color: var(--cpa-paper); }
.hero--photo .quote-promise p { color: rgba(255, 255, 255, 0.86); }
.hero--photo .quote-promise > div { border-left-color: var(--cpa-blue-bright); }

/* Brand names in the also-available row. Matches the weight and colour of the
   home page cross-link cluster so the two read as the same component. Linked
   to the brand capture pages since Oct 2026 (.cpa-meta--link). */
.cpa-meta--strong {
  font-weight: 700;
  color: var(--cpa-blue-deep);
}
/* Same look as the original plain-text names (Felix, 22 Oct 2026): no
   underline. Only a colour shift on hover, so they still feel clickable. */
a.cpa-meta--link,
a.cpa-meta--link:visited {
  text-decoration: none;
  color: var(--cpa-blue-deep);
  transition: color var(--transition-fast);
}
a.cpa-meta--link:hover,
a.cpa-meta--link:focus-visible {
  color: var(--cpa-blue-hover);
  text-decoration: none;
}

/* Split block media ratios are set per block via the split renderer's
   media_ratio / media_max keys, written inline by cpa_image(). A .split__media
   rule block used to live here; it targeted a class the theme never outputs,
   and an inline ratio would have beaten it regardless. Removed rather than
   left to be edited by someone expecting it to do something. */

/* --------------------------------------------------------------------------
   11 · UTILITIES + A11Y
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--cpa-ink); color: var(--cpa-paper);
  padding: 12px 20px; border-radius: 0 0 8px 0; font-weight: 700;
}
.skip-link:focus { left: 0; color: var(--cpa-paper); }

a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: 2px solid var(--cpa-blue); outline-offset: 2px; border-radius: 4px;
}

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

/* Sticky mobile CTA */
.cta-bar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 90;
  background: var(--cpa-paper); border-top: 1px solid var(--cpa-line);
  box-shadow: 0 -4px 16px rgba(48, 49, 48, 0.10);
  padding: 10px 14px; display: none; gap: 10px;
}
.cta-bar .btn { flex: 1 1 0; font-size: 15px; padding: 13px 12px; min-height: 46px; }
@media (max-width: 767px) {
  .cta-bar { display: flex; }
  body { padding-bottom: 74px; }
}

/* Podium webchat bubble sits bottom-left at roughly 20px from the viewport
   base, which lands inside the 74px band occupied by .cta-bar and covers the
   "Call us" button. Podium's dashboard only offers left/right, no vertical
   offset, so the bubble is lifted here instead. Selectors are deliberately
   broad because Podium injects its markup via JavaScript and its class names
   are not contractual. If Podium ever moves to a shadow root these rules stop
   matching and the bubble simply returns to its default position. */
@media (max-width: 767px) {
  iframe[src*="podium"],
  iframe[title*="odium"],
  div[id*="podium"],
  div[class*="podium"],
  #podium-website-widget,
  #podium-bubble {
    bottom: 88px !important;
  }
}

/* ==========================================================================
   Third-party contrast overrides (WCAG 2.2 AA, August 2026 audit)
   ==========================================================================
   Both blocks below fix colour-contrast failures in markup we do not own, so
   they cannot be fixed at source. Verified in the browser against the rendered
   DOM, not inferred from a scanner summary.

   These are the only remaining AA failures on the site. They need re-checking
   whenever Trustindex or Elementor updates, because a vendor class rename will
   silently drop the override and the failure returns with no warning.
   -------------------------------------------------------------------------- */

/* Trustindex "Read more" control.
 * Ships as #595959 at 50% alpha, which renders as roughly #ACACAC on white:
 * 2.27:1 at 13.5px, against a 4.5:1 requirement. The alpha is the whole
 * problem, which is why a scanner reading only the RGB triplet computes 7:1
 * and passes it. Dropping the alpha takes it to 7.0:1.
 * Appears on the home page, /referral/, /case-studies/ and at least one post. */
.ti-read-more-active,
.ti-widget .ti-read-more,
.ti-ai-summary-item .ti-read-more {
  color: #595959 !important;
  opacity: 1 !important;
}

/* Elementor multi-step form step indicators, used on /referral/.
 * Active was #39B54A on white (2.66:1) and inactive #C2CBD2 (1.64:1), both at
 * 13px where 4.5:1 is required. Replacements hold the same hue family so the
 * green still reads as "done" and the grey still reads as "not yet". */
.e-form__indicators__indicator {
  color: #6B7680 !important;          /* 1.64:1 -> 4.62:1 */
  border-color: #6B7680 !important;
}
.e-form__indicators__indicator--state-active,
.e-form__indicators__indicator.e-form__indicators__indicator--state-active {
  color: #2E7D3A !important;          /* 2.66:1 -> 4.63:1 */
  border-color: #2E7D3A !important;
}
.e-form__indicators__indicator--state-completed {
  color: #2E7D3A !important;
  border-color: #2E7D3A !important;
}

/* --------------------------------------------------------------------------
   TARGET SIZE — remaining 2.5.8 failures found in the mobile audit
   --------------------------------------------------------------------------
   Measured at a 360px viewport with a real mobile user agent. Inline links
   sitting inside a sentence are deliberately left alone: 2.5.8 exempts them,
   and padding them would break the prose line rhythm.
   -------------------------------------------------------------------------- */

/* Phone and email links in meta lines, e.g. the contact and quote pages.
   Measured 81x18px. */
.cpa-meta a {
  display: inline-block;
  padding-block: 4px;                 /* 18px -> 26px */
}

/* Trustindex ships these as bare inline spans and anchors: "Read more" at
   74x19px and the "Top Rated Service" header link at 136x21px. The widget is
   plugin-generated so it cannot be fixed at the markup level. cpa-overlay.css
   already recolours these nodes; it sets no box properties, so padding here
   does not collide with it. */
.ti-widget .ti-read-more,
.ti-widget .ti-read-more-active,
.ti-ai-summary-item .ti-read-more,
.ti-widget .ti-top-rated-title a {
  display: inline-block;
  padding-block: 4px;
}

/* --------------------------------------------------------------------------
   LAYOUT SHIFT — WP Rocket automatic lazy rendering
   --------------------------------------------------------------------------
   WP Rocket tags top-level <section> elements with data-wpr-lazyrender and
   applies content-visibility:auto to them, but sets no contain-intrinsic-size.

   Correction, 9 Sep: this is NOT the CLS cause. Measured shift culprits are
   form.elementor-form (0.1535) and iframe#podium-prompt (0.0618, a site-wide
   constant). Mobile CLS did not move at all when this rule was added.

   It is still worth setting, for a different reason: an unrendered section
   never resolves its ancestor background, so axe reads footer links as sitting
   on white and reports false contrast failures. Reserving enough space lets the
   section render and the false positives disappear. A flat 600px was roughly
   half-right on desktop and badly wrong on mobile, so it is now measured per
   breakpoint: median section height is 1420px at 412px wide and 921px at
   1350px wide. "auto" means the browser uses the real height after first paint,
   so these numbers only govern the first render.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   ANCHOR TARGETS
   --------------------------------------------------------------------------
   Zero-height targets emitted before a block by cpa_render_blocks(). The
   scroll margin clears the sticky header, so a deep link lands on the section
   heading rather than tucking it underneath the nav.
   -------------------------------------------------------------------------- */
.block-anchor {
  display: block;
  height: 0;
  scroll-margin-top: 120px;
}

[data-wpr-lazyrender] {
  contain-intrinsic-size: auto 1450px;
}

@media (min-width: 1024px) {
  [data-wpr-lazyrender] {
    contain-intrinsic-size: auto 950px;
  }
}

/* --------------------------------------------------------------------------
   LINK PURPOSE — links that relied on colour alone
   --------------------------------------------------------------------------
   Lighthouse flagged link-in-text-block: these anchors sit inside running text
   and were distinguished only by colour, which fails SC 1.4.1. Scoped to the
   two blocks where links genuinely sit in prose. Nav, buttons and card CTAs
   are deliberately excluded: they read as controls, not as inline links.
   -------------------------------------------------------------------------- */
.hub-nap__contact a,
.cpa-meta a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* --------------------------------------------------------------------------
   TRUSTINDEX REVIEW WIDGET — horizontal overflow
   --------------------------------------------------------------------------
   The widget lays its reviews out as one long flex row and relies on its own
   script to clip and paginate that row. The script is deferred, so on a cold
   load the raw row sets the document width: 4,380px on desktop and 20,911px
   on a phone, which gave every page carrying the widget (reviews, VPP,
   commercial, privacy) a sideways scrollbar and squashed the cards to ~70px
   columns of one-word lines.

   Clipping the widget and letting the row scroll inside itself fixes both:
   the page can no longer be wider than the viewport, and the cards keep a
   readable width whether or not the script ever initialises.
   -------------------------------------------------------------------------- */
.ti-widget {
  max-width: 100%;
  overflow: hidden;
}

.ti-widget .ti-reviews-container {
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.ti-widget .ti-review-item {
  flex: 0 0 auto;
  scroll-snap-align: start;
  /* A fixed card width at every size. Left to itself the item is either
     squashed to a 70px column of one-word lines (phone) or stretched to
     2,000px (desktop), because the widget sizes its cards against a row it
     expects its own script to have measured. */
  width: min(320px, 82vw);
}

/* Multi-step quote form, before Elementor's JS runs (Sep 2026).
   Elementor only wraps each step in .e-form__step once its (deferred) script
   initialises. Until then every field renders flat, so step 2 (name, phone,
   email, submit) showed underneath the tiles on first paint. Hide everything
   after the second step marker until the step wrappers exist. Single-step
   forms have no second marker, so they are untouched. */
.elementor-form:not(:has(.e-form__step)) .elementor-field-type-step ~ .elementor-field-type-step ~ .elementor-field-group,
.elementor-form:not(:has(.e-form__step)) .elementor-field-type-step ~ .elementor-field-type-step ~ .e-form__buttons {
  display: none;
}
