/* =========================================================================
   3Thirty — shared base stylesheet
   -------------------------------------------------------------------------
   Every theme loads this first, then its own theme.css. This file owns
   structure and behaviour: grids, states, responsive breakpoints, focus
   handling, reduced-motion. It makes no visual decision it does not first
   route through a custom property.

   That is the whole contract. A theme changes the look by overriding tokens
   in :root and, where it wants something structurally different, a handful of
   rules. A theme never has to restate the layout, and a new section only has
   to be written once.

   --primary and --accent are injected inline per client by the layout.
   ========================================================================= */

:root {
  /* ---- Brand (per client) ---- */
  --primary: #16203a;
  --accent: #c69b4c;
  --accent-ink: #2a1f08;
  --accent-soft: color-mix(in srgb, var(--accent) 14%, white);

  /* ---- Neutrals ---- */
  --ink: #1b2435;
  --ink-soft: #5b6473;
  /* Every theme overrides this, and every one of them has to clear 4.5:1
     against --paper. It is only ever used at .8-.85rem — breadcrumbs, a
     testimonial's job title, the note under the contact form — which is small
     text under WCAG, so the large-text allowance of 3:1 does not apply. The
     grey this started as read as 3.4:1 and was unreadable in daylight. */
  --ink-faint: #666e7d;
  --paper: #ffffff;
  --paper-alt: #f7f5ef;
  --line: #e7e3d8;
  --surface-dark: #131c33;
  --on-dark: #ffffff;
  --on-dark-soft: #c4cbd9;
  --on-dark-faint: #8b93a3;
  /* A token rather than a literal so a dark section can reassign it once
     instead of every link rule needing a dark counterpart. */
  --link-color: var(--primary);

  /* ---- Type ---- */
  --font-display: "Georgia", "Times New Roman", serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --body-size: clamp(1rem, .96rem + .2vw, 1.075rem);
  --body-leading: 1.65;
  --h1-size: clamp(2.4rem, 1.6rem + 3.6vw, 4.25rem);
  --h2-size: clamp(1.9rem, 1.4rem + 2vw, 2.9rem);
  --h3-size: clamp(1.2rem, 1.05rem + .6vw, 1.5rem);
  --heading-family: var(--font-display);
  --heading-weight: 600;
  --heading-leading: 1.1;
  --heading-tracking: -.01em;
  --heading-transform: none;
  --lede-size: 1.15rem;

  /* ---- Rhythm ---- */
  --container: 1200px;
  --container-narrow: 820px;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --section-y: clamp(3.5rem, 7vw, 7rem);
  --section-head-gap: clamp(2rem, 4vw, 3.25rem);
  --grid-gap: clamp(1rem, 2vw, 1.75rem);
  /* Separate from --grid-gap because a theme that wants its cards butted
     together behind a hairline should not also collapse the gaps between
     team portraits, stats, and steps, which have no borders to rule them. */
  --card-gap: var(--grid-gap);

  /* ---- Shape ---- */
  --radius: 16px;
  --radius-sm: 10px;
  --border-width: 1px;
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .08);
  --shadow-md: 0 10px 30px -12px rgba(16, 24, 40, .22);
  --shadow-lg: 0 30px 70px -24px rgba(16, 24, 40, .35);

  /* ---- Buttons ---- */
  --btn-radius: 999px;
  --btn-padding: .85rem 1.5rem;
  --btn-padding-sm: .6rem 1.1rem;
  --btn-size: .98rem;
  --btn-weight: 600;
  --btn-tracking: normal;
  --btn-transform: none;
  --btn-border-width: 1.5px;
  --btn-shadow: 0 8px 22px -10px color-mix(in srgb, var(--accent) 70%, transparent);
  --btn-lift: -2px;

  /* ---- Eyebrows / small labels ---- */
  --eyebrow-color: var(--accent);
  --eyebrow-size: .78rem;
  --eyebrow-weight: 700;
  --eyebrow-tracking: .18em;
  --eyebrow-transform: uppercase;
  --eyebrow-family: var(--font-body);

  /* ---- Header ---- */
  --header-height: 76px;
  --header-bg: color-mix(in srgb, var(--paper) 88%, transparent);
  --header-blur: saturate(180%) blur(12px);
  --header-border: var(--border-width) solid var(--line);
  --header-position: sticky;
  --nav-size: .97rem;
  --nav-weight: 500;
  --nav-tracking: normal;
  --nav-transform: none;
  --nav-radius: 8px;
  --nav-hover-bg: var(--paper-alt);
  --nav-hover-fg: var(--primary);
  --brand-size: 1.4rem;
  --brand-family: var(--heading-family);
  /* None normally; a transparent header sets the same reversal the footer uses,
     because a dark wordmark on a hero photo is unreadable. */
  --header-logo-filter: none;

  /* ---- Hero ---- */
  --hero-min-height: clamp(560px, 82vh, 860px);
  --hero-max-width: 760px;
  --hero-align: start;
  --hero-text-align: left;
  --hero-overlay: linear-gradient(180deg, rgba(11,16,26,.55) 0%, rgba(11,16,26,.45) 45%, rgba(11,16,26,.75) 100%);
  --hero-sub-size: clamp(1.05rem, 1rem + .5vw, 1.3rem);
  /* White because the default hero is text over a photo. The split hero has no
     scrim to sit on and reassigns these rather than fighting them. */
  --hero-fg: #fff;
  --hero-heading-fg: #fff;
  --hero-sub-fg: rgba(255, 255, 255, .9);

  /* ---- Cards ---- */
  --card-bg: var(--paper);
  --card-border: var(--border-width) solid var(--line);
  --card-radius: var(--radius);
  --card-pad: clamp(1.5rem, 2.5vw, 2.25rem);
  --card-shadow: none;
  --card-shadow-hover: var(--shadow-md);
  --card-lift: -4px;
  --card-hover-border: color-mix(in srgb, var(--accent) 45%, var(--line));
  --icon-bg: var(--accent-soft);
  --icon-fg: var(--primary);
  --icon-radius: 14px;
  --icon-size: 52px;

  /* ---- Forms ---- */
  --input-radius: var(--radius-sm);
  --input-border: 1.5px solid var(--line);
  --input-bg: #fff;

  /* ---- Footer ---- */
  --footer-bg: var(--surface-dark);
  --footer-fg: var(--on-dark-soft);
  --footer-heading-fg: var(--on-dark);
  --footer-rule: rgba(255, 255, 255, .1);
  /* One logo file has to sit on a light header and a dark footer. Flattening
     it to white is what a print designer would do with a one-color reversal,
     and it beats asking a client to supply two files. A theme with a light
     footer sets this to none. */
  --footer-logo-filter: brightness(0) invert(1);

  /* ---- Motion ---- */
  --reveal-shift: 18px;
  --reveal-duration: .6s;
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* The user agent gives figure and blockquote a 40px inline margin, which
   inside a grid track silently shrinks the item by 80px and reads as a
   mysteriously generous gap. Reset once here rather than per component. */
