/* =========================================================
   Action Force Klerksdorp — global stylesheet
   Tokens derived from the source Divi site; see
   docs/rebuild-specification.md "Confirmed facts".
   ========================================================= */

/* 1. Variables ------------------------------------------- */
:root {
    --color-primary: #ED4421;
    --color-primary-hover: #544E70;
    --color-dark: #000724;
    --color-light: #FFFFFF;
    --color-accent: #FFC230;
    --color-text: #000724;
    --color-text-muted: #4A4E63;
    --color-border: #E3E4EA;

    --font-heading: 'Michroma', 'Segoe UI', Arial, sans-serif;
    --font-body: 'Ubuntu', 'Segoe UI', Arial, sans-serif;

    /* Single authoritative content grid: full-width section backgrounds,
       1500px inner container, responsive gutters (see .container below). */
    --container-width: 1500px;
    --container-gutter: 20px;

    --space-xs: 8px;
    --space-sm: 16px;
    --space-md: 24px;
    --space-lg: 40px;
    --space-xl: 64px;

    --section-spacing: clamp(56px, 5vw + 24px, 108px);

    --radius-sm: 0px;
    --radius-md: 0px;

    --transition-fast: 150ms ease;
    --transition-base: 250ms cubic-bezier(0.22, 1, 0.36, 1);
    --transition-slow: 350ms cubic-bezier(0.22, 1, 0.36, 1);
    /* Only for landing out of the block-bob hover animation (see main.css
       component rules below) — 250ms reads as an abrupt snap coming off a
       slow 2.2s float, so this is deliberately slower and gentler, used
       only for the transform property on those blocks. */
    --transition-bob-out: 550ms cubic-bezier(0.16, 1, 0.3, 1);

    --focus-ring: 3px solid var(--color-accent);

    /* Claymorphism surface system — soft moulded panels, not glass.
       See docs/rebuild-specification.md discussion in the homepage
       claymorphism brief: matte, dual-shadow, restrained radii.
       Shadow tones are neutral blue-grey rather than pure white/black —
       every component below reads its depth from these tokens, so keep
       the softening here rather than patching individual components. */
    --clay-surface: #F4F5F8;
    --clay-surface-sunken: #EAEBF1;
    --clay-surface-navy: #0B1230;
    --clay-shadow-dark: rgba(15, 23, 42, 0.13);
    --clay-shadow-light: rgba(223, 227, 235, 0.6);
    --clay-inset: rgba(15, 23, 42, 0.1);
    --clay-radius-sm: 14px;
    --clay-radius-md: 22px;
    --clay-radius-lg: 30px;

    --clay-raised: 8px 8px 18px var(--clay-shadow-dark), -6px -6px 14px var(--clay-shadow-light);
    --clay-raised-sm: 4px 4px 10px var(--clay-shadow-dark), -3px -3px 8px var(--clay-shadow-light);
    --clay-raised-hover: 11px 11px 24px var(--clay-shadow-dark), -7px -7px 16px var(--clay-shadow-light);
    --clay-pressed: inset 3px 3px 7px var(--clay-inset), inset -3px -3px 7px rgba(255, 255, 255, 0.45);
    --clay-raised-navy: 8px 8px 18px rgba(0, 0, 0, 0.3), -4px -4px 10px rgba(84, 78, 112, 0.16);
    --clay-raised-navy-hover: 10px 10px 22px rgba(0, 0, 0, 0.34), -5px -5px 12px rgba(84, 78, 112, 0.19);
}

/* 2. Reset -------------------------------------------------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

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

body,
h1, h2, h3, h4, p,
ul, ol, dl, dd,
figure {
    margin: 0;
}

ul[class],
ol[class] {
    list-style: none;
    padding: 0;
}

img {
    max-width: 100%;
    display: block;
    height: auto;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}

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

/* 3. Base ----------------------------------------------------- */
body {
    font-family: var(--font-body);
    color: var(--color-text);
    background: var(--color-light);
    line-height: 1.6;
}

a {
    color: var(--color-primary);
    text-decoration: underline;
    text-decoration-thickness: 1px;
}

a:hover {
    color: var(--color-primary-hover);
}

:focus-visible {
    outline: var(--focus-ring);
    outline-offset: 2px;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    background: var(--color-dark);
    color: var(--color-light);
    padding: var(--space-sm) var(--space-md);
}

.skip-link:focus {
    left: var(--space-sm);
    top: var(--space-sm);
}

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

/* 4. Typography ------------------------------------------------ */
h1, h2, h3, h4 {
    font-family: var(--font-heading);
    color: var(--color-dark);
    line-height: 1.25;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

h1 {
    font-size: clamp(1.75rem, 1.3rem + 2vw, 2.75rem);
}

h2 {
    font-size: clamp(1.4rem, 1.15rem + 1.1vw, 2rem);
    margin-bottom: var(--space-sm);
}

h3 {
    font-size: 1.15rem;
    margin-bottom: var(--space-xs);
}

p {
    max-width: 68ch;
}

/* 5. Layout ------------------------------------------------------ */
/* One authoritative container: full-width section backgrounds hold a
   1500px inner grid, with gutters that widen at each breakpoint so
   content never touches the viewport edge (16–24px mobile, 24–32px
   tablet, 40–60px desktop). Do not introduce alternate max-widths. */
.container {
    width: min(calc(100% - (var(--container-gutter) * 2)), var(--container-width));
    margin-inline: auto;
}

@media (min-width: 480px) {
    :root {
        --container-gutter: 24px;
    }
}

@media (min-width: 640px) {
    :root {
        --container-gutter: 28px;
    }
}

@media (min-width: 1024px) {
    :root {
        --container-gutter: 44px;
    }
}

@media (min-width: 1440px) {
    :root {
        --container-gutter: 60px;
    }
}

.section,
main > section {
    padding-block: var(--section-spacing);
}

/* 6. Header -------------------------------------------------------- */
.utility-bar {
    background: var(--color-primary);
}

.utility-bar__inner {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-md);
    padding-block: 6px;
}

.utility-bar__link {
    color: var(--color-light);
    text-decoration: none;
    font-size: 0.875rem;
}

.utility-bar__link:hover {
    color: var(--color-dark);
}

.site-header {
    background: var(--color-light);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: 50;
}

/* Symmetric outer tracks (1fr / auto / 1fr), not auto/1fr/auto: with
   auto/1fr/auto the nav only centres within whatever space is left over
   after the logo and actions group, so it visibly drifts toward whichever
   side is narrower (here, away from the wider actions group). Two equal
   1fr tracks either side of the auto-sized nav column instead pin the
   nav's centre to the header's true centre regardless of how wide the
   logo vs. actions group are — logo/actions are then pulled to their own
   outer edge with justify-self so they don't just sit centred in their
   own 1fr track. */
.site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--space-md);
    padding-block: 12px;
}

.site-header__logo {
    justify-self: start;
}

.site-header__logo img {
    height: 54px;
    width: 54px;
}

.site-header__actions {
    display: flex;
    align-items: center;
    justify-self: end;
    gap: var(--space-md);
}

/* 7. Navigation ------------------------------------------------------- */
.primary-nav__list {
    display: flex;
    justify-content: center;
    gap: var(--space-md);
}

.primary-nav__list a {
    position: relative;
    color: var(--color-dark);
    text-decoration: none;
    font-family: var(--font-heading);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding-block: var(--space-xs);
    transition: color var(--transition-fast);
}

.primary-nav__list a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 100%;
    bottom: 0;
    height: 2px;
    background: var(--color-primary);
    transition: right var(--transition-base);
}

.primary-nav__list a:hover,
.primary-nav__list a:focus-visible {
    color: var(--color-primary-hover);
}

.primary-nav__list a:hover::after,
.primary-nav__list a:focus-visible::after {
    right: 0;
}

.primary-nav__list a[aria-current="page"] {
    color: var(--color-primary);
}

.primary-nav__list a[aria-current="page"]::after {
    right: 0;
}

/* Header search — native <details>/<summary> icon toggle. Zero layout
   footprint when closed; the revealed form is an absolutely-positioned
   clay dropdown so it never pushes header content around when it opens. */
.site-search {
    position: relative;
}

.site-search__toggle {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised-sm);
    color: var(--color-dark);
    cursor: pointer;
    list-style: none;
    transition: color var(--transition-base), transform var(--transition-base), box-shadow var(--transition-base);
}

.site-search__toggle::-webkit-details-marker {
    display: none;
}

.site-search__toggle:hover,
.site-search__toggle:focus-visible {
    color: var(--color-primary);
    transform: translateY(-2px);
    box-shadow: var(--clay-raised);
}

.site-search[open] .site-search__toggle {
    color: var(--color-primary);
    box-shadow: var(--clay-pressed);
}

.site-search__form {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    width: 280px;
    background: var(--color-light);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised);
    padding: 6px;
}

.site-search__form input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    padding: var(--space-xs) var(--space-sm);
    font: inherit;
}

/* The site-wide gold :focus-visible outline (--focus-ring) reads as a
   jarring box around this small pill-shaped field sitting inside its own
   already-boxed dropdown — swap it for a softer ring in the field's own
   border radius instead of suppressing focus styling altogether. */
