/* ==========================================================================
   Solution One Inc. — main stylesheet
   Hand-written, no build step. Load order: tokens, reset, layout, components.
   Brand colors sampled from the existing logo (flame #d92e3f, teal #3e8f80).
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Brand. --red passes AA on white (4.76:1); --red-ink for small text on
     light backgrounds, --teal-ink likewise (--teal alone is large-text only). */
  --red: #d92e3f;
  --red-ink: #b8232f;
  --red-wash: #fdf2f3;
  --teal: #3e8f80;
  --teal-ink: #2f6c60;
  --teal-wash: #f1f7f5;

  --ink: #1a1d21;
  --ink-soft: #4a5158;
  /* #757d85 measured 4.18:1 on white — under the 4.5:1 WCAG 1.4.3 minimum for
     body-size text. Darkened to clear it on white (5.23) and on --surface-alt
     (4.91), since breadcrumbs sit on both. */
  --ink-faint: #666d76;
  --rule: #e3e6e9;
  /* Form field borders are "visual information required to identify a user
     interface component" under WCAG 1.4.11, so they need 3:1 against the page.
     The old #c8ced4 measured 1.59:1. This is 3.74:1. Card borders keep --rule:
     they are decoration, and the card's own link text carries the affordance. */
  --field-border: #7d858e;
  /* Focus ring. --red is the one brand colour that clears 3:1 against every
     background it can land on: white 4.76, --surface-alt 4.47, --teal-wash 4.39,
     --surface-dark 3.71. --teal-ink failed on dark (2.88), so focus rings were
     effectively invisible in the hero, CTA band and footer. */
  --focus: var(--red);
  --surface: #ffffff;
  --surface-alt: #f6f8f9;
  --surface-dark: #16191d;

  /* Fluid type. Min is the phone size, max the desktop size.
     Scaled up ~12% from the first pass: body copy is capped by --measure in ch,
     so at a 17px base the text column could never exceed ~700px no matter how
     wide the container got — which is what read as a narrow site. Bigger type
     means the same character count occupies more of the screen. */
  --step--1: clamp(0.9375rem, 0.9rem + 0.15vw, 1rem);
  --step-0: clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem);
  --step-1: clamp(1.1875rem, 1.1rem + 0.4vw, 1.375rem);
  --step-2: clamp(1.4375rem, 1.28rem + 0.7vw, 1.75rem);
  --step-3: clamp(1.75rem, 1.5rem + 1.1vw, 2.375rem);
  --step-4: clamp(2.125rem, 1.7rem + 2vw, 3.5rem);

  --sans: "Open Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --display: "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;

  /* Roomier than it was. At 1280 the old gutter resolved to 35px, which put the
     card grids and the trust strip almost against the window edge while the text
     blocks sat 200px in — the combination is what read as cramped. */
  --gutter: clamp(1.25rem, 1rem + 2.5vw, 4.5rem);
  /* One measure for every text block. Prose was 80ch and section heads 72ch, so
     paragraphs and the headings above them started at different left edges — 198px
     against 241px on a 1280 screen. Four different content edges down one page is
     what reads as "shifted": nothing shares an alignment line. */
  --measure: 72ch;
  --wrap: 1440px;

  --radius: 6px;
  --radius-lg: 12px;
  --shadow: 0 1px 2px rgb(22 25 29 / 0.06), 0 4px 12px rgb(22 25 29 / 0.06);
  --shadow-lift: 0 2px 4px rgb(22 25 29 / 0.08), 0 12px 28px rgb(22 25 29 / 0.12);

  --header-h: 4.5rem;
  /* The sticky bar's wordmark. Shared with the spacer that balances it, so the
     nav loses twice this from its row — kept modest for that reason. */
  --brand-w: clamp(130px, 13vw, 180px);
}

/* Self-hosted variable fonts, latin subset. Same two families the old site
   loaded from Google (Montserrat for headings, Open Sans for body), so the
   typography carries over — but served from our own origin, which drops the
   third-party connection and the privacy question that comes with it.
   `format("woff2")` is deliberate: the older `woff2-variations` keyword is not
   recognised everywhere and silently kills the whole @font-face rule. */