figure, blockquote { margin: 0; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  line-height: var(--body-leading);
  font-size: var(--body-size);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

h1, h2, h3, h4 {
  font-family: var(--heading-family);
  color: var(--ink);
  line-height: var(--heading-leading);
  font-weight: var(--heading-weight);
  letter-spacing: var(--heading-tracking);
  text-transform: var(--heading-transform);
  margin: 0 0 .5em;
  text-wrap: balance;
}
h1 { font-size: var(--h1-size); }
h2 { font-size: var(--h2-size); }
h3 { font-size: var(--h3-size); }
p { margin: 0 0 1rem; }
a { color: var(--link-color); text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* ---------- Accessibility helpers ---------- */
.skip-link {
  position: absolute; left: 1rem; top: -3rem;
  background: var(--ink); color: #fff; padding: .6rem 1rem;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm); z-index: 200;
  transition: top .2s ease;
}
.skip-link:focus { top: 0; }

:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--accent);
  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;
}
.hp { position: absolute !important; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- Layout ---------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.container--narrow { max-width: var(--container-narrow); }

/* A blog post is one column of prose, so every section on it is constrained to
   a readable measure. The other page types are compositions of blocks where
   full width is the point, which is what data-measure distinguishes. */
[data-measure="narrow"] main .container { max-width: var(--container-narrow); }
/* A section reserves room for two things a theme may want and base.css never
   does itself.

   --section-clip cuts an angled or curved edge. The section below does the
   cutting, not the one above, so whatever color is arriving from above shows
   through the wedge and the seam is right by construction. --section-clip-inset
   buys back the vertical room the wedge eats, so text never lands inside it.

   --section-overlap lifts a section over the one above — a card row crossing
   into a dark band, say. A theme scopes both to the sections it means, e.g.
   `.hero + .section { --section-overlap: 4rem; }`. */
.section {
  position: relative;
  padding-top: calc(var(--section-y) + var(--section-clip-inset, 0px) + var(--section-overlap, 0px));
  padding-bottom: var(--section-y);
  margin-top: calc(-1 * var(--section-overlap, 0px));
  clip-path: var(--section-clip, none);
}
.section--alt { background: var(--paper-alt); }

/* A dark section rebinds the neutrals rather than restating every descendant.
   Components already draw themselves from these tokens, so a card, an input,
   a stat label and an FAQ rule all invert together and anything added later
   inherits the same treatment for free. Accent stays put: it is the client's
   color and it is what should still read as the accent here. */
/* Two dark tones, identical apart from what they are painted with. `dark` is
   the theme's own near-black, the same one the footer uses. `brand` is the
   client's primary, which is the color they already recognise as theirs from
   the announcement bar — a band in it reads as part of the business rather
   than as another slab of chrome. */
.section--dark { --section-surface: var(--surface-dark); }
.section--brand { --section-surface: var(--primary); }
.section--dark, .section--brand {
  --ink: var(--on-dark);
  --ink-soft: var(--on-dark-soft);
  --ink-faint: var(--on-dark-faint);
  --paper: color-mix(in srgb, var(--section-surface) 88%, white);
  --paper-alt: var(--section-surface);
  --line: rgba(255, 255, 255, .14);
  --card-bg: color-mix(in srgb, var(--section-surface) 88%, white);
  --card-hover-border: color-mix(in srgb, var(--accent) 55%, transparent);
  --input-bg: color-mix(in srgb, var(--section-surface) 82%, white);
  --nav-hover-bg: rgba(255, 255, 255, .08);
  /* Primary is a dark brand color by convention. Anything that used it as a
     foreground needs the light-surface equivalent of the same role. */
  --link-color: var(--accent);
  --stat-color: var(--on-dark);
  --step-bg: var(--accent);
  --step-fg: var(--accent-ink);
  background: var(--section-surface);
  color: var(--on-dark-soft);
}
.section--dark :is(h1, h2, h3, h4),
.section--brand :is(h1, h2, h3, h4) { color: var(--on-dark); }

.section-head { max-width: var(--container-narrow); margin-bottom: var(--section-head-gap); }
.section-head.center { margin-inline: auto; text-align: center; }
.eyebrow {
  display: inline-block;
  font-family: var(--eyebrow-family);
  font-weight: var(--eyebrow-weight);
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-tracking);
  text-transform: var(--eyebrow-transform);
  color: var(--eyebrow-color); margin-bottom: 1rem;
}
.lede { font-size: var(--lede-size); color: var(--ink-soft); }
.section--dark .lede, .section--brand .lede { color: var(--on-dark-soft); }

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-ink);
  display: inline-flex; align-items: center; gap: .55rem;
  background: var(--btn-bg); color: var(--btn-fg);
  padding: var(--btn-padding); border-radius: var(--btn-radius);
  font-weight: var(--btn-weight); font-size: var(--btn-size);
  letter-spacing: var(--btn-tracking); text-transform: var(--btn-transform);
  text-decoration: none;
  border: var(--btn-border-width) solid transparent; cursor: pointer; line-height: 1;
  transition: transform .15s ease, box-shadow .2s ease, background-color .2s ease, color .2s ease;
  box-shadow: var(--btn-shadow);
}
.btn:hover { transform: translateY(var(--btn-lift)); }
.btn:active { transform: translateY(0); }
.btn .arrow { transition: transform .2s ease; }
.btn:hover .arrow { transform: translateX(3px); }