.site-search__form input:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(0, 7, 36, 0.15);
    border-radius: var(--clay-radius-sm, 8px);
}

.site-search__submit {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--color-primary);
    color: var(--color-light);
    transition: background-color var(--transition-base);
}

.site-search__submit:hover,
.site-search__submit:focus-visible {
    background: var(--color-primary-hover);
}

@media (prefers-reduced-motion: reduce) {
    .site-search__toggle:hover {
        transform: none;
    }
}

.nav-toggle {
    display: none;
    width: 44px;
    height: 44px;
    position: relative;
}

.nav-toggle::before,
.nav-toggle::after {
    content: "";
    position: absolute;
    left: 10px;
    right: 10px;
    height: 2px;
    background: var(--color-dark);
}

.nav-toggle::before {
    top: 16px;
}

.nav-toggle::after {
    bottom: 16px;
}

/* 8. Hero ------------------------------------------------------------------
   Full-width cinematic band; content is a centred composition capped to
   an intentional text measure (~960px) inside the 1500px site grid, per
   the hero-width rule — the container is wide, the copy is not. */
/* Height is viewport-aware (not the shared --section-spacing, which is
   tuned for normal content sections) so the hero scales with the window
   rather than sitting at a brittle fixed height — min-height is a floor,
   not a fixed height, so taller content (longer copy, a wrapped headline)
   simply grows the section. Sized for cinematic presence without going
   full-screen: comfortably short of 100vh at every common viewport. */
.hero {
    position: relative;
    background-color: var(--color-dark);
    background-size: cover;
    background-position: center;
    color: var(--color-light);
    padding-block: clamp(48px, 8vh, 96px);
    min-height: clamp(540px, 80vh, 780px);
    display: flex;
    align-items: center;
    overflow: hidden;
    isolation: isolate;
}

/* Overlay lightened (was 0.6/0.72/0.92) so the original Action Force
   photography stays clearly visible rather than reading as a flat navy
   background — text legibility is preserved instead via a stronger
   text-shadow on the heading/lede below, not by darkening the image. */
.hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, rgba(0, 7, 36, 0.52) 0%, rgba(0, 7, 36, 0.65) 45%, rgba(0, 7, 36, 0.88) 100%),
        radial-gradient(120% 90% at 50% 15%, rgba(0, 7, 36, 0.15) 0%, rgba(0, 7, 36, 0.52) 70%);
    z-index: 0;
}

.hero__inner {
    position: relative;
    z-index: 1;
    max-width: 960px;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.hero__eyebrow {
    display: inline-block;
    font-family: var(--font-heading);
    color: var(--color-accent);
    font-size: 1.05rem;
    letter-spacing: 0.18em;
    margin-bottom: 14px;
    text-shadow: 0 2px 12px rgba(0, 7, 36, 0.5);
}

.hero h1 {
    color: var(--color-light);
    font-size: clamp(2.9rem, 1.2rem + 6.4vw, 5.6rem);
    line-height: 1.08;
    max-width: 18ch;
    text-shadow: 0 4px 28px rgba(0, 7, 36, 0.65), 0 1px 3px rgba(0, 7, 36, 0.8);
}

.hero__lede {
    color: rgba(255, 255, 255, 0.92);
    max-width: 56ch;
    margin-inline: auto;
    margin-block: 16px 20px;
    font-size: 1.35rem;
    text-shadow: 0 2px 14px rgba(0, 7, 36, 0.6);
}

.hero__divider {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    margin-block: 12px;
    width: 200px;
}

.hero__divider::before,
.hero__divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255, 196, 48, 0.55));
}

.hero__divider::after {
    background: linear-gradient(90deg, rgba(255, 196, 48, 0.55), transparent);
}

.hero__divider span {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--clay-surface-navy);
    color: var(--color-accent);
    box-shadow: var(--clay-raised-navy);
}

.hero__actions {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    gap: var(--space-sm);
    margin-top: var(--space-md);
}

/* Equal width on both buttons — matched to whichever label needs more
   room ("Get A Free Consultation") — so with the row centred above, the
   gap between them lands exactly on the page's centre line instead of
   being pulled off-centre by the shorter "Our Services" button.
   flex:1 1 0 so they share space equally, and white-space:nowrap so
   "Get A Free Consultation" can't wrap onto a second line inside its own
   button — flexbox won't shrink a nowrap item below its own text width,
   so rather than let that fight for space against its sibling below a
   certain viewport width, the two buttons switch to stacking (each full
   width) well before that point — see the dedicated breakpoint below. */
.hero__actions .btn {
    flex: 1 1 0;
    max-width: 380px;
    white-space: nowrap;
    justify-content: center;
}

@media (max-width: 900px) {
    .hero__actions {
        flex-direction: column;
        align-items: stretch;
    }

    .hero__actions .btn {
        max-width: none;
        white-space: normal;
    }
}

/* Stat strip — a light band directly below the dark hero (deliberate
   contrast, not a continuation of it), styled after the node/line
   treatment on auxia.io: pale background, small pill badges threaded on a
   connecting line. Colours stay the site's own — navy icon circles, gold
   icon glyphs, a red connecting line — nothing from Auxia's palette. */
.stat-strip {
    background: var(--color-light);
}

/* Back to a straight row (the staircase offsets are gone) with a plain
   straight connector line — not the diagonal/stepped path from before.
   Both the first and last item still sit flush with their own edge of
   .container, which already carries the site's symmetric left/right
   gutter. */
.stat-strip__track {
    position: relative;
}

.stat-strip__list {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    row-gap: var(--space-md);
}

/* Stretches to fill the list's box regardless of its actual pixel size
   (preserveAspectRatio="none"); the path's coordinates are a hand-tuned
   approximation of where a space-between row of 4 pills sits, not a
   pixel-exact measurement — good enough for a decorative connector. JS
   only adds .is-drawing (which is what makes the path start "undrawn")
   right before observing it, so no-JS/reduced-motion visitors simply see
   the finished curve with nothing to animate — see the
   .stat-strip__connector block in main.js. */
.stat-strip__connector {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    z-index: 0;
    pointer-events: none;
}

.stat-strip__connector path {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 2;
    stroke-linecap: round;
    opacity: 0.5;
}

/* Sped up from the original 3s (and a since-reverted 5.5s) to 2.2s — kept
   in sync with DURATION_MS in main.js, which uses this same 2200ms figure
   to work out roughly when the line "reaches" each item (see the comment
   there for how) and to time each item's own two-stage reveal below. */
.stat-strip__connector path.is-drawing {
    stroke-dasharray: var(--path-length, 1000);
    stroke-dashoffset: var(--path-length, 1000);
    transition: stroke-dashoffset 2.2s ease;
}

.stat-strip__track.is-active .stat-strip__connector path.is-drawing {
    stroke-dashoffset: 0;
}

@media (prefers-reduced-motion: reduce) {
    .stat-strip__connector path.is-drawing {
        stroke-dashoffset: 0;
        transition: none;
    }
}

/* Fixed width (not content-sized) so every card lines up regardless of
   how much its own description text runs to — "none is longer than the
   other". Column layout now that each card carries a full sentence, not
   just a value/label/sub trio, so the description gets its own row
   underneath the icon instead of being squeezed beside it. */
.stat-strip__item {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    width: 280px;
    color: var(--color-dark);
    background: var(--clay-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--clay-radius-lg);
    padding: 16px 20px 20px;
    box-shadow: var(--clay-raised-sm), 0 6px 16px rgba(0, 7, 36, 0.08);
    transition: box-shadow var(--transition-base), transform var(--transition-bob-out);
}

.stat-strip__item:hover,
.stat-strip__item:focus-within {
    animation: block-bob 2.2s ease-in-out infinite;
    box-shadow: var(--clay-raised), 0 6px 16px rgba(0, 7, 36, 0.08);
}

.stat-strip__item-head {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.stat-strip__icon {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--color-dark);
    color: var(--color-accent);
}

.stat-strip__value {
    display: block;
    font-family: var(--font-heading);
    font-size: 1.3rem;
    color: var(--color-dark);
    line-height: 1.25;
}

.stat-strip__label {
    display: block;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.stat-strip__sub {
    display: block;
    font-size: 0.72rem;
    color: rgba(0, 7, 36, 0.4);
}

.stat-strip__desc {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--color-text-muted);
}

/* Two-stage reveal, box first then text — not the line finishing, then
   everything else. main.js times both stages off the line's own
   progress: the empty box (this rule, on .stat-strip__item itself) pops
   in right as the line reaches it, and .stat-strip__item-content (the
   icon/value/label/desc inside) fades in a short beat afterward via its
   own class below. Content stays fully visible with zero JS/motion
   (progressive enhancement, same pattern as .reveal-init elsewhere) —
   main.js only adds these -init classes right before it starts
   controlling reveal timing. */
.stat-strip__item-init {
    opacity: 0;
    transform: scale(0.9);
    transition: opacity 180ms ease, transform 180ms ease;
}

.stat-strip__item-init.is-revealed {
    opacity: 1;
    transform: none;
    animation: stat-strip-pop 350ms ease;
}

/* The box's own "just arrived" moment — a brief accent-coloured glow
   that settles back to the normal resting clay shadow. Plays once (not
   infinite) exactly when .is-revealed is added. */