@font-face {
  font-family: "Open Sans";
  src: url("/assets/fonts/open-sans-variable.woff2") format("woff2");
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("/assets/fonts/montserrat-variable.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font: var(--step-0) / 1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
  /* Reflow insurance. At 320px with text zoomed to 200%, "info@solution1inc.com"
     and long heading words are individually wider than the viewport, which
     forces two-dimensional scrolling.
     `anywhere` rather than `break-word` on purpose: only `anywhere` reduces an
     element's min-content width, and min-content is what stops a flex or grid
     item from shrinking. `break-word` breaks the glyphs visually but leaves the
     intrinsic size unchanged, so the overflow survives. Neither has any visible
     effect until a word genuinely cannot fit. */
  overflow-wrap: anywhere;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 0.5em;
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: var(--step-4);
}
h2 {
  font-size: var(--step-3);
}
h3 {
  font-size: var(--step-2);
}
h4 {
  font-size: var(--step-1);
}

p,
ul,
ol,
dl {
  margin: 0 0 1.15em;
}

p {
  text-wrap: pretty;
}

img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

/* Images are not in the repo yet — this keeps a missing file from collapsing
   the layout or flashing a broken-image glyph mid-build. */
img {
  background: var(--surface-alt);
}

a {
  color: var(--red-ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

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

:focus-visible {
  outline: 3px solid var(--focus);
  /* The offset matters: it lifts the ring off the control and onto the page
     background, which is what the 3:1 figures above are measured against. On a
     button the ring would otherwise sit on the button's own fill. */
  outline-offset: 3px;
  border-radius: 2px;
}

/* Windows High Contrast / forced-colours mode: let the OS draw the ring. */
@media (forced-colors: active) {
  :focus-visible {
    outline: 3px solid CanvasText;
  }
}

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

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
}

.section--alt {
  background: var(--surface-alt);
  border-block: 1px solid var(--rule);
}

.section--teal {
  background: var(--teal-wash);
  border-block: 1px solid #dceae5;
}

.prose {
  max-width: var(--measure);
}

.prose > :last-child {
  margin-bottom: 0;
}

/* A lone prose column in a 1320px container leaves a dead gutter down the whole
   right side. Centre it so the section reads as deliberate rather than as
   content that failed to fill the page. */
.prose--center {
  margin-inline: auto;
}

/* Centred section intro: eyebrow, heading, one paragraph. Narrower than the
   grid below it, which is what makes the grid look intentional. */
.section__head {
  /* var(--measure), not its own 72ch: they resolved 8px apart because ch depends
     on the element's own font, and the point is that headings and the prose under
     them start on the same line. */
  max-width: var(--measure);
  margin-inline: auto;
  margin-bottom: clamp(1.75rem, 1rem + 2vw, 2.75rem);
  text-align: center;
}

.section__head > :last-child {
  margin-bottom: 0;
}

.section__head .lede {
  margin-top: 0.75rem;
}

.lede {
  font-size: var(--step-1);
  color: var(--ink-soft);
}

.eyebrow {
  margin: 0 0 0.75rem;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--teal-ink);
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  z-index: 100;
  padding: 0.6rem 1rem;
  background: var(--surface-dark);
  color: #fff;
  border-radius: var(--radius);
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 0.5rem;
  color: #fff;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   4. Header — logo left, contact right, nav below. Same arrangement as the
      current site; sticky and slimmer.
   -------------------------------------------------------------------------- */

.topbar {
  background: var(--surface-dark);
  color: #e8ebee;
  font-size: var(--step--1);
}

.topbar__inner {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  align-items: center;
  justify-content: center;
  padding-block: 0.55rem;
  text-align: center;
}

.topbar strong {
  color: #fff;
  font-family: var(--display);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.topbar a {
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}

.topbar__tel {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.topbar__tel svg {
  width: 1em;
  height: 1em;
}

.topbar a:hover {
  color: #fff;
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   Brand band — the full lockup, above the sticky bar and outside it.

   It is not sticky, and nothing in the header resizes on scroll. That is the
   whole design: a shrinking sticky header shortens the document, the browser's
   scroll anchoring compensates by moving scrollY, and that moves the page back
   across the threshold which triggered the shrink. Measured at 108px of travel
   per toggle, which is why the collapsing version flickered. Letting this band
   scroll away removes the loop rather than damping it.
   -------------------------------------------------------------------------- */

.brandband {
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
}

.brandband .wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Generous, and asymmetric on purpose: the lockup's own artwork carries more
     clear space under the strapline than above the roundel. */
  padding-block: clamp(1.75rem, 1.2rem + 2vw, 2.75rem) clamp(1.25rem, 1rem + 1.4vw, 2rem);
  gap: clamp(1.25rem, 1rem + 1.2vw, 2rem);
}

.brandband__mark {
  display: block;
  flex-shrink: 0;
}

.brandband__mark img {
  width: clamp(240px, 34vw, 400px);
  height: auto;
  background: none;
}

/* The six services. Real text, so the labels stay sharp and reflow; the glyphs
   are the artwork's own, cut to a common 96px square so they align on centre. */
.services {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(1rem, 0.5rem + 1.6vw, 2.25rem);
  width: 100%;
  max-width: 68rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Basis clears the longest single word — "Extinguishers", 8.56rem at this size.
   At 8.5rem it was a pixel short, and the body's `overflow-wrap: anywhere`
   reflow insurance duly split it as "EXTINGUISHER / S". Sized around that rule
   rather than overriding it: it is what stops 320px at 200% zoom needing
   two-dimensional scrolling, so it earns its keep. Growing lets six sit on one
   row at full width and share the space evenly when they wrap. */
.services li {
  display: flex;
  flex: 1 1 8.75rem;
  max-width: 12rem;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  text-align: center;
}

.services img {
  width: clamp(1.75rem, 1.4rem + 1.1vw, 2.5rem);
  height: auto;
}

.services span {
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink);
  text-wrap: balance;
}

.strapline {
  margin: 0;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  text-wrap: balance;
  color: var(--ink-soft);
}

.strapline b {
  font-weight: 700;
  color: var(--red-ink);
}

/* On a phone the full band came to 528px, and with the topbar and the sticky bar
   that was 772px of an 812px viewport — 95% header before a word of content. All
   six services stay; the icon moves alongside its label so each is one row
   instead of three, which is most of the height back. Two columns rather than
   three because "Extinguishers" alone needs 8.5rem and will not sit in a third
   of a 375px screen without the body's reflow rule splitting it. */
@media (max-width: 40rem) {
  .brandband .wrap {
    padding-block: 1.25rem 1rem;
    gap: 1rem;
  }

  .brandband__mark img {
    width: min(210px, 62vw);
  }

  .services {
    gap: 0.5rem 0.75rem;
  }

  .services li {
    flex-direction: row;
    align-items: center;
    gap: 0.45rem;
    text-align: left;
    max-width: none;
  }

  .services img {
    width: 1.15rem;
    flex-shrink: 0;
  }

  .services span {
    font-size: 0.72rem;
    letter-spacing: 0.02em;
  }

  .strapline {
    font-size: 0.72rem;
    letter-spacing: 0.05em;
  }
}

/* --------------------------------------------------------------------------
   Sticky bar — one fixed size, always. Nothing here changes on scroll.
   -------------------------------------------------------------------------- */

.masthead {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgb(255 255 255 / 0.94);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--rule);
}

/* Mark and nav on one line. The mark sharing the row is what makes reserving its
   space free: the row is as tall as the nav either way, so showing or hiding the
   mark changes no geometry at all. */
.masthead__row {
  display: flex;
  align-items: center;
  gap: 0.5rem 1.5rem;
}

/* Balances the mark, so the nav is centred on the page rather than in the space
   left over beside it. Without this the nav's box starts after the mark's reserved
   width and its centred contents land about half that width to the right — which
   is visibly lopsided whether or not the mark is showing. An empty flex item
   rather than margins, so it stays correct as the mark's width flexes. */
.masthead__row::after {
  content: "";
  flex: 0 0 var(--brand-w);
}

.masthead__row .nav {
  flex: 1;
  min-width: 0;
  border-top: 0;
}

/* Hidden while the lockup above is in view, so only one mark is ever on screen,
   and faded in once it scrolls away.

   visibility, not display: the space stays reserved either way, so this bar has
   exactly one height for its whole life. That is the point — a sticky header that
   resizes shortens the document, scroll anchoring moves scrollY to compensate, and
   that moves the page back across whatever triggered the resize. Toggling
   visibility cannot start that loop because nothing reflows.

   Default hidden rather than visible so that with JS off the big lockup is still
   the only mark, which is what was asked for. The cost is no wordmark in the bar
   once scrolled, which is the better failure of the two. */
.brand {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.25s ease;
}

.masthead[data-scrolled] .brand {
  visibility: visible;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .brand { transition: none; }
}

/* The type-only mark is 8.2:1 against the old logo's 6.2:1, so matching the old
   280px would have shrunk the wordmark and left FIRE & LIFE SAFETY unreadable. */
/* Smaller than it was when it had a row to itself — it now sits beside the nav,
   so it has to leave room for eight menu items. The width is a variable because
   the spacer that balances it has to match exactly. */
.brand img {
  width: var(--brand-w);
  background: none;
}

/* Also in the topbar, which is deliberate rather than duplication: the topbar
   scrolls away with the brand band, and this is the copy that stays reachable
   once the bar is pinned. */
.contactblock {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.25rem;
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.4;
}

.contactblock dt {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.contactblock dd {
  margin: 0;
}

.tel {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--red-ink);
  text-decoration: none;
}

.tel:hover {
  color: var(--red);
  text-decoration: underline;
}

.tel svg {
  width: 1em;
  height: 1em;
  flex: none;
}

/* Nav ------------------------------------------------------------------- */

.nav {
  border-top: 1px solid var(--rule);
}

.nav__toggle {
  display: none;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.8rem 0;
  border: 0;
  background: none;
  font: 700 var(--step-0) / 1 var(--display);
  color: var(--ink);
  cursor: pointer;
}

.nav__toggle svg {
  width: 1.25rem;
  height: 1.25rem;
}

.nav__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__list a {
  display: block;
  /* 0.9rem each side wanted 871px for seven items, which does not fit beside a
     legible mark and its balancing spacer. The click target keeps its full height
     and the row still reads as separated links. */
  padding: 0.85rem 0.55rem;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 3px solid transparent;
}

.nav__list a:hover {
  color: var(--red-ink);
  background: var(--red-wash);
}

.nav__list a[aria-current="page"] {
  color: var(--red-ink);
  border-bottom-color: var(--red);
}

/* Hamburger below 78rem. Was 64rem, chosen when the nav had the row to itself.
   It now shares the row with the mark and the spacer that balances it, so it needs
   roughly 2x the mark's width more than it did — measured, one line stops fitting
   below about 1245px. Showing the hamburger from there down beats a nav that wraps
   onto two lines. The seven items need ~769px of content width at
   the current type size; the previous 56rem (896px) breakpoint left a dead band
   where the nav was still horizontal but too wide to fit, so "Contact" wrapped
   onto a second line on its own. 64rem clears it with room to spare. */
/* Nav dropdown -----------------------------------------------------------
   Disclosure pattern, per the ARIA Authoring Practices "Disclosure Navigation
   Menu". Deliberately NOT role="menu"/menuitem — those are for application
   menus and change how screen readers announce the contents. These are plain
   links, so they stay links.

   "Service Areas" is a real link to the hub AND has a separate toggle button
   beside it. One control doing both would mean either the hub is unreachable or
   the dropdown cannot be opened from the keyboard without navigating away. */

.nav__has-sub {
  position: relative;
  /* No gap between the label row and the panel: any dead space here would drop
     :hover on the way down to the first link. */
  padding-bottom: 0;
  /* Row, not the default block flow: `.nav__list a` is display:block, so the
     link would otherwise fill the <li> and push the chevron onto its own line.
     The mobile query below overrides this with a two-column grid. */
  display: flex;
  align-items: center;
}

.nav__sub-toggle {
  display: inline-grid;
  place-items: center;
  /* 24x24 minimum hit area — WCAG 2.2 SC 2.5.8 Target Size (Minimum). */
  width: 2.25rem;
  height: 2.25rem;
  margin-inline-start: -0.5rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.nav__sub-toggle:hover {
  color: var(--red-ink);
}

.nav__chev {
  width: 0.85rem;
  height: 0.85rem;
  transition: rotate 0.15s ease;
}

.nav__has-sub[data-open="true"] .nav__chev {
  rotate: 180deg;
}

.nav__sub {
  display: none;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__has-sub[data-open="true"] .nav__sub {
  display: block;
}

@media (min-width: 64.0625rem) {
  /* Pure-CSS hover/focus reveal, but ONLY when the script is absent. The script
     adds data-js to .nav, which switches this off and hands control to
     [data-open], set by the same script on mouseenter and focusin.

     The split matters for WCAG 1.4.13 "Dismissible": Escape returns focus to the
     toggle, which is *inside* .nav__has-sub, so an ungated :focus-within would
     keep the panel open and Escape could never dismiss it. Gating on data-js
     means Escape wins whenever there is a script to honour it, while a
     JS-disabled browser still gets a working hover menu. */
  .nav:not([data-js]) .nav__has-sub:hover > .nav__sub,
  .nav:not([data-js]) .nav__has-sub:focus-within > .nav__sub {
    display: block;
  }

  .nav:not([data-js]) .nav__has-sub:hover .nav__chev,
  .nav:not([data-js]) .nav__has-sub:focus-within .nav__chev {
    rotate: 180deg;
  }

  .nav__sub {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 60;
    min-width: 15rem;
    padding: 0.4rem;
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
  }

  .nav__sub a {
    padding: 0.6rem 0.75rem;
    border-bottom: 0;
    border-radius: 4px;
    text-transform: none;
    letter-spacing: 0;
    font-size: var(--step-0);
    white-space: nowrap;
  }

  .nav__sub a[aria-current="page"] {
    background: var(--red-wash);
    border-bottom: 0;
  }
}

/* The parent link stays highlighted while you are on one of its children. */
.nav__list a[data-section-current] {
  color: var(--red-ink);
}

@media (max-width: 78rem) {
  /* The nav becomes a disclosure below this width, so it takes its own line —
     otherwise the expanded list would stretch the row and centre the mark and the
     phone against the whole open menu. */
  .masthead__row {
    flex-wrap: wrap;
  }
  .masthead__row .nav {
    flex: 1 0 100%;
    order: 2;
    border-top: 1px solid var(--rule);
  }
  /* The nav has its own line here, so there is nothing to balance. */
  .masthead__row::after {
    display: none;
  }
  .nav__toggle {
    display: flex;
    justify-content: space-between;
  }
  .nav__list {
    display: none;
    padding-bottom: 0.75rem;
  }
  .nav[data-open="true"] .nav__list {
    display: block;
  }
  .nav__list a {
    padding: 0.8rem 0.25rem;
    border-bottom: 1px solid var(--rule);
  }
  .nav__list a[aria-current="page"] {
    border-bottom-color: var(--red);
  }

  /* Inline expansion on small screens rather than an overlay panel. */
  .nav__has-sub {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
  }

  .nav__has-sub > a {
    grid-column: 1;
  }

  .nav__sub-toggle {
    grid-column: 2;
    margin-inline-start: 0;
    border-bottom: 1px solid var(--rule);
    height: auto;
    align-self: stretch;
  }

  .nav__sub {
    grid-column: 1 / -1;
  }

  .nav__sub a {
    padding-inline-start: 1.25rem;
    text-transform: none;
    letter-spacing: 0;
  }
}

/* --------------------------------------------------------------------------
   5. Hero — full-bleed image, headline, CTA. Overlay is what makes the
      headline legible; the current site sets dark text straight on a photo.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  display: grid;
  align-items: center;
  min-height: clamp(20rem, 46vh, 30rem);
  isolation: isolate;
  background: var(--surface-dark);
  color: #fff;
  overflow: hidden;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

/* <picture> is the img's containing block, so it has to be stretched too —
   otherwise height:100% on the img resolves against auto and the photo sits at
   its intrinsic ratio with a gap below. */
.hero__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: none;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    100deg,
    rgb(12 14 17 / 0.86) 0%,
    rgb(12 14 17 / 0.66) 48%,
    rgb(12 14 17 / 0.28) 100%
  );
}

.hero__inner {
  padding-block: clamp(2.5rem, 1.5rem + 5vw, 5rem);
  max-width: 56rem;
  min-width: 0;
}

.hero h1 {
  color: #fff;
}

/* Was 40rem against an h1 free to fill 56rem, which left the hero visibly
   heavier on one side. Sharing the container's measure squares it up. */
.hero p {
  max-width: 100%;
  font-size: var(--step-1);
  color: #e2e6ea;
}

.hero .eyebrow {
  color: #7fd4c2;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.75rem;
}

.hero--compact {
  min-height: clamp(14rem, 30vh, 20rem);
}

/* --------------------------------------------------------------------------
   6. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 1.5rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  font: 700 var(--step-0) / 1.2 var(--display);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease,
    box-shadow 0.15s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn--primary {
  background: var(--red-ink);
  color: #fff;
  box-shadow: var(--shadow);
}

.btn--primary:hover {
  background: #9d1c27;
  color: #fff;
  box-shadow: var(--shadow-lift);
}

.btn--ghost {
  border-color: rgb(255 255 255 / 0.55);
  color: #fff;
}

.btn--ghost:hover {
  background: rgb(255 255 255 / 0.12);
  border-color: #fff;
  color: #fff;
}

.btn--outline {
  border-color: var(--rule);
  background: var(--surface);
  color: var(--ink);
}

.btn--outline:hover {
  border-color: var(--red);
  color: var(--red-ink);
}

/* --------------------------------------------------------------------------
   7. Cards / grids
   -------------------------------------------------------------------------- */

.grid {
  display: grid;
  gap: clamp(1rem, 0.6rem + 1.4vw, 1.75rem);
}

.grid--2 {
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}

/* Auto-fit on narrow screens, but pinned to three columns once there is room.
   Left to auto-fit, a 1320px container fits four cards, so the five-card sets
   (services, cities, building types) break as 4 + 1 with a single orphan on the
   second row. Three columns gives 3 + 2, which reads as intentional. */
.grid--3 {
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}

/* Same 64rem threshold as the nav, so the whole layout switches to its desktop
   arrangement at one width rather than two. */
@media (min-width: 64rem) {
  .grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

.grid--4 {
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}

.card {
  display: flex;
  flex-direction: column;
  padding: clamp(1.25rem, 1rem + 0.8vw, 1.75rem);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  transition: border-color 0.15s ease, box-shadow 0.15s ease,
    transform 0.15s ease;
}

a.card,
.card a.card__title {
  text-decoration: none;
}

a.card:hover,
.card:has(a:hover) {
  border-color: var(--red);
  box-shadow: var(--shadow-lift);
  transform: translateY(-2px);
}

.card__icon {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin-bottom: 1rem;
  border-radius: var(--radius);
  background: var(--red-wash);
  color: var(--red-ink);
}

.card__icon svg {
  width: 1.6rem;
  height: 1.6rem;
}

.card__title {
  margin-bottom: 0.4rem;
  font-size: var(--step-1);
  color: var(--ink);
}

.card p {
  margin-bottom: 0;
  color: var(--ink-soft);
  font-size: var(--step--1);
}

.card__more {
  margin-top: auto;
  padding-top: 1rem;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--red-ink);
}

/* Media card — image over text, used for city and industry pages. */
.mediacard img {
  aspect-ratio: 3 / 2;
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.mediacard {
  padding: 0;
  overflow: hidden;
}

.mediacard__body {
  padding: clamp(1.1rem, 0.9rem + 0.6vw, 1.5rem);
}

/* Plain link list — city names, no descriptions. Replaces the card grid that
   used to carry a line of subtext under each location. */
.linklist ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.linklist a {
  display: block;
  padding: 0.7rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-family: var(--display);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

.linklist a:hover {
  border-color: var(--red);
  color: var(--red-ink);
}

.linklist a[aria-current="page"] {
  border-color: var(--red);
  background: var(--red-wash);
  color: var(--red-ink);
}

/* --------------------------------------------------------------------------
   8. Trust strip / checklists / FAQ
   -------------------------------------------------------------------------- */

.trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem clamp(1rem, 3vw, 3rem);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-soft);
}

.trust li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.trust svg {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--teal-ink);
  flex: none;
}

.checklist {
  padding: 0;
  list-style: none;
}

.checklist li {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: 0.75rem;
  margin-bottom: 0.7rem;
}

.checklist svg {
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.28em;
  color: var(--teal-ink);
}

.faq {
  max-width: var(--measure);
  margin-inline: auto;
}

.faq details {
  border-bottom: 1px solid var(--rule);
}

.faq summary {
  padding: 1.1rem 2rem 1.1rem 0;
  position: relative;
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

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

.faq summary::after {
  content: "+";
  position: absolute;
  right: 0.25rem;
  top: 50%;
  translate: 0 -50%;
  font-size: 1.5em;
  font-weight: 400;
  color: var(--red-ink);
  line-height: 1;
}

.faq details[open] summary::after {
  content: "\2212";
}

.faq details > :not(summary) {
  margin-inline-end: 2rem;
}

.faq details[open] summary {
  color: var(--red-ink);
}

/* --------------------------------------------------------------------------
   9. Split panels (copy beside a figure)
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  gap: clamp(1.5rem, 1rem + 3vw, 3.5rem);
  align-items: center;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}

.split figure {
  margin: 0;
}

.split img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

/* --------------------------------------------------------------------------
   10. Call-to-action band
   -------------------------------------------------------------------------- */

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

.cta h2 {
  color: #fff;
}

.cta p {
  max-width: 44rem;
  margin-inline: auto;
  color: #cfd5db;
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  margin-top: 1.75rem;
}

/* --------------------------------------------------------------------------
   11. Contact form
   -------------------------------------------------------------------------- */

.form {
  display: grid;
  gap: 1rem;
}

.field {
  display: grid;
  gap: 0.35rem;
  min-width: 0;
}

.field label {
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
}

.field .req {
  color: var(--red-ink);
}

.field input,
.field textarea {
  /* A text input's intrinsic width comes from the `size` attribute, default 20
     characters — about 435px once text is zoomed to 200%. That min-content width
     propagates up through the grid and forces horizontal scrolling. width:100%
     plus min-width:0 lets the field track its column instead. */
  width: 100%;
  min-width: 0;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  background: var(--surface);
  font: inherit;
  color: inherit;
}

.field input:focus,
.field textarea:focus {
  border-color: var(--teal-ink);
  outline: 2px solid var(--teal-ink);
  outline-offset: 0;
}

.field textarea {
  min-height: 8rem;
  resize: vertical;
}

.form__row {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.footer {
  padding-block: clamp(2.5rem, 1.5rem + 3vw, 3.5rem) 1.5rem;
  background: var(--surface-dark);
  color: #b9c0c7;
  font-size: var(--step--1);
}

.footer h2 {
  margin-bottom: 0.9rem;
  font-size: var(--step-0);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
}

.footer a {
  color: #dfe4e8;
  text-decoration: none;
}

.footer a:hover {
  color: #fff;
  text-decoration: underline;
}

.footer__cols {
  display: grid;
  gap: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

.footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer li + li {
  margin-top: 0.45rem;
}

/* Prose following a link list needs its own separation, or it reads as a
   final list item. */
.footer ul + p {
  margin-top: 1.1rem;
}

.footer address {
  font-style: normal;
  line-height: 1.7;
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  margin-top: clamp(1.75rem, 1rem + 2vw, 2.5rem);
  padding-top: 1.25rem;
  border-top: 1px solid rgb(255 255 255 / 0.14);
  color: #8d959d;
}

.footer__licence {
  font-family: var(--display);
  font-weight: 600;
  color: #fff;
}

/* --------------------------------------------------------------------------
   13. Breadcrumbs
   -------------------------------------------------------------------------- */

.crumbs {
  padding-block: 0.9rem;
  border-bottom: 1px solid var(--rule);
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.crumbs li + li::before {
  content: "/";
  margin-right: 0.4rem;
  color: var(--rule);
}

.crumbs a {
  color: var(--ink-soft);
  text-decoration: none;
}

.crumbs a:hover {
  color: var(--red-ink);
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Editor-authored layout
   ---------------------------------------------------------------------------
   Everything below exists so the page editor can offer real layout choices
   without inventing new visual primitives. Each rule reuses the gap clamps, the
   64rem breakpoint and the colour variables already declared above, so a row
   built in the editor sits in the same rhythm as one written by hand and the
   contrast record in docs/accessibility.md still holds — no new colour pairing
   is introduced anywhere in this block.

   Column widths are a fixed set of tokens rather than arbitrary values. That is
   deliberate: it is what stops a page ending up with a 7%-wide column, and it
   means every arrangement has been looked at once rather than never.
   --------------------------------------------------------------------------- */

.cols {
  display: grid;
  gap: clamp(1.5rem, 1rem + 3vw, 3.5rem);
  align-items: start;
}

/* min-width: 0 lets a grid child actually shrink; without it long words and
   pre-formatted content force the column wider than its track. */
.cols > .col {
  min-width: 0;
}

.cols > .col > :last-child {
  margin-bottom: 0;
}

/* Single column below the desktop threshold — the same place .grid--3 and the
   nav switch, so the whole layout changes at one width rather than several. */
@media (min-width: 64rem) {
  .cols--1-1     { grid-template-columns: 1fr 1fr; }
  .cols--2-1     { grid-template-columns: 2fr 1fr; }
  .cols--1-2     { grid-template-columns: 1fr 2fr; }
  .cols--1-1-1   { grid-template-columns: repeat(3, 1fr); }
  .cols--1-1-1-1 { grid-template-columns: repeat(4, 1fr); }
}

/* Section spacing. Same shape as .section's own clamp, scaled. `flush` is for a
   band that has to sit directly against its neighbour. */
.section--tight { padding-block: clamp(1.25rem, 0.9rem + 1.4vw, 2rem); }
.section--loose { padding-block: clamp(4rem, 2.5rem + 6vw, 7rem); }
.section--flush { padding-block: 0; }

/* A rule, not a decoration: --rule is the same border colour the section
   variants use, so a divider reads as part of the same system. */
.divider {
  border: 0;
  border-top: 1px solid var(--rule);
  margin-block: clamp(1.5rem, 1rem + 2vw, 3rem);
}

/* Deliberate vertical space. Presentational and empty, so it is hidden from
   assistive technology rather than announced as a blank element. */
.spacer { display: block; }
.spacer--sm { height: clamp(1rem, 0.8rem + 0.8vw, 1.5rem); }
.spacer--md { height: clamp(2rem, 1.5rem + 2vw, 3.5rem); }
.spacer--lg { height: clamp(3.5rem, 2.5rem + 4vw, 6rem); }

/* Standalone heading and button blocks. The type scale comes from the h2/h3/h4
   rules above; these only handle the block-level spacing an isolated one needs. */
.blk-heading { margin-bottom: 0; }
.blk-button { margin-block: clamp(1rem, 0.8rem + 1vw, 1.75rem); }
.blk-button--center { text-align: center; }
.blk-button--right { text-align: right; }

/* Text alignment, offered only on short blocks (headings, buttons). Body copy is
   deliberately not alignable: centred or right-aligned paragraphs are harder to
   read, and the ragged left edge is a real accessibility cost, not a taste. */
.u-text-center { text-align: center; }
.u-text-right  { text-align: right; }

/* A picture placed on its own, rather than beside text. */
.blk-picture { margin: 0; }
.blk-picture img { display: block; width: 100%; height: auto; border-radius: var(--radius, 4px); }