.btn--ghost {
  --btn-bg: transparent; --btn-fg: var(--btn-ghost-fg, #fff);
  border-color: var(--btn-ghost-border, rgba(255, 255, 255, .55)); box-shadow: none;
}
.btn--ghost:hover { background: rgba(255, 255, 255, .12); }

.btn--outline {
  --btn-bg: transparent; --btn-fg: var(--ink);
  border-color: var(--ink); box-shadow: none;
}
.btn--outline:hover { background: var(--ink); color: #fff; }

.btn--sm { padding: var(--btn-padding-sm); font-size: .9rem; }

.text-link {
  display: inline-flex; align-items: center; gap: .4rem;
  font-weight: 600; color: var(--link-color); text-decoration: none; font-size: .95rem;
}
.text-link .arrow { transition: transform .2s ease; }
.text-link:hover .arrow { transform: translateX(4px); }

/* ---------- Announcement bar ---------- */
/* Scrolls away with the page even when the header sticks, because a permanent
   strip of opening hours is a permanent tax on the viewport. */
.announcement {
  background: var(--announcement-bg, var(--primary));
  color: var(--announcement-fg, #fff);
  font-size: .87rem;
}
.announcement-inner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .35rem 1.5rem; padding-block: .55rem;
}
.announcement p { margin: 0; }
.announcement-meta { display: flex; flex-wrap: wrap; gap: .35rem 1.25rem; }
.announcement a { color: inherit; font-weight: 600; text-decoration: none; }
.announcement a:hover { text-decoration: underline; }

/* ---------- Header / nav ---------- */
.site-header {
  position: var(--header-position); top: 0; z-index: 100;
  background: var(--header-bg);
  color: var(--header-fg, var(--ink));
  backdrop-filter: var(--header-blur);
  -webkit-backdrop-filter: var(--header-blur);
  border-bottom: var(--header-border);
}
/* Relative rather than static: the mobile nav panel hangs off the header, and
   without a positioned ancestor it would anchor to the viewport instead — which
   is wrong the moment an announcement bar sits above. */
.site-header--static { position: relative; }
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; min-height: var(--header-height); }

/* Links centred between the logo and the action, rather than pushed up against
   one of them. The empty third column is what keeps the middle actually
   centred when the logo and the button are different widths. */
@media (min-width: 901px) {
  .site-header--centered-nav .header-inner {
    display: grid; grid-template-columns: 1fr auto 1fr; justify-content: initial;
  }
  .site-header--centered-nav .header-actions { justify-content: flex-end; }

  /* Logo over the links, both centred: the arrangement a firm that wants to
     look established tends to ask for. The action sits absolutely so it does
     not pull the stack off centre. */
  .site-header--logo-above-nav .header-inner {
    position: relative; flex-direction: column; justify-content: center; gap: .5rem;
    padding-block: 1.1rem;
  }
  .site-header--logo-above-nav .header-actions {
    position: absolute; right: var(--gutter); top: 50%; transform: translateY(-50%);
  }
}

/* Transparent over a hero photo until the page moves. Only ever applied when
   the page opens with an image hero, so the white text always has something
   behind it. Without JS the header simply stays transparent, which is still
   legible over the hero's scrim. */
/* Absolute with no `top`, so the bar keeps the exact position it would have
   had in flow — directly under an announcement strip if there is one — while
   taking no height, which is what lets the hero start behind it. Offsetting a
   fixed bar instead would mean hardcoding the strip's height. */
.site-header--transparent {
  position: absolute; top: auto; left: 0; right: 0;
  background: transparent; border-bottom-color: transparent;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  --header-fg: #fff;
  --header-line: rgba(255, 255, 255, .35);
  --header-logo-filter: var(--footer-logo-filter);
  --nav-hover-bg: rgba(255, 255, 255, .14);
  --nav-hover-fg: #fff;
}
/* Once the hero has gone past, the bar comes back as a solid one. Without JS
   it simply scrolls away with the hero, which is a working page, not a broken
   one. */
.site-header--transparent.is-stuck {
  position: fixed; top: 0;
  background: var(--header-bg); border-bottom: var(--header-border);
  backdrop-filter: var(--header-blur); -webkit-backdrop-filter: var(--header-blur);
  animation: header-drop .25s ease;
  --header-fg: var(--ink);
  --header-line: var(--line);
  --header-logo-filter: none;
  --nav-hover-bg: var(--paper-alt);
  --nav-hover-fg: var(--primary);
}
@keyframes header-drop { from { transform: translateY(-100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .site-header--transparent.is-stuck { animation: none; }
}

.header-phone {
  display: inline-flex; align-items: center; gap: .5rem;
  font-weight: 700; font-size: 1.02rem; color: inherit; text-decoration: none;
  white-space: nowrap;
}
.header-phone:hover { color: var(--accent); }
.header-phone .ci { color: var(--accent); }

.brand { display: inline-flex; align-items: center; gap: .6rem; text-decoration: none; color: inherit; font-family: var(--brand-family); font-weight: 600; font-size: var(--brand-size); }
.brand img { max-height: var(--brand-logo-height, 44px); width: auto; filter: var(--header-logo-filter); }

.primary-nav > ul { list-style: none; display: flex; align-items: center; gap: .35rem; margin: 0; padding: 0; }
.primary-nav a {
  display: block; text-decoration: none; color: inherit;
  font-weight: var(--nav-weight); font-size: var(--nav-size);
  letter-spacing: var(--nav-tracking); text-transform: var(--nav-transform);
  padding: .55rem .8rem; border-radius: var(--nav-radius);
  transition: background-color .15s ease, color .15s ease;
}
.primary-nav a:hover { background: var(--nav-hover-bg); color: var(--nav-hover-fg); }
.has-children { position: relative; }
.has-children > a::after { content: ""; display: inline-block; width: .42em; height: .42em; margin-left: .45em; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg) translateY(-2px); opacity: .6; }
/* Its own color, not the header's: this panel is always on --paper, including
   under a transparent header whose text is white. */
.subnav {
  list-style: none; margin: 0; padding: .4rem; position: absolute; top: calc(100% + .4rem); left: 0;
  color: var(--ink);
  min-width: 220px; background: var(--paper); border: var(--border-width) solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md); opacity: 0; visibility: hidden; transform: translateY(6px);
  transition: opacity .18s ease, transform .18s ease, visibility .18s; z-index: 20;
}
.has-children:hover > .subnav,
.has-children:focus-within > .subnav { opacity: 1; visibility: visible; transform: translateY(0); }
.subnav a { padding: .55rem .75rem; }

.header-actions { display: flex; align-items: center; gap: .75rem; }

.nav-toggle {
  display: none; align-items: center; justify-content: center; color: inherit;
  width: 46px; height: 46px; background: transparent; border: var(--border-width) solid var(--header-line, var(--line)); border-radius: var(--nav-radius); cursor: pointer;
}
.nav-toggle span, .nav-toggle span::before, .nav-toggle span::after {
  content: ""; display: block; width: 22px; height: 2px; background: currentColor; border-radius: 2px; transition: transform .25s ease, opacity .2s ease;
}
.nav-toggle span::before { transform: translateY(-7px); }
.nav-toggle span::after { transform: translateY(5px); }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(0) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after { transform: translateY(-2px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: inline-flex; }
  .header-actions .btn { display: none; }
  /* The number stays reachable as a tap target but gives up its width, which
     would otherwise push the menu button off the edge of a phone. */
  .header-phone-number { display: none; }
  .header-phone {
    width: 46px; height: 46px; justify-content: center; font-size: 1.35rem;
    border: var(--border-width) solid var(--header-line, var(--line)); border-radius: var(--nav-radius);
  }
  /* Hung off the header rather than pinned to the viewport, so an announcement
     bar above it does not leave the panel covering the header it opened from.
     Its own color for the same reason the subnav has one. */
  .primary-nav {
    position: absolute; top: 100%; left: 0; right: 0; background: var(--paper); color: var(--ink);
    border-bottom: var(--border-width) solid var(--line); box-shadow: var(--shadow-lg);
    max-height: 0; overflow: hidden; transition: max-height .3s ease; visibility: hidden;
  }
  .primary-nav.open { max-height: calc(100vh - var(--header-height)); overflow-y: auto; visibility: visible; }
  .primary-nav > ul { flex-direction: column; align-items: stretch; gap: 0; padding: .75rem var(--gutter) 1.5rem; }
  .primary-nav a { padding: .85rem .5rem; border-radius: 0; border-bottom: var(--border-width) solid var(--line); }
  .has-children > a::after { float: right; }
  .subnav {
    position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none;
    border: none; padding: 0 0 0 1rem; min-width: 0;
  }
  .subnav a { color: var(--ink-soft); }
}

/* ---------- Breadcrumbs ---------- */
/* Sits between the header and main rather than inside a section, so it does
   not inherit a section's background and cannot end up inside a dark hero. */
.breadcrumbs { background: var(--breadcrumb-bg, var(--paper-alt)); border-bottom: var(--border-width) solid var(--line); }
.breadcrumbs ol {
  list-style: none; margin: 0; padding-block: .8rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .55rem;
  font-size: .85rem; color: var(--ink-faint);
}
.breadcrumbs li { display: flex; align-items: center; gap: .55rem; }
.breadcrumbs li + li::before { content: "/"; color: var(--ink-faint); opacity: .6; }
.breadcrumbs a { color: var(--ink-soft); text-decoration: none; }
.breadcrumbs a:hover { color: var(--primary); text-decoration: underline; }
.breadcrumbs [aria-current="page"] { color: var(--ink); font-weight: 500; }

/* ---------- Hero ---------- */
.hero { position: relative; isolation: isolate; color: var(--hero-fg); overflow: hidden; }
.hero-carousel { position: relative; display: grid; }
.hero-slide {
  position: absolute; inset: 0; opacity: 0; visibility: hidden;
  transition: opacity .9s ease; z-index: 1;
}
.hero-slide.is-active { opacity: 1; visibility: visible; z-index: 2; }
.hero-carousel > .hero-slide:first-child { position: relative; } /* establishes height */
.hero-slide-bg { position: absolute; inset: 0; background-size: cover; background-position: center; z-index: -2; }
.hero-slide::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--hero-overlay);
}
.hero--single { background: var(--surface-dark); }
.hero-inner {
  position: relative; z-index: 3;
  min-height: var(--hero-min-height);
  display: flex; flex-direction: column; justify-content: center;
  align-items: var(--hero-align);
  text-align: var(--hero-text-align);
  padding-block: clamp(4rem, 10vh, 8rem);
  max-width: var(--hero-max-width);
  margin-inline: var(--hero-margin, 0);
}
.hero .eyebrow { color: var(--accent); }
.hero h1 { color: var(--hero-heading-fg); margin-bottom: 1.1rem; }
.hero .hero-sub { font-size: var(--hero-sub-size); color: var(--hero-sub-fg); max-width: 56ch; margin-bottom: 2rem; }
.hero-cta { display: flex; flex-wrap: wrap; gap: .9rem; justify-content: var(--hero-align); }