@keyframes stat-strip-pop {
    0% {
        border-color: var(--color-border);
        box-shadow: var(--clay-raised-sm), 0 6px 16px rgba(0, 7, 36, 0.08);
    }
    40% {
        border-color: var(--color-primary);
        box-shadow: 0 0 0 4px rgba(237, 68, 33, 0.16), var(--clay-raised), 0 6px 16px rgba(0, 7, 36, 0.08);
    }
    100% {
        border-color: var(--color-border);
        box-shadow: var(--clay-raised-sm), 0 6px 16px rgba(0, 7, 36, 0.08);
    }
}

.stat-strip__item-content-init {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 160ms ease, transform 160ms ease;
}

.stat-strip__item-content-init.is-revealed {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .stat-strip__item-init,
    .stat-strip__item-content-init {
        opacity: 1;
        transform: none;
        transition: none;
        animation: none;
    }
}

/* Larger than before for more cinematic presence, still visibly smaller
   than the homepage hero (it's a supporting moment, not the main one) and
   nowhere near full-screen. min-height is a floor; content alignment
   (flex-end) is unchanged from the established pattern. */
.inner-hero {
    position: relative;
    background-color: var(--color-dark);
    background-size: cover;
    background-position: center;
    color: var(--color-light);
    min-height: clamp(540px, 80vh, 780px);
    display: flex;
    align-items: center;
    padding-block: clamp(48px, 8vh, 96px);
    isolation: isolate;
}

/* Opt-in full centring (individual service pages) — same image size,
   overlay and padding as the default bottom-anchored hero used elsewhere,
   just centred both axes instead of resting on the bottom padding. */
.inner-hero--centered {
    align-items: center;
}

/* Overlay lightened (was 0.75/0.88) to match the homepage hero change —
   the original photography stays visible; legibility comes from the
   heading/lede text-shadow below instead of a heavier overlay. */
.inner-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0, 7, 36, 0.65) 0%, rgba(0, 7, 36, 0.82) 100%);
    z-index: 0;
}

/* Same composition as the homepage hero — same container width, same
   text-align, and now (below) the exact same type scale, so every page's
   hero reads as one consistent format rather than a smaller "supporting"
   variant. */
.inner-hero__inner {
    position: relative;
    z-index: 1;
    max-width: 960px;
    margin-inline: auto;
    text-align: center;
}

/* Matches .hero h1 exactly — see the note above .inner-hero__inner. */
.inner-hero h1 {
    color: var(--color-light);
    font-size: clamp(2.9rem, 1.2rem + 6.4vw, 5.6rem);
    line-height: 1.08;
    max-width: 18ch;
    margin-inline: auto;
    text-shadow: 0 4px 28px rgba(0, 7, 36, 0.65), 0 1px 3px rgba(0, 7, 36, 0.8);
}

/* Same eyebrow/lede language and sizing as the homepage hero — opt-in, so
   pages that only pass a title are unaffected. */
.inner-hero__eyebrow {
    display: inline-block;
    font-family: var(--font-heading);
    color: var(--color-accent);
    font-size: 1.05rem;
    letter-spacing: 0.18em;
    margin-bottom: 14px;
    text-shadow: 0 2px 12px rgba(0, 7, 36, 0.5);
}

.inner-hero__lede {
    color: rgba(255, 255, 255, 0.92);
    max-width: 56ch;
    margin-inline: auto;
    margin-block: 16px 20px;
    font-size: 1.35rem;
    text-shadow: 0 2px 14px rgba(0, 7, 36, 0.6);
}

/* 9. Components ------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    font-family: var(--font-heading);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    text-decoration: none;
    padding: var(--space-sm) var(--space-lg);
    border-radius: var(--clay-radius-sm);
    border: 2px solid transparent;
    transition: transform var(--transition-base), box-shadow var(--transition-base), background-color var(--transition-base), color var(--transition-base), border-color var(--transition-base);
    min-height: 44px;
}

/* Compact sizing variant — same colour, depth, hover/active/focus behaviour
   as the base .btn, just tighter padding and type for contexts (e.g. the
   bottom CTA) where the default size reads oversized. */
.btn--compact {
    padding: 12px var(--space-md);
    font-size: 0.78rem;
    min-height: 42px;
}

.btn svg {
    transition: transform var(--transition-base);
}

.btn:hover svg {
    transform: translateX(3px);
}

/* Single, neutral, low-opacity shadow — deliberately not the dual-shadow
   clay-highlight technique used on cards. On a small element like a button
   that pairing reads as a glow/halo rather than depth, which is exactly
   what this replaces (was two-layer red + white). One soft shadow keeps a
   tactile raised/hover/press feel without the puffy white outline. */
/* Coloured (not neutral-dark) shadow: buttons sit on both light sections
   and the dark hero photo, and a neutral dark shadow disappears against a
   dark background. A shadow tinted with the button's own colour stays
   visible either way, at low opacity/blur so it still reads as "a small
   shadow", not the glow this replaced. */
.btn--primary {
    background: var(--color-primary);
    color: var(--color-light);
    box-shadow: 0 4px 12px rgba(237, 68, 33, 0.4);
}

.btn--primary:hover {
    background: var(--color-primary-hover);
    color: var(--color-light);
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(237, 68, 33, 0.42);
}

.btn--primary:active {
    transform: translateY(0);
    box-shadow: 0 2px 6px rgba(237, 68, 33, 0.35);
}

.btn--secondary {
    background: var(--color-light);
    color: var(--color-dark);
    border-color: var(--color-dark);
    box-shadow: 0 4px 12px rgba(15, 23, 42, 0.22);
}

.btn--secondary:hover {
    background: var(--color-dark);
    color: var(--color-light);
    border-color: var(--color-dark);
    transform: translateY(-3px);
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.3);
}

.btn--secondary:active {
    transform: translateY(0);
    box-shadow: var(--clay-pressed);
}

/* Ghost/outline variant used over the hero photo — a neutral shadow would
   be invisible against the dark image, so this uses the same low-opacity
   black used elsewhere for on-photo depth cues. */
.btn--contrast {
    background: rgba(255, 255, 255, 0.06);
    color: var(--color-light);
    border-color: rgba(255, 255, 255, 0.55);
    backdrop-filter: blur(2px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}

.btn--contrast:hover {
    background: var(--color-light);
    color: var(--color-dark);
    border-color: var(--color-light);
    transform: translateY(-3px);
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.25);
}

.btn--contrast:active {
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .btn:hover,
    .btn:active,
    .btn:hover svg {
        transform: none;
    }
}

/* Persistent floating Call Us button (partials/call-fab.php) — fixed to the
   viewport, not the header, so it stays reachable the whole time the visitor
   scrolls. Opens the same two-number chooser as a <details>/<summary>
   popover; the menu opens upward since the button sits at the bottom of the
   screen. */
.call-fab {
    position: fixed;
    right: var(--space-lg);
    bottom: var(--space-lg);
    z-index: 70;
}

.call-fab__toggle {
    display: grid;
    place-items: center;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: var(--color-primary);
    color: var(--color-light);
    list-style: none;
    cursor: pointer;
    box-shadow: 0 6px 16px rgba(237, 68, 33, 0.4), 0 0 16px rgba(237, 68, 33, 0.38);
    transition: background-color var(--transition-base), box-shadow var(--transition-base);
    animation: fab-bob 2.4s ease-in-out infinite;
}

/* Continuous, not hover-triggered — the button bobs in place the whole
   time so it stays noticeable in the corner even before a visitor's mouse
   ever reaches it. A gentler amplitude than the shared .block-bob keyframe
   used on full-size cards elsewhere, since this is a small 60px circle
   that's permanently on screen rather than something animating only while
   actively hovered. */
@keyframes fab-bob {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-6px); }
}

.call-fab__toggle::-webkit-details-marker {
    display: none;
}

.call-fab__toggle:hover,
.call-fab__toggle:focus-visible {
    background: var(--color-primary-hover);
    box-shadow: 0 10px 24px rgba(237, 68, 33, 0.46), 0 0 22px rgba(237, 68, 33, 0.5);
}

.phone-chooser[open] .call-fab__toggle {
    background: var(--color-dark);
}

@media (prefers-reduced-motion: reduce) {
    .call-fab__toggle {
        animation: none;
    }
}

.phone-chooser {
    position: relative;
}

.phone-chooser__menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + 12px);
    z-index: 70;
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 230px;
    background: var(--color-light);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised);
    padding: 8px;
}