/* ---------- Split hero: photo beside the words, not behind them ---------- */
.hero--split {
  --hero-fg: var(--ink);
  --hero-heading-fg: var(--ink);
  --hero-sub-fg: var(--ink-soft);
  --btn-ghost-fg: var(--primary);
  --btn-ghost-border: var(--line);
  /* The text column no longer has to fill a viewport-height photo, so it is
     sized by its own content and the image alongside it. */
  --hero-min-height: 0;
  --hero-max-width: none;
  background: var(--hero-split-bg, var(--paper));
  padding-block: var(--section-y);
  overflow: visible;
}
.hero-split { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.hero--split .hero-inner { padding-block: 0; }
.hero-figure { position: relative; }
.hero-figure img {
  display: block; width: 100%; height: auto;
  border-radius: var(--hero-figure-radius, var(--radius));
  box-shadow: var(--hero-figure-shadow, var(--shadow-lg));
  aspect-ratio: var(--hero-figure-ratio, auto);
  object-fit: cover;
}
/* Stacked below this, where two columns would leave both too narrow to read
   and shrink the photo to a thumbnail. */
@media (min-width: 900px) {
  .hero-split { grid-template-columns: var(--hero-split-columns, 1.05fr .95fr); }
}

.hero-controls { position: absolute; z-index: 5; bottom: 1.75rem; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 1rem; }
.hero-dots { display: flex; gap: .5rem; }
.hero-dot { width: 10px; height: 10px; border-radius: 50%; border: 0; background: rgba(255,255,255,.4); cursor: pointer; padding: 0; transition: background-color .2s, transform .2s; }
.hero-dot[aria-current="true"] { background: var(--accent); transform: scale(1.25); }
.hero-arrow, .hero-pause {
  width: 42px; height: 42px; border-radius: 50%; border: 1px solid rgba(255,255,255,.45);
  background: rgba(255,255,255,.08); color: #fff; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: background-color .2s ease;
}
.hero-arrow:hover, .hero-pause:hover { background: rgba(255,255,255,.22); }
.hero-count { color: rgba(255,255,255,.85); font-size: .85rem; font-variant-numeric: tabular-nums; min-width: 3ch; text-align: center; }

/* ---------- Cards (services and anything else grid-shaped) ---------- */
.split-head { display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: end; }
@media (min-width: 820px) { .split-head { grid-template-columns: 1.1fr 1fr; gap: 3rem; } .split-head .lede { margin: 0; } }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--card-gap); }
.card {
  background: var(--card-bg); border: var(--card-border); border-radius: var(--card-radius);
  padding: var(--card-pad); box-shadow: var(--card-shadow);
  transition: transform .2s ease, box-shadow .25s ease, border-color .2s ease;
  display: flex; flex-direction: column; gap: .65rem;
}
.section--alt .card { background: var(--paper); }
.card:hover { transform: translateY(var(--card-lift)); box-shadow: var(--card-shadow-hover); border-color: var(--card-hover-border); }
.card .icon {
  width: var(--icon-size); height: var(--icon-size); border-radius: var(--icon-radius);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--icon-bg); color: var(--icon-fg); font-size: 1.5rem; margin-bottom: .75rem;
}
.card h3 { margin-bottom: 0; }
.card p { color: var(--ink-soft); margin-bottom: .35rem; }
.card .text-link { margin-top: auto; }

/* ---------- Prose (rich text from the CMS editor) ---------- */
/* Scoped to .prose rather than applied to bare elements, so editor output
   cannot restyle headings and lists in the rest of the page. */
.prose { max-width: var(--prose-measure, 68ch); color: var(--ink); }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h2, .prose h3, .prose h4 { margin-top: 1.6em; }
.prose p, .prose ul, .prose ol { margin: 0 0 1.1rem; }
.prose ul, .prose ol { padding-left: 1.4rem; }
.prose li { margin-bottom: .45rem; }
.prose li::marker { color: var(--accent); }
.prose a { color: var(--link-color); font-weight: 500; }
.prose blockquote {
  margin: 1.75rem 0; padding: .35rem 0 .35rem 1.4rem;
  border-left: 3px solid var(--accent); color: var(--ink-soft); font-size: 1.1rem;
}
.prose strong { color: var(--ink); }
.prose hr { border: 0; border-top: var(--border-width) solid var(--line); margin: 2.5rem 0; }
/* A narrow rich-text block narrows its container rather than floating a short
   measure inside a full-width one, so the heading and the body share an edge
   whether the theme aligns section heads left or centre. */
.rich-text .prose { max-width: none; }

/* ---------- Image and text ---------- */
.media-text-grid { display: grid; grid-template-columns: 1fr; gap: clamp(1.75rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 860px) { .media-text-grid { grid-template-columns: 1fr 1fr; } }
/* The flip is done with grid order rather than direction so the image still
   comes first in the DOM, and therefore first on a narrow screen. */
@media (min-width: 860px) {
  .media-text--flip .media-text-figure { order: 2; }
}
.media-text-figure { border-radius: var(--radius); overflow: hidden; background: var(--paper-alt); }
.media-text-figure img { width: 100%; aspect-ratio: var(--media-text-ratio, 4 / 3); object-fit: cover; }
.media-text-body .btn { margin-top: 1.5rem; }

/* ---------- Steps ---------- */
.steps-list { list-style: none; counter-reset: none; margin: 0; padding: 0; display: grid; gap: var(--grid-gap); }
@media (min-width: 760px) { .steps-list { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); } }
.step { display: flex; gap: 1rem; align-items: flex-start; }
.step-number {
  flex: none; width: var(--step-size, 44px); height: var(--step-size, 44px);
  border-radius: var(--step-radius, 50%); display: inline-flex; align-items: center; justify-content: center;
  background: var(--step-bg, var(--primary)); color: var(--step-fg, #fff);
  font-family: var(--heading-family); font-weight: 700; font-size: 1.05rem;
}
.step h3 { margin-bottom: .3rem; }
.step p { color: var(--ink-soft); margin: 0; }

/* ---------- Stats ---------- */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--grid-gap); margin: 0; padding: 0; list-style: none; }
.stat { text-align: var(--stat-align, center); padding: var(--stat-pad, 1rem 0); }
.stat-value {
  margin: 0;
  font-family: var(--heading-family); font-weight: 700;
  font-size: var(--stat-size, clamp(2.4rem, 1.8rem + 2.4vw, 3.6rem));
  line-height: 1; letter-spacing: var(--heading-tracking); color: var(--stat-color, var(--primary));
}
.stat-label { margin: .6rem 0 0; font-weight: 600; color: var(--ink); }
.stat-note { display: block; font-weight: 400; font-size: .9rem; color: var(--ink-soft); margin-top: .25rem; }