.phone-chooser__menu-label {
    display: block;
    padding: var(--space-xs) var(--space-sm) 2px;
    font-family: var(--font-heading);
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.phone-chooser__menu a {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-xs) var(--space-sm);
    border-radius: var(--clay-radius-sm);
    color: var(--color-dark);
    text-decoration: none;
    font-size: 0.95rem;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.phone-chooser__menu a:hover,
.phone-chooser__menu a:focus-visible {
    background: var(--clay-surface-sunken);
    color: var(--color-primary);
}

.cta {
    background: var(--color-dark);
    color: var(--color-light);
    text-align: center;
}

.cta__inner {
    max-width: 840px;
    margin-inline: auto;
}

/* The base h1–h4 rule sets a dark navy heading colour by default — that
   rule targets .cta's h2 directly, so it overrides the dark-background
   .cta's inherited light text colour (an explicit rule always beats
   inheritance, regardless of specificity). Every .cta usage needs this
   explicit override, not just .cta--bottom, which already had its own —
   that's exactly why the service-page CTAs (using the plain .cta class
   via partials/cta.php) were showing a dark-on-dark heading. */
.cta h2 {
    margin-bottom: var(--space-md);
    color: var(--color-light);
}

/* Deliberate rhythm: room below the paragraph before the button, and more
   again below the button before the trust row, so the CTA reads as three
   distinct beats rather than everything sitting flush together. */
.cta p {
    margin-inline: auto;
    margin-block: 0 var(--space-lg);
    color: var(--color-light);
}

/* Compact variant — for a CTA with just a heading, paragraph and button
   (no trust-features row underneath), the full responsive section-spacing
   this class would otherwise inherit from main > section reads as far too
   much empty space around not much content. Scoped to its own class so it
   never touches .cta--bottom's already-tuned proportions on the homepage. */
.cta--compact {
    padding-block: clamp(40px, 7vw, 72px);
}

.cta--compact .cta__inner p {
    margin-bottom: var(--space-md);
}

.contact-block {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-lg);
}

/* Selector is tag-qualified (div.contact-block__item) for the same reason
   as a.service-card / li.journey__step: keeps this transition list from
   being replaced wholesale by the later, equal-specificity .reveal-init
   rule when this item is also a scroll-reveal target. A coloured top edge
   (navy by default, red on the phone card via the --phone modifier) gives
   each card a small branded identity instead of three identical grey
   boxes. */
div.contact-block__item {
    position: relative;
    overflow: hidden;
    background: var(--clay-surface);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised-sm);
    padding: var(--space-lg) var(--space-md);
    text-align: center;
    transition: transform var(--transition-bob-out), box-shadow var(--transition-base);
}

.contact-block__item::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: var(--color-dark);
}

.contact-block__item--phone::before {
    background: var(--color-primary);
}

.contact-block__item:hover,
.contact-block__item:focus-within {
    animation: block-bob 2.2s ease-in-out infinite;
    box-shadow: var(--clay-raised);
}

.contact-block__icon {
    position: relative;
    display: grid;
    place-items: center;
    width: 56px;
    height: 56px;
    margin-inline: auto;
    margin-bottom: var(--space-sm);
    border-radius: 50%;
    color: var(--color-light);
    box-shadow: var(--clay-raised-sm);
}

.contact-block__icon--navy {
    background: var(--color-dark);
}

.contact-block__icon--red {
    background: var(--color-primary);
}

/* Idle "live line" pulse — purely decorative (the card is fully
   understandable with it removed), so it's fine to switch off entirely
   for reduced-motion rather than needing a static fallback state. */
.contact-block__item--phone .contact-block__icon::after {
    content: "";
    position: absolute;
    inset: -6px;
    border-radius: 50%;
    border: 2px solid var(--color-primary);
    opacity: 0;
    animation: contact-pulse 2.4s ease-out infinite;
}

@keyframes contact-pulse {
    0% { transform: scale(0.85); opacity: 0.55; }
    70% { transform: scale(1.4); opacity: 0; }
    100% { transform: scale(1.4); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .contact-block__item--phone .contact-block__icon::after {
        display: none;
    }
}

.contact-block__item p {
    margin-inline: auto;
    color: var(--color-text-muted);
}

.contact-block__link {
    font-weight: 600;
}

/* Copy-to-clipboard row — see partials/contact-block.php's header comment
   and main.js for why this is a real button next to a working tel:/mailto:
   link rather than a replacement for it. */
.contact-block__copy-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

.contact-block__copy {
    position: relative;
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--clay-surface-sunken);
    color: var(--color-text-muted);
    transition: background-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}

.contact-block__copy:hover,
.contact-block__copy:focus-visible {
    background: var(--color-primary);
    color: var(--color-light);
    transform: translateY(-1px) scale(1.06);
}

.contact-block__copy-icon--check {
    display: none;
}

.contact-block__copy.is-copied {
    background: var(--color-accent);
    color: var(--color-dark);
}

.contact-block__copy.is-copied .contact-block__copy-icon:not(.contact-block__copy-icon--check) {
    display: none;
}

.contact-block__copy.is-copied .contact-block__copy-icon--check {
    display: block;
}

.contact-block__copy-tip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: var(--color-dark);
    color: var(--color-light);
    font-family: var(--font-heading);
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: var(--clay-radius-sm);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast), transform var(--transition-fast);
}

.contact-block__copy.is-copied .contact-block__copy-tip {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .contact-block__copy,
    .contact-block__copy-tip {
        transition: none;
    }
}

.error-page {
    text-align: center;
}

/* Shared section heading pattern: small red eyebrow + heading. */
.section-heading {
    text-align: center;
    max-width: 640px;
    margin-inline: auto;
    margin-bottom: var(--space-xl);
}

.section-heading__eyebrow {
    display: block;
    color: var(--color-primary);
    font-family: var(--font-heading);
    font-size: 0.8rem;
    letter-spacing: 0.12em;
    margin-bottom: var(--space-xs);
}

.section-heading__eyebrow::before,
.section-heading__eyebrow::after {
    content: "— ";
}

.section-heading__eyebrow::after {
    content: " —";
}

/* Same dark navy as the hero/footer/stat icons — scoped to this section
   only, so the cards (still the light clay surface) read as floating
   panels on a dark band rather than the rest of the page changing. The
   heading needs its own override here since the global h1-h4 rule would
   otherwise paint it the same dark navy as the background. */
.services-section {
    background: var(--color-dark);
}

.services-section .section-heading h2 {
    color: var(--color-light);
}

/* The cards' normal clay shadow (--clay-raised / --clay-raised-hover) is a
   dual dark+light shadow tuned to sit on a light page background — its
   light side barely shows there, but against this section's dark navy it
   reads as a bright glow/halo around every card. Swapping in a plain,
   single dark shadow here removes that glow while keeping real depth.
   Specificity note: needs the extra .services-section qualifier (making
   this 0,2,1 / 0,3,1) to actually beat the base a.service-card /
   a.service-card:hover rules (0,1,1 / 0,2,1) below — a same-specificity
   .service-card-only override would lose regardless of source order. */
/* A soft ambient glow (not the directional --clay-raised dual-shadow,
   which reads as a bright, lopsided halo on a dark background) — always
   on, not just on hover. Needs its own :hover rule too: without one, the
   generic a.service-card:hover rule further down (same specificity,
   later in source) would win during hover and silently bring back the
   stronger --clay-raised-hover glow — which is exactly why hovering a
   card here used to look like a sudden bright flare. */
.services-section a.service-card {
    box-shadow: 0 0 26px rgba(182, 188, 201, 0.28), 0 6px 16px rgba(0, 0, 0, 0.28);
}

.services-section a.service-card:hover,
.services-section a.service-card:focus-visible {
    box-shadow: 0 0 32px rgba(182, 188, 201, 0.34), 0 6px 16px rgba(0, 0, 0, 0.28);
}

/* Services grid — the strongest claymorphism moment on the page. There are
   seven cards (four then three): an 8-column grid lets every card span 2
   columns, matching row-one's width, while the second row's three cards
   get explicit column starts so they sit centred under row one instead of
   packing flush left — same card size, just placement. */
.services-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: var(--space-lg);
}

a.service-card {
    grid-column: span 2;
}

.services-grid a.service-card:nth-child(5) {
    grid-column: 2 / span 2;
}

.services-grid a.service-card:nth-child(6) {
    grid-column: 4 / span 2;
}

.services-grid a.service-card:nth-child(7) {
    grid-column: 6 / span 2;
}

/* Selector is tag-qualified (a.service-card) so its transition list keeps
   priority over the later, equal-specificity .reveal-init rule — otherwise
   the reveal fade's shorthand would silently replace this hover timing. */
a.service-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
    background: var(--clay-surface);
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised);
    padding: var(--space-xl) var(--space-md) var(--space-lg);
    text-align: center;
    text-decoration: none;
    color: inherit;
    transition: transform var(--transition-bob-out), box-shadow var(--transition-base), border-color var(--transition-base);
}

/* Shared continuous hover "bob" for every card-style block on the site —
   while hovered/focused, the block keeps gently floating up and down for
   as long as the pointer stays over it (or it stays focused), rather than
   lifting once and holding. It reverts to its resting position the
   instant the pointer leaves. Used on: service cards, contact cards,
   why-choose items, journey steps, service chips, stat cards, FAQ items
   and service-page rows — see each component's own :hover rule below. */
/* Starts and ends at translateY(0) — matching each block's own resting
   transform — rather than jumping straight to an offset the moment the
   animation applies. That jump (and the equivalent snap back on mouse-out)
   was the actual source of the "choppy" feel; easing smoothly in and out
   of a 0-based cycle, plus a transform transition on each block's resting
   state below (so a mid-cycle mouse-out still eases back rather than
   cutting off), removes both. */
@keyframes block-bob {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-9px); }
}

@media (prefers-reduced-motion: reduce) {
    a.service-card:hover,
    a.service-card:focus-visible,
    .contact-block__item:hover,
    .contact-block__item:focus-within,
    .why-choose__list li:hover,
    .why-choose__list li:focus-within,
    .journey__step:hover,
    .journey__step:focus-within,
    .service-chip:hover,
    .service-chip:focus-visible,
    .stat-strip__item:hover,
    .stat-strip__item:focus-within,
    .faq-item:hover,
    .faq-item:focus-within,
    a.service-row:hover,
    a.service-row:focus-visible {
        animation: none;
        transform: none;
    }
}

/* Tag-qualified (a.service-card:hover, not .service-card:hover) for the
   same reason as the base rule above: .reveal-init.is-visible (further
   down the file) sets transform:none at equal specificity to a plain
   .service-card:hover, and since it comes later in source order it would
   silently win once a card had finished its scroll-reveal — the lift on
   hover would stop working right after the card first became visible.
   Tag-qualifying raises this rule's specificity above that one. */
a.service-card:hover,
a.service-card:focus-visible {
    animation: block-bob 2.2s ease-in-out infinite;
    box-shadow: var(--clay-raised-hover);
    border-color: rgba(237, 68, 33, 0.25);
}

a.service-card:active {
    box-shadow: var(--clay-pressed);
    transform: translateY(-1px);
}

.service-card__icon {
    display: grid;
    place-items: center;
    width: 72px;
    height: 72px;
    margin-bottom: var(--space-md);
    border-radius: 50%;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised-sm);
    color: var(--color-dark);
    transition: transform var(--transition-base), color var(--transition-base), box-shadow var(--transition-base);
}

.service-card:hover .service-card__icon,
.service-card:focus-visible .service-card__icon {
    transform: translateY(-3px) rotate(-6deg);
    color: var(--color-primary);
    box-shadow: var(--clay-raised);
}

.service-card h3 {
    font-size: 0.95rem;
    margin-bottom: var(--space-xs);
    position: relative;
    display: inline-block;
    padding-bottom: var(--space-sm);
}

.service-card h3::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    width: 28px;
    height: 3px;
    background: var(--color-primary);
    border-radius: 2px;
    transition: width var(--transition-base);
}

.service-card:hover h3::after,
.service-card:focus-visible h3::after {
    width: 44px;
}

.service-card p {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-inline: auto;
}

/* "More Info" link — optional; only services.php's fuller cards use it.
   margin-top: auto pins it to the bottom of every card regardless of
   description length, so it lines up across a row even when cards
   stretch to equal height. */
.service-card__more {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: auto;
    padding-top: var(--space-sm);
    font-family: var(--font-heading);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-primary);
}

.service-card__more svg {
    transition: transform var(--transition-base);
}

.service-card:hover .service-card__more svg,
.service-card:focus-visible .service-card__more svg {
    transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
    .service-card:hover .service-card__more svg {
        transform: none;
    }
}

/* About section — image one side, content the other, badge overlay. */
.about-section {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    align-items: center;
}

.about-section__media {
    position: relative;
    border-radius: var(--clay-radius-lg);
    padding: 10px;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised);
}

.about-section__media img {
    border-radius: calc(var(--clay-radius-lg) - 8px);
    display: block;
    width: 100%;
    height: auto;
    transition: transform var(--transition-slow);
}

.about-section__media:hover img,
.about-section__media:focus-within img {
    transform: scale(1.02);
}

.about-section__badge {
    position: absolute;
    left: var(--space-lg);
    bottom: calc(var(--space-md) * -1);
    background: var(--color-dark);
    color: var(--color-light);
    border-radius: var(--clay-radius-sm);
    box-shadow: var(--clay-raised-navy);
    padding: var(--space-sm) var(--space-md);
    text-align: center;
    transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.about-section__media:hover .about-section__badge,
.about-section__media:focus-within .about-section__badge {
    transform: translateY(-3px);
    box-shadow: var(--clay-raised-navy-hover);
}

.about-section__badge strong {
    display: block;
    font-family: var(--font-heading);
    color: var(--color-accent);
    font-size: 1.2rem;
}

.about-section__badge span {
    font-size: 0.7rem;
    color: rgba(255, 255, 255, 0.8);
}

.about-section__eyebrow {
    display: block;
    color: var(--color-primary);
    font-family: var(--font-heading);
    font-size: 0.8rem;
    letter-spacing: 0.1em;
    margin-bottom: var(--space-xs);
}

.about-section__bullets {
    margin-block: var(--space-md);
    display: grid;
    gap: var(--space-sm);
}

.about-section__bullets li {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.about-section__bullet-icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised-sm);
    color: var(--color-primary);
}

/* Services page listing — deliberately not the homepage's .service-card
   icon grid again (see the comment above .service-rows in services.php):
   alternating image+text rows reusing .about-section__media's existing
   clay-frame photo treatment, one row per service, each row a single
   clickable link rather than a separate "More Info" link nested inside
   it (nesting an <a> inside an <a> isn't valid HTML). */
.service-rows {
    display: flex;
    flex-direction: column;
    gap: var(--space-xl);
}

a.service-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    align-items: center;
    color: inherit;
    text-decoration: none;
    transition: transform var(--transition-bob-out);
}

a.service-row:hover,
a.service-row:focus-visible {
    animation: block-bob 2.2s ease-in-out infinite;
}

.service-row--reverse .service-row__media {
    order: 2;
}

/* Intercoms' own photo is an unusually wide banner crop (real Centurion
   product photography, not cropped further here — see the services.php
   comment on this entry) — at the standard even 1fr/1fr split it renders
   short and small next to the other rows' taller images. Giving its media
   column extra width makes the image genuinely bigger (it scales up at
   its own aspect ratio) rather than stretching or cropping it.
   Compound selector (a.service-row.service-row--wide-media, not just
   .service-row--wide-media) so it actually beats the base a.service-row
   rule above — that rule is tag+class (0,1,1), a plain single-class
   modifier is only (0,1,0), so it was silently losing regardless of
   source order and never actually applying. */
a.service-row.service-row--wide-media {
    grid-template-columns: 1.6fr 1fr;
}

.service-row:hover .about-section__media img,
.service-row:focus-visible .about-section__media img {
    transform: scale(1.02);
}

.service-row__icon {
    display: grid;
    place-items: center;
    width: 60px;
    height: 60px;
    margin-bottom: var(--space-sm);
    border-radius: 50%;
    background: var(--color-dark);
    color: var(--color-accent);
    box-shadow: var(--clay-raised-navy);
    transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.service-row:hover .service-row__icon,
.service-row:focus-visible .service-row__icon {
    transform: translateY(-3px);
    box-shadow: var(--clay-raised-navy-hover);
}

.service-row__content h3 {
    margin-bottom: var(--space-sm);
}

.service-row__content p {
    color: var(--color-text-muted);
    margin-bottom: var(--space-md);
}

.service-row__more {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-heading);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-primary);
}

.service-row__more svg {
    transition: transform var(--transition-base);
}

.service-row:hover .service-row__more svg,
.service-row:focus-visible .service-row__more svg {
    transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
    .service-row:hover .service-row__icon,
    .service-row:hover .service-row__more svg {
        transform: none;
    }
}

/* Why choose us — four-item icon row. */
.why-choose {
    background: var(--clay-surface-sunken);
}

.why-choose__list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-md);
    text-align: center;
}

.why-choose__list li {
    padding: var(--space-lg) var(--space-md);
    border-radius: var(--clay-radius-md);
    transition: transform var(--transition-bob-out), box-shadow var(--transition-base), background-color var(--transition-base);
}

.why-choose__list li:hover,
.why-choose__list li:focus-within {
    animation: block-bob 2.2s ease-in-out infinite;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised);
}

.why-choose__icon {
    display: grid;
    place-items: center;
    width: 68px;
    height: 68px;
    margin-inline: auto;
    margin-bottom: var(--space-md);
    border-radius: 50%;
    color: var(--color-light);
    box-shadow: var(--clay-raised-sm);
    transition: transform var(--transition-base), box-shadow var(--transition-base);
}

.why-choose__list li:hover .why-choose__icon,
.why-choose__list li:focus-within .why-choose__icon {
    transform: translateY(-2px) scale(1.06);
    box-shadow: var(--clay-raised);
}

.why-choose__icon--navy {
    background: var(--color-dark);
}

.why-choose__icon--red {
    background: var(--color-primary);
}

.why-choose__list h3 {
    font-size: 0.9rem;
    position: relative;
    padding-bottom: var(--space-sm);
    display: inline-block;
}

.why-choose__list h3::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    width: 24px;
    height: 2px;
    background: var(--color-accent);
    transition: width var(--transition-base);
}

.why-choose__list li:hover h3::after,
.why-choose__list li:focus-within h3::after {
    width: 36px;
}

.why-choose__list p {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-inline: auto;
}

/* SAIDSA trust row — integrated into the bottom of Why Choose rather than
   a standalone section, so it reads as this section's closing trust line.
   Same background family (no separate white/clay panel), just a divider. */
.why-choose__trust {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-lg);
    flex-wrap: wrap;
    text-align: center;
    margin-top: var(--space-xl);
    padding-top: var(--space-xl);
    border-top: 1px solid rgba(0, 7, 36, 0.1);
}