/* ---------- Team ---------- */
.team-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--grid-gap); }
.member { text-align: var(--member-align, left); }
.member-photo {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: var(--member-photo-radius, var(--radius)); margin-bottom: 1rem; background: var(--paper-alt);
}
.member-photo--initials {
  display: flex; align-items: center; justify-content: center;
  background: var(--primary); color: #fff; font-family: var(--heading-family); font-size: 3rem; font-weight: 700;
}
.member h3 { margin-bottom: .15rem; }
.member-role { color: var(--accent); font-weight: 600; font-size: .92rem; margin-bottom: .6rem; }
.member-bio { color: var(--ink-soft); font-size: .95rem; }
.member-links { display: flex; gap: 1rem; font-size: .9rem; font-weight: 600; margin: 0; }
.member-links a { color: var(--link-color); text-decoration: none; }
.member-links a:hover { text-decoration: underline; }

/* ---------- Logo strip ---------- */
.logo-row {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: clamp(1.75rem, 4vw, 3.5rem);
}
.logo-row img {
  max-height: var(--logo-height, 56px); width: auto;
  /* Desaturated by default so a wall of mismatched brand colors does not
     out-shout the client's own palette. */
  filter: var(--logo-filter, grayscale(1));
  opacity: var(--logo-opacity, .65);
  transition: filter .2s ease, opacity .2s ease;
}
.logo-row li:hover img { filter: none; opacity: 1; }

/* ---------- Map ---------- */
.map-frame { border-radius: var(--radius); overflow: hidden; border: var(--card-border); background: var(--paper-alt); }
.map-frame iframe { display: block; width: 100%; height: clamp(280px, 42vh, 460px); border: 0; }
.map-address {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between;
  margin: 1.25rem 0 0; color: var(--ink-soft);
}

/* ---------- Carousel ---------- */
/* A scroll-snap track, not a JS slider. Each item claims at least
   --carousel-item, so a handful of items lay out as a plain row and a longer
   list overflows into something swipeable. Touch and keyboard scrolling both
   work with JS off; the arrows are the only enhancement. */
.carousel { position: relative; }
.carousel-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(min(100%, var(--carousel-item, 300px)), 1fr);
  gap: var(--grid-gap);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1px;
  /* Room for card shadows and focus rings, which a clipped overflow would cut. */
  padding: 4px;
  margin: -4px;
  scrollbar-width: none;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track > * { scroll-snap-align: start; }