.why-choose__trust-logo {
    max-height: 72px;
    width: auto;
    border-radius: var(--clay-radius-sm);
    box-shadow: var(--clay-raised-sm);
    padding: var(--space-xs);
    background: var(--color-light);
}

.why-choose__trust-text {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    font-family: var(--font-heading);
    font-size: 0.95rem;
    color: var(--color-dark);
    max-width: 46ch;
    text-align: left;
}

.why-choose__trust-text svg {
    color: var(--color-primary);
    flex-shrink: 0;
}

/* Long-form legal prose (Privacy Policy, Terms of Service) — a single
   comfortable reading column rather than the wide 1500px grid the rest of
   the site uses, since dense paragraph text stays far more readable at a
   capped measure. Reuses the site's own heading/paragraph/link styling,
   just constrained and with its own vertical rhythm between sections. */
.policy-content {
    max-width: 760px;
    margin-inline: auto;
}

.policy-content h3 {
    margin-top: var(--space-xl);
}

.policy-content h3:first-child {
    margin-top: 0;
}

.policy-content p {
    max-width: none;
    color: var(--color-text-muted);
}

.policy-content ul {
    display: grid;
    gap: var(--space-xs);
    margin-block: var(--space-sm) var(--space-md);
    padding-left: var(--space-md);
    color: var(--color-text-muted);
    list-style: disc;
}

.policy-content .policy-meta {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    margin-bottom: var(--space-xl);
}

.saidsa-more {
    max-width: 1100px;
    margin: var(--space-lg) auto 0;
}

.saidsa-more .about-section__bullets {
    margin-bottom: 0;
}

/* Full-width variant — for a context (About page) with nothing else in the
   section fighting for width, the default centred/capped trust row reads
   as a small floating group lost in a wide container. This turns it into
   a deliberate horizontal band: logo, then copy that actually fills the
   available width, then a verify-membership action, still inside the same
   section rather than split into a standalone one. */
.why-choose__trust--wide {
    justify-content: flex-start;
    text-align: left;
    padding: var(--space-lg) var(--space-xl);
    background: var(--clay-surface);
    border-radius: var(--clay-radius-lg);
    box-shadow: var(--clay-raised);
    border-top: none;
}

.why-choose__trust--wide .why-choose__trust-text {
    flex: 1 1 320px;
    max-width: none;
    text-align: left;
}

.why-choose__trust--wide .why-choose__trust-actions {
    flex-shrink: 0;
}

/* Journey — Auxia-motion-inspired but Action-Force-specific: a left-to-right
   customer journey (Call Us -> Free Consultation -> We Show Up -> Since
   1991). Evolves the former static "Our Process" section rather than
   duplicating it (same slot in the homepage flow, same section-heading
   pattern) — see app/Views/pages/home.php $journey. The connecting track is
   two layers: a static feint dashed .journey__track (always visible, so the
   finished composition reads correctly with zero JS) and a solid
   .journey__progress bar drawn on top of it. That progress bar's default,
   no-JS state is already fully grown (scaleX(1)) — main.js only adds the
   .journey-init class right before observing it, which is what makes it
   start collapsed so there's something to animate from. JS-disabled and
   prefers-reduced-motion visitors both simply see the completed journey. */
.journey__track-wrap {
    position: relative;
}

.journey__track,
.journey__progress {
    position: absolute;
    top: 36px;
    left: 12.5%;
    right: 12.5%;
    height: 3px;
    border-radius: 2px;
}

.journey__track {
    background: repeating-linear-gradient(90deg, var(--color-border) 0 8px, transparent 8px 16px);
}

.journey__progress {
    background: var(--color-primary);
    transform-origin: left center;
    transform: scaleX(1);
}

.journey__progress.journey-init {
    transform: scaleX(0);
    transition: transform 1200ms cubic-bezier(0.22, 1, 0.36, 1);
}

.journey__track-wrap.is-active .journey__progress.journey-init {
    transform: scaleX(1);
}

.journey__list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-lg);
}

/* Tag-qualified (li.journey__step) for the same reason noted on
   a.service-card above — keeps this transition list from being replaced
   wholesale by the later, equal-specificity .reveal-init rule. */
li.journey__step {
    text-align: center;
    position: relative;
    padding: var(--space-sm);
    border-radius: var(--clay-radius-md);
    transition: transform var(--transition-bob-out), background-color var(--transition-base);
}

.journey__step:hover,
.journey__step:focus-within {
    animation: block-bob 2.2s ease-in-out infinite;
    background: var(--clay-surface-sunken);
}

.journey__icon {
    position: relative;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 72px;
    height: 72px;
    margin-inline: auto;
    margin-bottom: var(--space-sm);
    border-radius: 50%;
    background-color: var(--color-dark);
    color: var(--color-light);
    box-shadow: var(--clay-raised-navy);
    transition: transform var(--transition-base), box-shadow var(--transition-base), background-color var(--transition-slow), color var(--transition-slow);
}

.journey__step:hover .journey__icon,
.journey__step:focus-within .journey__icon {
    transform: translateY(-3px);
    box-shadow: var(--clay-raised-navy-hover);
}

/* Before the journey activates, icons sit muted so the line reaching each
   one reads as the moment it "lights up" — staggered per stage so the
   activation visibly travels left to right with the progress bar above.
   Only applied once JS adds .journey-init (see main.js); no-JS/no-motion
   visitors keep the plain .journey__icon navy styling as their only state. */
.journey__step.journey-init .journey__icon {
    background-color: var(--clay-surface-sunken);
    color: var(--color-text-muted);
    box-shadow: var(--clay-raised-sm);
}

.journey__track-wrap.is-active .journey__step:nth-child(1).journey-init .journey__icon { transition-delay: 150ms; }
.journey__track-wrap.is-active .journey__step:nth-child(2).journey-init .journey__icon { transition-delay: 420ms; }
.journey__track-wrap.is-active .journey__step:nth-child(3).journey-init .journey__icon { transition-delay: 700ms; }
.journey__track-wrap.is-active .journey__step:nth-child(4).journey-init .journey__icon { transition-delay: 980ms; }

.journey__track-wrap.is-active .journey__step.journey-init .journey__icon {
    background-color: var(--color-dark);
    color: var(--color-light);
    box-shadow: var(--clay-raised-navy);
}

.journey__number {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--color-primary);
    color: var(--color-light);
    font-size: 0.7rem;
    font-family: var(--font-heading);
    box-shadow: var(--clay-raised-sm);
    transition: transform var(--transition-base);
}

.journey__step:hover .journey__number,
.journey__step:focus-within .journey__number {
    transform: scale(1.12);
}

.journey__step h3 {
    font-size: 0.9rem;
}

.journey__step p {
    font-size: 0.82rem;
    color: var(--color-text-muted);
    margin-inline: auto;
}

@media (prefers-reduced-motion: reduce) {
    .journey__progress.journey-init {
        transform: scaleX(1);
        transition: none;
    }

    .journey__step.journey-init .journey__icon {
        background-color: var(--color-dark);
        color: var(--color-light);
        box-shadow: var(--clay-raised-navy);
        transition: none;
        transition-delay: 0ms;
    }
}

/* Full-width live map, directly above the footer (see partials/map.php and
   layouts/base.php) — just the map, edge-to-edge, no card/overlay content.
   .site-map__frame is the <iframe> itself (see the inline style on that
   element too — belt-and-braces so the map is unambiguously full-width
   regardless of stylesheet caching). */
.site-map {
    background: var(--color-dark);
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    height: 460px;
}

.site-map__frame {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

/* Bottom CTA specifics beyond the base .cta rules. Extra bottom padding
   (beyond the shared main > section spacing, which still governs the top)
   gives the trust row room to breathe before the footer — otherwise two
   same-toned navy blocks with nothing but a hairline between them read as
   one merged mass rather than a deliberate transition. */
.cta--bottom {
    background-color: var(--color-dark);
    background-size: cover;
    background-position: center;
    position: relative;
    padding-bottom: calc(var(--section-spacing) * 1.5);
}

.cta--bottom::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0, 7, 36, 0.86) 0%, rgba(0, 7, 36, 0.9) 100%);
}

.cta--bottom .container {
    position: relative;
}

.cta--bottom h2 {
    color: var(--color-light);
}

.cta__features {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--space-lg);
    margin-top: var(--space-xl);
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.85);
}

.cta__features li {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.cta__features svg {
    color: var(--color-accent);
    flex-shrink: 0;
}

/* Footer social icons (only channels that actually exist — phone/email). */
.site-footer__social {
    display: flex;
    gap: var(--space-sm);
    margin-top: var(--space-sm);
}

.site-footer__social a {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    text-decoration: none;
    transition: transform var(--transition-base), background-color var(--transition-base);
}

.site-footer__social a:hover,
.site-footer__social a:focus-visible {
    background: var(--color-primary);
    transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
    .site-footer__social a:hover {
        transform: none;
    }
}

/* Stat strip reused standalone (About page) rather than attached to a
   hero — restores normal section-level breathing room around the compact
   list padding that the homepage's hero-attached version relies on.
   Compound selector so it reliably wins over the plain .stat-strip rule
   regardless of source order. */
.stat-strip.stat-strip--standalone {
    padding-block: var(--section-spacing);
}

/* Services preview chips — a lighter-weight reuse of the service-card icon
   language for contexts (About page) where the source content is a plain
   list of service names, not full descriptions; the homepage's full cards
   already own the descriptive version. */
.service-chip-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-sm);
}

/* Sized deliberately compact so a typical 6-item list clears one row at
   desktop widths without relying on the container happening to be wide
   enough — flex-wrap stays on as the tablet/mobile fallback. */
@media (min-width: 1100px) {
    .service-chip-list {
        flex-wrap: nowrap;
    }
}

.service-chip {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    background: var(--clay-surface);
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised-sm);
    padding: 10px var(--space-sm);
    text-decoration: none;
    color: var(--color-dark);
    white-space: nowrap;
    transition: transform var(--transition-bob-out), box-shadow var(--transition-base), border-color var(--transition-base);
}

.service-chip:hover,
.service-chip:focus-visible {
    animation: block-bob 2.2s ease-in-out infinite;
    box-shadow: var(--clay-raised);
    border-color: rgba(237, 68, 33, 0.25);
}

.service-chip:active {
    transform: translateY(0);
    box-shadow: var(--clay-pressed);
}

.service-chip__icon {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised-sm);
    color: var(--color-primary);
    transition: transform var(--transition-base);
}

.service-chip:hover .service-chip__icon,
.service-chip:focus-visible .service-chip__icon {
    transform: rotate(-6deg);
}

.service-chip span {
    font-family: var(--font-heading);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

@media (prefers-reduced-motion: reduce) {
    .service-chip:hover,
    .service-chip:hover .service-chip__icon {
        transform: none;
    }
}

/* FAQ accordion — native <details>/<summary>, so expand/collapse, keyboard
   operation (Tab + Enter/Space) and screen-reader semantics all come free
   from the browser with no ARIA or JS required. Two columns at desktop so
   the list actually uses the full 1500px container instead of sitting as
   a narrow stack in the middle of a wide section. grid-auto-flow: column
   fills the left column top-to-bottom first (questions 1–3), then the
   right column (4–6) — reading order down each column, matching a
   balanced 3-and-3 layout — rather than the default row-major
   interleaving. A trailing odd item spans both columns rather than
   leaving a lopsided gap if the list ever isn't a clean multiple of the
   row count. */
/* Two independent flex columns rather than a CSS grid with explicit row
   tracks — a grid's rows share height across both columns, so opening
   one item used to visibly stretch its row-mate in the other column even
   though that item stayed closed. align-items:flex-start keeps each
   column's own height independent of the other's. */
.faq-list {
    display: flex;
    align-items: flex-start;
    gap: var(--space-md);
}

.faq-list__col {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    gap: var(--space-md);
    min-width: 0;
}

.faq-item {
    background: var(--clay-surface);
    border-radius: var(--clay-radius-md);
    box-shadow: var(--clay-raised-sm);
    overflow: hidden;
    transition: box-shadow var(--transition-base), transform var(--transition-bob-out);
}

.faq-item[open] {
    box-shadow: var(--clay-raised);
}

.faq-item:hover,
.faq-item:focus-within {
    animation: block-bob 2.2s ease-in-out infinite;
}

.faq-item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-md) var(--space-lg);
    cursor: pointer;
    font-family: var(--font-heading);
    font-size: 0.92rem;
    color: var(--color-dark);
    list-style: none;
}

.faq-item summary::-webkit-details-marker {
    display: none;
}

.faq-item__icon {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--clay-surface);
    box-shadow: var(--clay-raised-sm);
    color: var(--color-primary);
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1;
    transition: transform var(--transition-base);
}

.faq-item[open] .faq-item__icon {
    transform: rotate(45deg);
}

.faq-item__body {
    padding: 0 var(--space-lg) var(--space-lg);
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

@media (prefers-reduced-motion: reduce) {
    .faq-item[open] .faq-item__icon {
        transform: rotate(45deg);
        transition: none;
    }
}

/* Image + form, side by side, filling the full 1500px container rather
   than leaving the form as a narrow floating box in a wide section. Image
   column reuses .about-section__media (same clay frame/hover-zoom as the
   About page) instead of a one-off image treatment. */
.contact-layout {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    align-items: stretch;
}

.contact-layout .about-section__media {
    height: 100%;
}

.contact-layout .about-section__media img {
    height: 100%;
    object-fit: cover;
}

/* 10. Forms ------------------------------------------------------------------ */
/* Raised clay panel wrapping a form (Contact page) — a sunken input tray
   sits inside a raised outer surface, giving the recessed-detail depth the
   claymorphism system calls for elsewhere without inventing new tokens.
   Width comes from its .contact-layout grid column, not a fixed cap, so
   it uses the space the 1500px container actually gives it. */
.form-panel {
    background: var(--clay-surface);
    border-radius: var(--clay-radius-lg);
    box-shadow: var(--clay-raised);
    padding: var(--space-xl);
}

.form-panel .section-heading {
    text-align: left;
    margin-inline: 0;
    max-width: none;
}

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 var(--space-md);
}

.form-field {
    margin-bottom: var(--space-md);
}

.form-field label {
    display: block;
    font-family: var(--font-heading);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-dark);
    margin-bottom: var(--space-xs);
}

.form-field input,
.form-field textarea,
.form-field select {
    width: 100%;
    padding: var(--space-sm) var(--space-md);
    background: var(--clay-surface-sunken);
    border: 1px solid rgba(0, 7, 36, 0.08);
    border-radius: var(--clay-radius-sm);
    box-shadow: var(--clay-pressed);
    font: inherit;
    color: var(--color-text);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.form-field textarea {
    resize: vertical;
    min-height: 120px;
}

.form-field input:focus-visible,
.form-field textarea:focus-visible,
.form-field select:focus-visible {
    border-color: var(--color-primary);
    box-shadow: var(--clay-pressed), 0 0 0 3px rgba(237, 68, 33, 0.25);
    outline: none;
}

.form-field--honeypot {
    position: absolute;
    left: -9999px;
}

.form-status {
    padding: var(--space-sm) var(--space-md);
    margin-bottom: var(--space-md);
    border-radius: var(--clay-radius-sm);
}

.form-status--success {
    background: #E9F7EF;
    border: 1px solid #2E7D32;
}

.form-status--error {
    background: #FDEDEA;
    border: 1px solid var(--color-primary);
}

@media (max-width: 767px) {
    .form-panel {
        padding: var(--space-lg) var(--space-md);
    }

    .form-grid {
        grid-template-columns: 1fr;
    }
}

/* 11. Footer ------------------------------------------------------------------- */
.site-footer {
    background: var(--color-dark);
    color: var(--color-light);
    /* Hairline seam so the footer reads as a deliberate new block rather
       than a continuation of whatever same-toned dark section precedes it
       (the bottom CTA, notably) — both are var(--color-dark), with nothing
       else to mark the boundary. */
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.site-footer a {
    color: var(--color-light);
}

.site-footer__inner {
    display: grid;
    grid-template-columns: 1.15fr 0.9fr 1fr 0.85fr 1.1fr;
    gap: var(--space-lg);
    padding-block: var(--space-xl);
}

/* Hairline rules between the four columns instead of relying on gap alone
   to separate them — see the ul/link rewrite below for why the previous
   version read as "text everywhere": bare <ul>s with no explicit list
   styling, links carrying the site's default underline on every single
   item, and no vertical rhythm between rows. */
.site-footer__col + .site-footer__col {
    border-left: 1px solid rgba(255, 255, 255, 0.1);
    padding-left: var(--space-lg);
}

.site-footer__col > p {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.92rem;
    max-width: 30ch;
}

.site-footer__col h2 {
    color: var(--color-light);
    font-size: 0.82rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: var(--space-md);
    padding-bottom: var(--space-sm);
    position: relative;
}

.site-footer__col h2::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 28px;
    height: 2px;
    background: var(--color-primary);
}

.site-footer__links {
    display: flex;
    flex-direction: column;
    gap: 11px;
}

.site-footer__links a {
    color: rgba(255, 255, 255, 0.72);
    text-decoration: none;
    font-size: 0.92rem;
    display: inline-block;
    transition: color var(--transition-fast), transform var(--transition-fast);
}

.site-footer__links a:hover,
.site-footer__links a:focus-visible {
    color: var(--color-accent);
    transform: translateX(3px);
}

.site-footer__contact {
    display: flex;
    flex-direction: column;
    gap: 13px;
}

.site-footer__contact li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 0.92rem;
    color: rgba(255, 255, 255, 0.72);
    line-height: 1.4;
}

.site-footer__contact-icon {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    margin-top: 1px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    color: var(--color-accent);
}