.carousel-track:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.carousel-controls { display: flex; align-items: center; justify-content: center; gap: .6rem; margin-top: 1.75rem; }
.carousel-controls[hidden] { display: none; }
.carousel-dots { display: flex; align-items: center; gap: .45rem; }
.carousel-dot {
  width: 9px; height: 9px; padding: 0; border: 0; border-radius: 50%;
  background: var(--line); cursor: pointer; transition: background-color .2s, transform .2s;
}
.carousel-dot.is-active { background: var(--accent); transform: scale(1.35); }
.carousel-arrow {
  width: 42px; height: 42px; display: inline-flex; align-items: center; justify-content: center;
  border: var(--border-width) solid var(--line); border-radius: var(--btn-radius);
  background: var(--paper); color: var(--ink); cursor: pointer;
  transition: background-color .2s, border-color .2s, color .2s;
}
.carousel-arrow:hover:not(:disabled) { background: var(--primary); border-color: var(--primary); color: #fff; }
.carousel-arrow:disabled { opacity: .35; cursor: default; }

/* ---------- Testimonials ---------- */
.testimonials .carousel-track { --carousel-item: 320px; align-items: stretch; }
.testimonial {
  background: var(--card-bg); border: var(--card-border); border-radius: var(--card-radius);
  padding: var(--card-pad); display: flex; flex-direction: column; gap: 1rem; box-shadow: var(--card-shadow);
}
.section--alt .testimonial { background: var(--paper); }
.stars { color: var(--accent); letter-spacing: .15em; font-size: 1rem; }
/* Drawn rather than omitted so four stars still reads as four out of five at a
   glance, instead of as an unexplained short row. */
.star--empty { color: var(--line); }
.testimonial blockquote { font-size: 1.05rem; color: var(--ink); font-style: var(--quote-style, italic); line-height: 1.6; }
.testimonial figcaption { display: flex; align-items: center; gap: .8rem; margin-top: auto; }
.testimonial .avatar { width: 46px; height: 46px; border-radius: var(--avatar-radius, 50%); object-fit: cover; flex: none; }
.testimonial .avatar--initials { display: inline-flex; align-items: center; justify-content: center; background: var(--primary); color: #fff; font-weight: 700; font-family: var(--font-body); font-size: .95rem; }
.testimonial .who { display: block; font-weight: 700; line-height: 1.2; }
.testimonial .role { display: block; color: var(--ink-faint); font-size: .85rem; }

/* ---------- FAQ ---------- */
.faq-list { max-width: var(--container-narrow); margin-inline: auto; }
.faq-item { border-bottom: var(--border-width) solid var(--line); }
.faq-item summary {
  list-style: none; cursor: pointer; padding: 1.25rem .25rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  font-family: var(--heading-family); font-size: 1.15rem; font-weight: var(--heading-weight); color: var(--ink);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary .chev { flex: none; width: 1.5rem; height: 1.5rem; position: relative; transition: transform .25s ease; }
.faq-item summary .chev::before, .faq-item summary .chev::after { content: ""; position: absolute; top: 50%; left: 50%; width: 14px; height: 2px; background: var(--accent); border-radius: 2px; transform: translate(-50%, -50%); }
.faq-item summary .chev::after { transform: translate(-50%, -50%) rotate(90deg); }
.faq-item[open] summary .chev::after { opacity: 0; }
.faq-item[open] summary .chev { transform: rotate(180deg); }
.faq-answer { padding: 0 .25rem 1.4rem; color: var(--ink-soft); max-width: 70ch; }

/* ---------- Gallery ---------- */
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: clamp(.6rem, 1.2vw, 1rem); }
.gallery-grid figure { position: relative; border-radius: var(--radius-sm); overflow: hidden; aspect-ratio: 4 / 3; background: var(--paper-alt); }
.gallery-link { display: block; width: 100%; height: 100%; }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.gallery-grid figure:hover img { transform: scale(1.06); }
/* Always on. These used to fade in on hover, which meant a caption that only
   existed for people using a mouse — a touch screen has no hover state to
   enter, and there is nothing to hover with a keyboard. If the caption is
   worth writing it is worth showing. pointer-events keeps it out of the way of
   the link underneath it. */
.gallery-grid figcaption {
  position: absolute; inset: auto 0 0 0; padding: 1.75rem .9rem .8rem; color: #fff; font-size: .9rem;
  background: linear-gradient(transparent, rgba(11,16,26,.85)); pointer-events: none;
}

/* ---------- Lightbox ---------- */
/* Only ever built by site.js, and only where <dialog> exists. Without it the
   gallery links open the photo on its own, which is a worse experience but
   not a broken one. */
.lightbox {
  width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
  margin: 0; padding: 0; border: 0; overflow: hidden;
  background: rgba(8, 11, 18, .96); color: #fff;
}
.lightbox::backdrop { background: rgba(8, 11, 18, .85); }
/* A scroll-snap strip rather than hand-rolled touch handlers: swiping is then
   the browser's own gesture, with its own momentum and rubber-banding, and it
   costs nothing on a desktop where the arrows do the same job. */
.lightbox-strip {
  display: flex; height: 100%; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; scrollbar-width: none;
}
.lightbox-strip::-webkit-scrollbar { display: none; }
.lightbox-item {
  flex: 0 0 100%; scroll-snap-align: center;
  display: grid; place-items: center; padding: clamp(1rem, 4vw, 3.5rem);
}
.lightbox-item img { max-width: 100%; max-height: 78dvh; object-fit: contain; }
.lightbox button {
  position: absolute; z-index: 1; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, .12); color: #fff;
  cursor: pointer; line-height: 1; transition: background .15s ease;
}
.lightbox button:hover:not([disabled]) { background: rgba(255, 255, 255, .24); }
.lightbox button[disabled] { opacity: .25; cursor: default; }
.lightbox-close { top: 1rem; right: 1rem; width: 44px; height: 44px; font-size: 1.5rem; }
.lightbox-arrow { top: 50%; transform: translateY(-50%); width: 48px; height: 48px; font-size: 2rem; }
.lightbox-arrow--prev { left: 1rem; }
.lightbox-arrow--next { right: 1rem; }
.lightbox-caption {
  position: absolute; inset: auto 0 0 0; margin: 0; padding: 1rem clamp(1rem, 10vw, 6rem);
  text-align: center; font-size: .9rem; color: #eceef3;
  background: linear-gradient(transparent, rgba(8, 11, 18, .92));
}

/* ---------- CTA banner ---------- */
.cta-banner { position: relative; isolation: isolate; color: #fff; border-radius: var(--radius); overflow: hidden; text-align: center; padding: clamp(3rem, 6vw, 5.5rem) clamp(1.25rem, 4vw, 3rem); background: var(--surface-dark); }
.cta-banner .cta-bg { position: absolute; inset: 0; z-index: -2; background-size: cover; background-position: center; }
.cta-banner::after { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(rgba(11,16,26,.7), rgba(11,16,26,.78)); }
.cta-banner h2 { color: #fff; }
.cta-banner p { color: rgba(255,255,255,.88); max-width: 60ch; margin-inline: auto; margin-bottom: 2rem; }

/* ---------- Contact ---------- */
.contact-grid { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 4vw, 3.5rem); }
@media (min-width: 860px) { .contact-grid { grid-template-columns: 1fr 1.1fr; } }
.contact-info .lede { margin-bottom: 1.75rem; }
.contact-points { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
.contact-points li { display: flex; gap: .8rem; align-items: flex-start; color: var(--ink-soft); }
.contact-points .ci { color: var(--accent); flex: none; margin-top: .15rem; }
.contact-points a { color: var(--ink); text-decoration: none; }
.contact-points a:hover { color: var(--link-color); }

.contact-form { display: grid; gap: 1.1rem; background: var(--card-bg); border: var(--card-border); border-radius: var(--card-radius); padding: clamp(1.5rem, 3vw, 2.5rem); box-shadow: var(--card-shadow); }
.section--alt .contact-form,
.section--dark .contact-form,
.section--brand .contact-form { background: var(--paper); }
.field { display: grid; gap: .4rem; }
.field label { font-weight: 600; font-size: .92rem; color: var(--ink); }
.field input, .field textarea {
  font: inherit; color: var(--ink); padding: .8rem .9rem; border: var(--input-border); border-radius: var(--input-radius); background: var(--input-bg);
  transition: border-color .15s ease, box-shadow .15s ease; width: 100%;
}
/* No `outline: none` here. It used to sit alongside a 3px ring in
   --accent-soft, which is the accent mixed 14% into white — nowhere near the
   3:1 a focus indicator needs, and it beat the site-wide :focus-visible
   outline on specificity, so the form was the one place a keyboard user could
   not see where they were. The border shift is the affordance; the shared
   outline is the indicator. */
.field input:focus, .field textarea:focus { border-color: var(--accent); }
.form-status { font-weight: 600; }
.form-status.is-success { color: #097a4d; }
.form-status.is-error { color: #c0362c; }
.form-note { font-size: .8rem; color: var(--ink-faint); }
/* Shown by the URL fragment the CMS redirects back with, so a form posted
   without JavaScript still gets an answer on the page it was sent from. */
.form-flash { display: none; margin: 0; padding: .8rem 1rem; border-radius: var(--radius-sm); font-weight: 600; }
.form-flash:target { display: block; }
.form-flash--ok { background: color-mix(in srgb, #097a4d 12%, var(--paper)); color: #075c3a; }
.form-flash--bad { background: color-mix(in srgb, #c0362c 12%, var(--paper)); color: #93261e; }
.form-legend { font-size: .8rem; color: var(--ink-soft); margin: 0; }
.req { color: var(--accent); font-weight: 700; }

/* ---------- Footer ---------- */
.site-footer { background: var(--footer-bg); color: var(--footer-fg); padding-block: clamp(3rem, 6vw, 4.5rem) 1.5rem; }
.footer-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem; padding-bottom: 2.5rem; border-bottom: 1px solid var(--footer-rule); }
@media (min-width: 720px) { .footer-grid { grid-template-columns: 1.6fr 1fr 1.2fr; } }
@media (min-width: 1024px) { .footer-grid { grid-template-columns: 2fr 1fr 1fr 1.4fr; } }
.footer-brand .brand { color: var(--footer-heading-fg); margin-bottom: 1rem; }
.footer-brand .brand img { filter: var(--footer-logo-filter); }
.footer-brand p { color: var(--on-dark-faint); max-width: 38ch; }
/* An h2 sized like a label. The heading level says where these sit in the
   document — alongside the page's sections, not four levels down inside one —
   and the type here says how loudly to say it. The two are separate questions
   and the markup used to answer both with <h4>, which skipped a level on every
   page that had an h3 in it. */
.footer-heading {
  color: var(--footer-heading-fg); font-family: var(--eyebrow-family); font-size: .82rem;
  font-weight: 700; line-height: 1.3;
  letter-spacing: var(--eyebrow-tracking); text-transform: var(--eyebrow-transform); margin: 0 0 1.1rem;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .65rem; }
.footer-col a { color: var(--footer-fg); text-decoration: none; transition: color .15s ease; }
.footer-col a:hover { color: var(--accent); }
.footer-contact li { display: flex; gap: .65rem; align-items: flex-start; margin-bottom: .8rem; color: var(--footer-fg); }
.footer-contact .ci { color: var(--accent); flex: none; margin-top: .15rem; }
.socials { display: flex; gap: .6rem; margin-top: 1.4rem; }
.socials a { width: 40px; height: 40px; border-radius: var(--social-radius, 50%); display: inline-flex; align-items: center; justify-content: center; background: rgba(255,255,255,.08); color: #fff; transition: background-color .2s ease, transform .2s ease; }
.socials a:hover { background: var(--accent); color: var(--accent-ink); transform: translateY(-2px); }
.socials svg { width: 18px; height: 18px; }
.footer-bottom { display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between; align-items: center; padding-top: 1.5rem; font-size: .85rem; color: var(--on-dark-faint); }
.footer-bottom a { color: var(--on-dark-faint); text-decoration: none; }
.footer-bottom a:hover { color: var(--on-dark); }
/* Privacy, terms and the like live down here rather than in the navigation,
   where they would compete with the pages a client is trying to sell. */
.footer-legal { list-style: none; display: flex; flex-wrap: wrap; gap: .35rem 1.25rem; margin: 0; padding: 0; }
.footer-legal a { text-decoration: underline; text-underline-offset: 2px; }

/* One centred column, for a site with few enough pages that three columns
   would be mostly whitespace. */
.footer-center { display: grid; justify-items: center; text-align: center; gap: 1.25rem; padding-bottom: 2.5rem; border-bottom: 1px solid var(--footer-rule); }
.footer-center .brand { color: var(--footer-heading-fg); }
.footer-center .brand img { filter: var(--footer-logo-filter); }
.footer-center > p { max-width: 52ch; margin: 0; color: var(--on-dark-faint); }
.footer-center .socials { margin-top: 0; }
.footer-inline-nav, .footer-inline-contact {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem 1.5rem;
}
.footer-inline-nav a { color: var(--footer-heading-fg); text-decoration: none; font-weight: 600; }
.footer-inline-nav a:hover, .footer-inline-contact a:hover { color: var(--accent); }
.footer-inline-contact li { display: flex; align-items: center; gap: .5rem; }
.footer-inline-contact .ci { color: var(--accent); }
.footer-inline-contact a { color: var(--footer-fg); text-decoration: none; }

/* A map above the columns, for a business people drive to. */
.footer-map {
  margin-bottom: clamp(2rem, 4vw, 3rem);
  border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--footer-rule);
  /* Most map tiles are near-white and would glare out of a dark footer. */
  filter: var(--footer-map-filter, saturate(.85) brightness(.92));
}
.footer-map iframe { display: block; width: 100%; height: clamp(220px, 30vh, 320px); border: 0; }

/* ---------- Page heading ---------- */
/* Only rendered when no hero has supplied the h1. Given real space above and
   below because it is otherwise a title jammed against the header, and a
   bottom rule so it reads as the head of the page rather than as stray copy
   belonging to the section beneath it. */
.page-heading {
  padding-block: clamp(2.5rem, 1.5rem + 3vw, 4.5rem) clamp(1.5rem, 1rem + 2vw, 2.5rem);
  border-bottom: 1px solid var(--line);
}
.page-heading h1 { margin: 0; }

/* ---------- 404 ---------- */
/* Centred and given real height because it is the only page with nothing above
   or below it: left alone it collapses into a thin strip under the header, which
   reads as a broken page rather than an answered one. */
.error-page { text-align: center; display: grid; place-content: center; min-height: 52vh; }
.error-page .lede { margin-inline: auto; max-width: 46ch; }
.error-page .btn { margin-top: 1.6rem; }

/* ---------- Back to top ---------- */
.to-top {
  position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 90; width: 46px; height: 46px; border-radius: var(--to-top-radius, 50%);
  border: 0; background: var(--primary); color: #fff; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md); opacity: 0; transform: translateY(12px); pointer-events: none;
  /* pointer-events took it away from the mouse but left it in the tab order,
     so keyboard users hit an invisible button before reaching the footer.
     visibility removes it from focus too, delayed so the fade still plays on
     the way out and skipped entirely on the way in. */
  visibility: hidden;
  transition: opacity .25s ease, transform .25s ease, visibility 0s linear .25s;
}
.to-top.show { opacity: 1; transform: translateY(0); pointer-events: auto; visibility: visible; transition-delay: 0s; }

/* ---------- Scroll reveal (progressive enhancement) ---------- */
.js [data-reveal] { opacity: 0; transform: translateY(var(--reveal-shift)); transition: opacity var(--reveal-duration) ease, transform var(--reveal-duration) ease; }
.js [data-reveal].in-view { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1 !important; transform: none !important; transition: none; }
  .hero-slide { transition: none; }
  .btn, .card, .gallery-grid img, .to-top { transition: none !important; }
}