.site-footer__contact a {
    color: rgba(255, 255, 255, 0.88);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.site-footer__contact a:hover,
.site-footer__contact a:focus-visible {
    color: var(--color-accent);
}

@media (prefers-reduced-motion: reduce) {
    .site-footer__links a:hover {
        transform: none;
    }
}

.site-footer__bottom {
    border-top: 1px solid rgba(255, 255, 255, 0.15);
    padding-block: var(--space-sm);
    font-size: 0.8rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 4px;
}

.site-footer__credit {
    color: rgba(255, 255, 255, 0.6);
}

.site-footer__credit a {
    color: var(--color-accent);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.site-footer__credit a:hover {
    color: var(--color-light);
    text-decoration: underline;
}

/* 12. Utilities ------------------------------------------------------------------- */
.text-center {
    text-align: center;
}

/* Ambient screen wash (partials/police-lights.php) — fixed to the
   viewport so it reads as coming from the screen itself regardless of
   scroll position or which page is open. Each light is a full-viewport
   radial gradient anchored at a top corner rather than a small blurred
   blob: the gradient's own soft falloff does the diffusing (a CSS
   filter:blur on top of an already-fading gradient just doubles up and
   dilutes it to the point of being invisible — that's why an earlier
   version of this wasn't showing up). High z-index so the wash is visible
   over the header too, but low peak opacity so it never competes with
   page content for legibility. Each light pulses twice per cycle (not a
   rapid strobe — two gentle pulses, not a flicker) — gentle by design,
   and also keeps this well clear of the ~3-flashes-per-second
   photosensitive-seizure threshold. Blue's 0.2s delay is exactly the gap
   between red's own two pulses, so the sequence reads as left, right,
   left, right — not two independent left/right pairs firing on top of
   each other — before going quiet until the next 10s cycle. */
.police-lights {
    position: fixed;
    inset: 0;
    z-index: 200;
    pointer-events: none;
}

.police-lights__glow {
    position: absolute;
    inset: 0;
    opacity: 0;
    animation: police-flash 10s ease-in-out infinite;
}

.police-lights__glow--red {
    background: radial-gradient(circle at top left, rgba(220, 38, 38, 0.52) 0%, rgba(220, 38, 38, 0.2) 30%, rgba(220, 38, 38, 0) 65%);
}

.police-lights__glow--blue {
    background: radial-gradient(circle at top right, rgba(37, 99, 235, 0.52) 0%, rgba(37, 99, 235, 0.2) 30%, rgba(37, 99, 235, 0) 65%);
    animation-delay: 0.2s;
}

@keyframes police-flash {
    0%, 100% {
        opacity: 0;
    }
    2% {
        opacity: 0.42;
    }
    4% {
        opacity: 0;
    }
    6% {
        opacity: 0.42;
    }
    8% {
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .police-lights {
        display: none;
    }
}

/* Electric-spark cursor follower (partials/cursor-zap.php) — main.js lerps
   its position toward the real cursor every frame, so it trails a beat
   behind instead of rigidly overlapping the pointer. Starts hidden
   (opacity 0) until the first real mousemove (see main.js), stays
   pointer-events:none throughout so it can never intercept a click, and
   is left out entirely on touch/coarse-pointer devices and under
   prefers-reduced-motion — same "off, not just slower" rule as the police
   lights above, since this too runs for as long as the page stays open. */
.cursor-zap {
    position: fixed;
    top: 0;
    left: 0;
    width: 32px;
    height: 32px;
    /* Red fill (not the gold accent) so the bolt itself stays visible
       against the site's mostly-white sections, not just the dark ones —
       the gold shows up in the glow around it instead, which is where an
       "electric" look actually reads best regardless of background. */
    color: var(--color-primary);
    filter: drop-shadow(0 0 7px rgba(255, 194, 48, 0.95)) drop-shadow(0 0 18px rgba(255, 194, 48, 0.6)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
    opacity: 0;
    z-index: 300;
    pointer-events: none;
    transform: translate(-50%, -50%);
    will-change: transform;
}

.cursor-zap svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* The flicker keyframes drive opacity every frame, which would fight the
   base rule's opacity:0 "hidden until first mousemove" state above if it
   ran unconditionally — so the animation only starts once .is-active (set
   by main.js) is present. */
.cursor-zap.is-active {
    animation: cursor-zap-flicker 1.6s ease-in-out infinite;
}

@keyframes cursor-zap-flicker {
    0%, 100% { opacity: 1; }
    45% { opacity: 1; }
    50% { opacity: 0.55; }
    55% { opacity: 1; }
    82% { opacity: 1; }
    85% { opacity: 0.7; }
    88% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce), (pointer: coarse) {
    .cursor-zap {
        display: none;
    }
}

/* Scroll reveal — progressive enhancement only. The JS adds .reveal-init
   right before observing an element, so with JS disabled nothing ever
   receives it and content stays fully visible by default. */
.reveal-init {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 550ms ease, transform 550ms ease;
}

.reveal-init.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .reveal-init {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* 13. Responsive --------------------------------------------------------------------- */
@media (max-width: 980px) {
    /* The connecting line only makes sense across one unbroken row —
       once the stats wrap onto multiple rows, drop it and let every pill
       sit flush in a normal wrapped grid. */
    .stat-strip__connector {
        display: none;
    }

    .primary-nav {
        display: none;
    }

    .nav-toggle {
        display: block;
    }

    .site-search__form {
        width: 240px;
    }

    .site-header[data-nav-open="true"] .primary-nav {
        display: block;
        position: absolute;
        left: 0;
        right: 0;
        top: 100%;
        background: var(--color-light);
        border-top: 1px solid var(--color-border);
        padding: var(--space-md);
    }

    .site-header[data-nav-open="true"] .primary-nav__list {
        flex-direction: column;
        gap: var(--space-sm);
    }

    .site-footer__inner {
        grid-template-columns: repeat(2, 1fr);
        row-gap: var(--space-xl);
    }

    /* The left-border dividers only make sense in a single unbroken row —
       once columns wrap onto a second row they'd sit as a stray vertical
       line under an unrelated column, so drop them here. */
    .site-footer__col + .site-footer__col {
        border-left: none;
        padding-left: 0;
    }

    .contact-block {
        grid-template-columns: 1fr;
    }

    .faq-list {
        flex-direction: column;
    }

    .services-grid,
    .why-choose__list {
        grid-template-columns: repeat(2, 1fr);
    }

    .services-grid a.service-card,
    .services-grid a.service-card:nth-child(5),
    .services-grid a.service-card:nth-child(6),
    .services-grid a.service-card:nth-child(7) {
        grid-column: auto;
    }

    /* Journey becomes a vertical, top-to-bottom stack below the horizontal
       breakpoint — the horizontal track/progress bar only makes sense when
       all four stages sit in one row. */
    .journey__track,
    .journey__progress {
        display: none;
    }

    .journey__list {
        grid-template-columns: 1fr;
        gap: var(--space-md);
        max-width: 480px;
        margin-inline: auto;
    }

    li.journey__step {
        display: grid;
        grid-template-columns: 72px 1fr;
        column-gap: var(--space-md);
        text-align: left;
        padding-block: var(--space-sm);
    }

    .journey__step:not(:last-child) {
        position: relative;
        padding-bottom: var(--space-lg);
    }

    .journey__step:not(:last-child)::after {
        content: "";
        position: absolute;
        left: 35px;
        top: 72px;
        bottom: 4px;
        width: 2px;
        background: repeating-linear-gradient(180deg, var(--color-border) 0 8px, transparent 8px 16px);
    }

    .journey__icon {
        margin-inline: 0;
        margin-bottom: 0;
        grid-row: 1 / span 2;
    }

    .journey__step h3,
    .journey__step p {
        margin-inline: 0;
    }

    .site-map {
        height: 400px;
    }

    .about-section {
        grid-template-columns: 1fr;
    }

    .about-section__media {
        margin-bottom: var(--space-lg);
    }

    a.service-row,
    a.service-row.service-row--wide-media {
        grid-template-columns: 1fr;
    }

    .service-row--reverse .service-row__media {
        order: 0;
    }

    .service-row__media {
        margin-bottom: var(--space-lg);
    }

    .contact-layout {
        grid-template-columns: 1fr;
    }

    .contact-layout .about-section__media {
        height: auto;
        margin-bottom: var(--space-lg);
    }

    .contact-layout .about-section__media img {
        height: auto;
    }
}

@media (max-width: 767px) {
    :root {
        --space-xl: 40px;
    }

    .site-footer__inner {
        grid-template-columns: 1fr;
    }

    .utility-bar__inner {
        justify-content: center;
        flex-wrap: wrap;
        row-gap: 4px;
    }

    .call-fab {
        right: var(--space-md);
        bottom: var(--space-md);
    }

    .call-fab__toggle {
        width: 52px;
        height: 52px;
    }

    .stat-strip__list {
        justify-content: flex-start;
        column-gap: var(--space-xl);
    }

    .stat-strip__item {
        width: auto;
        flex: 1 1 calc(50% - var(--space-xl));
    }

    .services-grid,
    .why-choose__list {
        grid-template-columns: 1fr;
    }

    .site-map {
        height: 340px;
    }

    .why-choose__trust {
        flex-direction: column;
    }

    .why-choose__trust-text {
        text-align: center;
    }

    .why-choose__trust--wide {
        padding: var(--space-lg) var(--space-md);
    }

    .why-choose__trust--wide .why-choose__trust-text {
        text-align: center;
    }
}

@media (max-width: 479px) {
    .btn {
        display: block;
        text-align: center;
        justify-content: center;
    }
}
