@charset "utf-8";
/* ===========================================================================
   Great West Fire & Safety Ltd.
   Hand-written stylesheet. Mobile-first: the phone (390px) is the base layout,
   the tablet (min-width: 768px) and desktop (min-width: 1024px) add to it.
   =========================================================================== */

/* --- Fonts -------------------------------------------------------------- */
@font-face {
  font-family: 'Saira Condensed';
  src: url('fonts/saira-condensed-700-latin.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('fonts/ibm-plex-sans-var-latin.woff2') format('woff2-variations');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens ------------------------------------------------------------- */
:root {
  /* Brand roles fixed by the client */
  --primary: #007cba;
  --accent: #2ea3f2;
  --page: #ffffff;
  --body: #666666;
  --heading: #666666;
  --cta: #0085ba;
  --rule: #e7e7e7;
  --dark: #1f1f1f;

  /* Derived from the pinned roles */
  --surface: #f4f8fb;
  --surface-deep: #e8f0f6;
  --dark-rule: #3a3a3a;
  --dark-ink: #ffffff;
  --dark-ink-soft: #c9d3da;
  --primary-deep: #006496;
  /* Flame red lifted from the logo mark — the emergency line's colour. */
  --fire: #d32c25;
  --fire-deep: #a8221c;
  --fire-light: #f0705c;

  --display: 'Saira Condensed', 'Arial Narrow', Helvetica, sans-serif;
  --text: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  /* Overshoot curve: the "slight bounce" shared by the hamburger's bars as
     they swing into the X and by every element that pops up on load. */
  --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
  /* Crisp slide: fast off the mark, then a sharp settle with no overshoot.
     Shared by the page's arrival, the outgoing page and the scroll reveal so
     the whole site moves with one gesture. --slide is how far it travels. */
  --ease-crisp: cubic-bezier(0.16, 1, 0.3, 1);
  --slide: 32px;
  --t-fast: 150ms;
  --t: 220ms;
  --t-slow: 340ms;

  --gutter: 20px;
  --band-y: 56px;
  --measure: 66ch;
  --shadow-1: 0 2px 6px rgba(31, 31, 31, 0.08), 0 10px 24px rgba(31, 31, 31, 0.07);
  --shadow-2: 0 4px 10px rgba(0, 100, 150, 0.14), 0 16px 34px rgba(31, 31, 31, 0.12);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Literal black, not the primary token: the editor's palette can set that
     token to a near-white, which would leave the caret invisible in a field. */
  caret-color: #000;
  scrollbar-color: #b9c6ce var(--surface);
}

body {
  margin: 0;
  background: var(--page);
  color: var(--body);
  font-family: var(--text);
  font-size: 17px;
  line-height: 1.55;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

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

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  color: var(--heading);
  letter-spacing: 0.005em;
  line-height: 1.1;
  margin: 0;
}

h1 { font-size: clamp(30px, 8.4vw, 36px); }
h2 { font-size: clamp(22px, 6.4vw, 26px); }
h3 { font-size: 20px; }
h4 { font-size: 18px; }

p { margin: 0; }
ul, ol { margin: 0; padding: 0; }

/* Links in copy are black with an underline that thickens on hover — the same
   black the bar's menu words use. Set as a literal because the editor's palette
   holds --primary at a near-white, which made every inline link vanish on the
   page. Components on dark surfaces (footer, hero, crumbs) set their own. */
a { color: #000; text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: #000; text-decoration-thickness: 2px; }

/* Text selection. Literal hex on purpose: Chrome does not resolve var()
   inside ::selection, and an unresolved value paints the highlight white —
   invisible on the page, a blank block on dark bands. Primary blue with
   white text reads on every background the site uses (white, pale blue,
   the dark band, the hero photograph). */
::selection { background: #007cba; color: #ffffff; -webkit-text-fill-color: #ffffff; }
::-moz-selection { background: #007cba; color: #ffffff; }
img::selection, svg::selection, canvas::selection, video::selection { background: rgba(0, 124, 186, 0.35); }

/* Keyboard focus in the flame red: it reads on the white page, the pale bands,
   the black footer and the hero photographs alike, where the palette's primary
   (a near-white under the editor's overrides) disappears on the light ones. */
:focus-visible {
  outline: 3px solid var(--fire);
  outline-offset: 3px;
  border-radius: 2px;
}

::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--surface); }
::-webkit-scrollbar-thumb { background: #b9c6ce; border: 3px solid var(--surface); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: var(--primary); }

.skip {
  position: absolute;
  left: 12px;
  top: -200%;
  z-index: 90;
  background: #1f1f1f;
  color: #fff;
  font-family: var(--display);
  font-size: 18px;
  padding: 14px 20px;
  text-decoration: none;
}
.skip:focus { top: 12px; color: #fff; }

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

/* --- Condensed copy ------------------------------------------------------
   A passage that has to fit a phone screen carries both versions: .u-full is
   the desktop wording, .u-condensed the same message in fewer words. The
   phone block swaps them under 768px; desktop and tablet never see the short
   one. Used by the inspection items and by the home trio cards. */
.u-condensed { display: none; }

/* --- Layout primitives -------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: 1208px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.wrap--narrow { max-width: 760px; }

.band { padding: var(--band-y) 0; position: relative; }
.band--tint { background: var(--surface); }
.band--dark { background: var(--dark); color: var(--dark-ink-soft); }
.band--dark h1, .band--dark h2, .band--dark h3, .band--dark h4 { color: var(--dark-ink); }
.band--dark a { color: var(--accent); }
.band--dark a:hover { color: #fff; }
.band--edge { border-top: 1px solid var(--rule); }

.stack { display: flex; flex-direction: column; gap: 24px; }
.stack--tight { gap: 12px; }

.lede {
  font-size: 17px;
  line-height: 1.5;
  max-width: 34ch;
}

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 16px; }

/* Rule that sits above a heading, the width of the heading's first stroke */
.ruled { padding-top: 18px; position: relative; }
.ruled::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 44px; height: 3px;
  /* Heading grey rather than the primary token, which the editor's palette
     can hold at a near-white — the rule has to show on a white band. */
  background: var(--heading);
}
.band--dark .ruled::before { background: var(--accent); }

/* --- Buttons & links ---------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 12px 26px;
  background: var(--cta);
  color: #fff;
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-decoration: none;
  border: 2px solid var(--cta);
  border-radius: 2px;
  cursor: pointer;
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease),
              transform var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.btn:hover {
  background: var(--primary-deep);
  border-color: var(--primary-deep);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn__arw { transition: transform var(--t) var(--ease); flex: none; }
.btn:hover .btn__arw { transform: translateX(4px); }

.btn--ghost {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.55);
}
.btn--ghost:hover {
  background: #fff;
  border-color: #fff;
  color: var(--dark);
}

.btn[disabled], .btn[aria-disabled='true'] {
  background: var(--rule);
  border-color: var(--rule);
  color: #8a8a8a;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* An inline link that still offers a finger-sized target */
.taplink {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* Text link with a chevron that nudges */
.lnk {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  font-family: var(--display);
  font-size: 19px;
  color: #000;
  text-decoration: none;
  position: relative;
}
.lnk__t { position: relative; }
.lnk__t::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -3px;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t) var(--ease);
}
.lnk:hover .lnk__t::after, .lnk:focus-visible .lnk__t::after { transform: scaleX(1); }
.lnk__arw { transition: transform var(--t) var(--ease); flex: none; }
.lnk:hover .lnk__arw { transform: translateX(4px); }

.band--dark .lnk { color: var(--accent); }
.band--dark .lnk:hover { color: #fff; }

/* --- Masthead ----------------------------------------------------------- */
.masthead {
  position: relative;
  z-index: 40;
  background: var(--page);
}

.masthead__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Below about 360px the logo and the emergency number stop fitting on one
     line, so the number drops to a second row rather than pushing the page
     wider than the screen. */
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 14px var(--gutter);
  max-width: 1208px;
  margin: 0 auto;
}

.brand { display: block; line-height: 0; flex: none; }
.brand img { width: 200px; height: 36px; }

/* Emergency call block. The seal is struck as a flame-red coin, picking up the
   fire in the logo (the artwork is a flat grey mark, so it is knocked out to
   white over the coin), and the number floats free beside it in the display
   face — no plate around the pair. --coin drives every size in the block, so a
   breakpoint only has to restate the coin and the type. */
.emerg {
  --coin: 36px;
  --coin-inset: 0px;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  flex: none;
  /* With no plate the coin alone is the hit area on a phone; hold the link at
     finger size and let the coin sit centred inside it. */
  min-height: 48px;
  padding: 0;
  text-decoration: none;
  color: var(--dark);
  font-family: var(--display);
  font-size: 17px;
  white-space: nowrap;
  transition: transform var(--t) var(--ease);
}

/* The coin sits behind the badge image; the image itself carries a filter,
   which would recolour any background painted on it. */
.emerg::before,
.emerg::after {
  content: '';
  position: absolute;
  left: var(--coin-inset);
  top: 50%;
  width: var(--coin);
  height: var(--coin);
  margin-top: calc(var(--coin) / -2);
  border-radius: 50%;
  pointer-events: none;
}
.emerg::before {
  background: radial-gradient(circle at 32% 26%, var(--fire-light) 0%, var(--fire) 52%, var(--fire-deep) 100%);
  box-shadow: 0 3px 8px -2px rgba(168, 34, 28, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.28);
  transition: box-shadow var(--t) var(--ease);
}
/* Slow halo on the coin — the one piece of motion in the masthead, a nod to
   the round-the-clock line. */
.emerg::after {
  border: 2px solid var(--fire);
  opacity: 0;
  animation: emerg-halo 3.2s var(--ease) infinite;
}
@keyframes emerg-halo {
  0% { transform: scale(1); opacity: 0.45; }
  60%, 100% { transform: scale(1.5); opacity: 0; }
}

.emerg img {
  position: relative;
  width: var(--coin);
  height: var(--coin);
  padding: calc(var(--coin) * 0.16);
  flex: none;
  filter: brightness(0) invert(1);
}

.emerg__num {
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  transition: color var(--t) var(--ease);
}

.emerg:hover {
  color: var(--fire);
  transform: translateY(-1px);
}
.emerg:hover::before { box-shadow: 0 4px 12px -2px rgba(168, 34, 28, 0.65), inset 0 0 0 1px rgba(255, 255, 255, 0.4); }
.emerg:active { transform: translateY(0); }
.emerg:focus-visible { outline-color: var(--fire); outline-offset: 6px; border-radius: 4px; }

/* Stacked variant: no coin at all, just the number over a small caps label, so
   the emergency route is stated in words rather than a symbol. The label is
   sized in em off .emerg, so it tracks the number at every breakpoint without
   a rule of its own. Scoped to the class — the pages still carrying the badge
   image keep the coin above. */
.emerg--stack {
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  line-height: 1;
}
.emerg--stack::before,
.emerg--stack::after { content: none; }

.emerg__label {
  font-size: 0.5em;
  letter-spacing: 0.16em;
  /* Tracking adds a space after the last letter too; pull it back so the
     label's final glyph lines up with the number's, not its box. */
  margin-right: -0.16em;
  /* The words are authored in sentence case so the editor shows them that way;
     the masthead sets them as a caps label. */
  text-transform: uppercase;
  color: var(--fire);
  transition: color var(--t) var(--ease);
}
.emerg--stack:hover .emerg__label { color: var(--fire-deep); }

/* --- Navigation --------------------------------------------------------- */
.nav {
  background: var(--dark);
  position: relative;
}

.drawer { position: relative; }
.drawer > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 56px;
  padding: 0 var(--gutter);
  color: #fff;
  font-family: var(--display);
  font-size: 19px;
  letter-spacing: 0.02em;
  cursor: pointer;
  list-style: none;
  transition: background var(--t) var(--ease);
}
.drawer > summary::-webkit-details-marker { display: none; }
.drawer > summary:hover { background: #2b2b2b; }
.drawer__ico { width: 22px; height: 16px; flex: none; }
.drawer__ico line {
  stroke: #fff;
  stroke-width: 2;
  transition: transform var(--t) var(--ease), opacity var(--t) var(--ease);
  transform-origin: center;
}
.drawer[open] .drawer__ico .l1 { transform: translateY(7px) rotate(45deg); }
.drawer[open] .drawer__ico .l2 { opacity: 0; }
.drawer[open] .drawer__ico .l3 { transform: translateY(-7px) rotate(-45deg); }

.drawer__panel {
  position: absolute;
  top: 100%;
  right: 0;
  width: min(320px, 88vw);
  background: var(--dark);
  border-left: 1px solid var(--dark-rule);
  border-bottom: 1px solid var(--dark-rule);
  box-shadow: -14px 18px 40px rgba(31, 31, 31, 0.3);
  padding: 8px 0 20px;
}
.drawer:not([open]) .drawer__panel { display: none; }
.drawer[open] .drawer__panel { animation: drawer-in var(--t-slow) var(--ease) both; }
@keyframes drawer-in {
  from { transform: translateX(28px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

.nav__list { list-style: none; }
.nav__item { position: relative; }

.nav__link {
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 6px 24px;
  color: #fff;
  font-family: var(--display);
  font-size: 19px;
  letter-spacing: 0.01em;
  text-decoration: none;
  transition: color var(--t) var(--ease), background var(--t) var(--ease);
}
.nav__link:hover { color: var(--accent); background: #262626; }
.nav__link[aria-current='page'] { color: var(--accent); }

/* Contact Us stays white on hover/focus — text and its underline together */
.nav__link[data-mb='nav-contact']:hover,
.nav__link[data-mb='nav-contact']:focus-visible { color: #fff; }

.nav__sub { list-style: none; }
.nav__sub .nav__link {
  padding-left: 40px;
  font-size: 17px;
  color: var(--dark-ink-soft);
  min-height: 48px;
}
.nav__sub .nav__link::before {
  content: '';
  width: 12px; height: 1px;
  background: var(--dark-rule);
  margin-right: 10px;
  flex: none;
  transition: background var(--t) var(--ease), width var(--t) var(--ease);
}
.nav__sub .nav__link:hover { color: #fff; }
.nav__sub .nav__link:hover::before { background: var(--accent); width: 18px; }

.nav__cta {
  margin: 16px 24px 0;
  display: flex;
}
.nav__cta .btn { width: 100%; justify-content: center; }

.nav__caret { display: none; }

/* --- Hero (home) -------------------------------------------------------- */
/* The hero is the opening screen and nothing more: it is exactly as tall as
   the viewport left under the floating top bar, so the photograph it carries
   ends at the fold and the two route cards below it are only reached by
   scrolling. `overflow: visible` because the photo layer is pulled up behind
   the bar and does its own clipping. */
.hero {
  position: relative;
  overflow: visible;
  min-height: calc(100vh - var(--head-h, 100px));
  min-height: calc(100svh - var(--head-h, 100px));
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: var(--dark-ink-soft);
  isolation: isolate;
}

/* Stage: the dark ground the opening screen and the two route panels share.
   The hero's photo and veil layers are its first children and are pulled up
   by --head-h (the bar's height at each breakpoint, measured) so the image
   starts at the very top of the page; any overshoot above y=0 is unseen. */
.stage {
  --head-h: 100px;
  position: relative;
  isolation: isolate;
  background: var(--dark);
  color: var(--dark-ink-soft);
}
/* `overflow: clip` rather than hidden: hidden makes this layer a scroll
   container, and a scroll container is what `animation-timeline: view()`
   binds to — which pinned the photo's parallax at a standstill. Clip crops
   the same way without creating one, so the timeline reaches the page
   scroller and the photo actually drifts. `hidden` stays as the fallback. */
.stage .hero__bg {
  inset: calc(-1 * var(--head-h)) 0 0;
  overflow: hidden;
  overflow: clip;
}
.stage .hero__bg img { object-position: 58% 50%; }
/* An even dark wash over the opening photograph — one flat tint edge to edge,
   no gradient, so the bright stainless never climbs up under the white type.
   Written as a literal rgba rather than var(--dark), which the palette sets
   to white: the veil must only ever darken. */
.stage .hero__veil {
  inset: calc(-1 * var(--head-h)) 0 0;
  background: rgba(31, 31, 31, 0.62);
}
.hero__bg {
  position: absolute;
  inset: -1px;
  z-index: -2;
  overflow: hidden;
}
.hero__bg picture { display: block; height: 100%; }
.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
}
/* ---------------------------------------------------------------------------
   The home backdrop is a reel, not a still. Four photographs stacked in one
   frame: each one creeps in at one percent a second for ten seconds — from
   scale(1) to scale(1.10) — and at the top of its zoom dissolves into the
   next, which begins its own creep from rest. Four slides at ten seconds
   each makes a forty-second loop.
   The zoom lives on .hero__slide and never on the <img> inside it: the photo
   itself is already carrying the scroll parallax on its own transform, and
   the two would overwrite one another on a single element. Nested, they
   compose — the slide breathes, the photo drifts.
   Slide 1 opens at full opacity so the first paint is the photograph, not a
   fade from black; it is also first in the DOM, so when the loop wraps it
   flicks back to opaque underneath slide 4 and is simply uncovered as slide 4
   fades away. */
.hero__slide {
  position: absolute;
  inset: 0;
  height: 100%;
  transform-origin: 50% 50%;
}
.hero__slide--2,
.hero__slide--3,
.hero__slide--4 { opacity: 0; }

@media (prefers-reduced-motion: no-preference) {
  .hero__bg--reel .hero__slide {
    animation: hero-reel 40s linear infinite both;
  }
  .hero__bg--reel .hero__slide--1 { animation-name: hero-reel-first; }
  .hero__bg--reel .hero__slide--2 { animation-delay: 10s; }
  .hero__bg--reel .hero__slide--3 { animation-delay: 20s; }
  .hero__bg--reel .hero__slide--4 { animation-delay: 30s; }
}

/* 3% of 40s is the 1.2s dissolve; 25% is the ten-second mark where the zoom
   has gained its full 10%. Past 28% the slide is invisible, so it holds its
   scale there rather than animating back — the snap to scale(1) happens at
   the wrap, behind zero opacity. */
@keyframes hero-reel {
  0%   { opacity: 0; transform: scale(1); }
  3%   { opacity: 1; }
  25%  { opacity: 1; transform: scale(1.1); }
  28%  { opacity: 0; transform: scale(1.112); }
  100% { opacity: 0; transform: scale(1.112); }
}
@keyframes hero-reel-first {
  0%   { opacity: 1; transform: scale(1); }
  25%  { opacity: 1; transform: scale(1.1); }
  28%  { opacity: 0; transform: scale(1.112); }
  100% { opacity: 0; transform: scale(1.112); }
}

.hero__veil {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(105deg, rgba(31, 31, 31, 0.96) 0%, rgba(31, 31, 31, 0.9) 42%, rgba(31, 31, 31, 0.62) 100%),
    linear-gradient(0deg, rgba(0, 100, 150, 0.35), rgba(0, 100, 150, 0.35));
}
.hero__inner { padding: 30px 0 24px; }
.hero__title {
  color: #fff;
  font-size: clamp(32px, 8.7vw, 38px);
  line-height: 1.06;
  max-width: 20ch;
}
/* The service word leads the headline at a larger size and flips through the
   three systems we fit. The words are stacked in a fixed-height slot so the
   heading never reflows as they change; with no animation running the first
   one simply stays, so the headline reads correctly without CSS motion. */
.hero__flip {
  display: block;
  position: relative;
  height: 1.06em;
  margin-bottom: 0.04em;
  font-size: 1.5em;
  line-height: 1;
  /* The flame red from the logo, so the word that changes is also the word
     that carries the brand mark's colour. At 51px and up it clears AA for
     large text against the hero veil. */
  color: var(--fire);
  perspective: 800px;
}
.hero__flip-word {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  transform-origin: 50% 50%;
  backface-visibility: hidden;
}
.hero__flip-word--2,
.hero__flip-word--3 { opacity: 0; }
/* The 2 in CO2, set small and dropped rather than with vertical-align: sub,
   which would grow the line box and push the fixed-height flip slot open. */
.hero__flip-word sub {
  font-size: 0.6em;
  line-height: 1;
  vertical-align: baseline;
  position: relative;
  bottom: -0.06em;
}
.hero__title-rest { display: block; }
.hero__lead {
  font-size: 17px;
  max-width: 40ch;
  color: var(--dark-ink-soft);
}
.hero__lead b { color: #fff; font-weight: 600; }

/* ASTTBC certification: the mark itself, with the word set beside it */
.hero__cert {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 12px;
  max-width: none;
}
.hero__cert-mark {
  width: 176px;
  height: auto;
  flex: none;
}
.hero__cert-word {
  font-size: 18px;
  line-height: 1.3;
  color: var(--dark-ink-soft);
}

/* Call routes, set out like the rating plate on a piece of equipment.
   The three rows are a grid of equal fractions rather than three stacked
   boxes, so every row is exactly the same height as its neighbours at every
   width — and on desktop, where the rows sit beside the heading, the block
   as a whole is the same height as the heading and the ASTTBC mark. */
.spec {
  list-style: none;
  border-top: 1px solid var(--dark-rule);
  margin-top: 4px;
  display: grid;
  grid-auto-rows: 1fr;
}
.spec__item { border-bottom: 1px solid var(--dark-rule); display: grid; }
.spec__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 56px;
  padding: 8px 0;
  text-decoration: none;
  color: var(--dark-ink-soft);
  transition: padding-left var(--t) var(--ease), color var(--t) var(--ease);
}
.spec__link:hover, .spec__link:focus-visible { color: #fff; padding-left: 8px; }
.spec__label { font-size: 15px; line-height: 1.3; }
.spec__num {
  font-family: var(--display);
  font-size: 22px;
  line-height: 1.1;
  color: #fff;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.spec__link:hover .spec__num { color: var(--accent); }
/* Number with the person who answers it, stacked and ranged right so the
   digits stay on the rule and the name hangs quietly beneath them. */
.spec__call {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  min-width: 0;
}
.spec__who {
  font-size: 14px;
  line-height: 1.15;
  letter-spacing: 0.04em;
  opacity: .92;
  white-space: nowrap;
}
/* The same call list on a light band (contact page): rules in the page's
   own grey, the number black, flame red on hover — literals rather than the
   primary and accent tokens, which the editor's palette holds near white. */
.band--edge .spec { border-top-color: var(--rule); }
.band--edge .spec__item { border-bottom-color: var(--rule); }
.band--edge .spec__link { color: var(--body); }
.band--edge .spec__link:hover,
.band--edge .spec__link:focus-visible { color: #000; }
.band--edge .spec__num { color: #000; }
.band--edge .spec__link:hover .spec__num,
.band--edge .spec__link:focus-visible .spec__num { color: var(--fire); }

/* Hero columns: the heading and the ASTTBC mark on one side, the three call
   routes on the other. The copy column is a grid of the same fractions as the
   calls beside it (see the desktop block), so the two sides square up. */
.hero__copy { min-width: 0; }

/* --- Page head (interior pages) -----------------------------------------
   The interior pages carry the same floating top bar as the home page, so
   their head photo is pulled up by --head-h in exactly the way .stage pulls
   up the hero's: the picture starts at the very top of the page and the
   white card floats on it. Clipping lives on the photo layer rather than on
   .pagehead itself, or the overshoot above y=0 would be cropped away. */
.pagehead {
  --head-h: 100px;
  background: var(--dark);
  color: var(--dark-ink-soft);
  position: relative;
  isolation: isolate;
}
.pagehead > .pagehead__bg {
  inset: calc(-1 * var(--head-h)) 0 -1px;
  overflow: hidden;
  overflow: clip;
}
.pagehead > .hero__veil { inset: calc(-1 * var(--head-h)) 0 0; }
.pagehead__inner { padding: 40px 0; }
.pagehead h1 { color: #fff; max-width: 22ch; }
.pagehead__sub { font-size: 17px; max-width: 46ch; margin-top: 12px; }

.crumbs {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  margin-bottom: 16px;
}
.crumbs li { display: flex; align-items: center; gap: 6px; min-height: 44px; }
.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  /* Keeps the shortest crumb ("Home") above the 40px touch minimum in any
     typeface while its left edge stays flush with the heading below. */
  min-width: 44px;
  padding-right: 6px;
}
.crumbs li + li::before {
  content: '';
  width: 10px; height: 1px;
  background: var(--dark-rule);
  flex: none;
}
.crumbs a { color: var(--dark-ink-soft); text-decoration: none; }
.crumbs a:hover { color: #fff; text-decoration: underline; }
.crumbs [aria-current='page'] { color: #fff; }

/* --- Split: copy beside a photograph ------------------------------------ */
.split { display: grid; gap: 24px; align-items: center; }
.split--top { align-items: start; }
.split__body > * + * { margin-top: 16px; }
.split__fig { position: relative; margin: 0; }
.split__fig img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--surface-deep);
}
.split__fig::before {
  content: '';
  position: absolute;
  top: -6px; left: -6px;
  width: 54px; height: 54px;
  border-top: 3px solid var(--heading);
  border-left: 3px solid var(--heading);
  z-index: 1;
}
.split__fig--contain img {
  aspect-ratio: 4 / 3;
  object-fit: contain;
  padding: 16px;
}

/* --- Panels: the two promoted routes on the home page ------------------- */
.panels { display: grid; gap: 20px; }
.panel {
  position: relative;
  overflow: hidden;
  background: var(--dark);
  color: var(--dark-ink-soft);
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 260px;
  padding: 22px 18px;
  /* Lifted off the drifting stage photograph behind them. */
  box-shadow: 0 18px 38px rgba(0, 0, 0, 0.38), 0 4px 10px rgba(0, 0, 0, 0.24);
}
/* The picture sits flush to the panel box, never a pixel past it: any overhang
   is a hairline of untinted photograph on a fractional device-pixel edge. */
.panel__bg { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.panel__bg img { width: 100%; height: 100%; object-fit: cover; }
/* The tint has to reach all four borders. The gradient alone only darkens down
   the panel, so bright photograph rimmed the top and sides against the black
   stage; the inset shadows seal the perimeter and the 1px ring closes the edge. */
.panel::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(31, 31, 31, 0.68) 0%, rgba(31, 31, 31, 0.93) 78%);
  box-shadow:
    inset 0 0 0 1px rgba(20, 20, 20, 0.92),
    inset 0 0 64px 22px rgba(20, 20, 20, 0.72);
}
.panel--flat { background: var(--surface); color: var(--body); justify-content: flex-start; }
.panel--flat::after { display: none; }
.panel--flat h2 { color: var(--heading); }
.panel h2 { color: #fff; }
.panel__list {
  list-style: none;
  font-size: 16px;
  margin-top: 12px;
}
.panel__list li {
  padding: 7px 0 7px 18px;
  position: relative;
  border-bottom: 1px solid var(--dark-rule);
}
.panel__list li::before {
  content: '';
  position: absolute;
  left: 0; top: 17px;
  width: 8px; height: 1px;
  background: var(--accent);
}
.panel--flat .panel__list li { border-bottom-color: var(--rule); }
.panel--flat .panel__list li::before { background: var(--heading); }
.panel > * + * { margin-top: 14px; }
.panel .btn, .panel .lnk { align-self: flex-start; }
/* The two promoted routes carry a solid button; the trio below them carries a
   text link, which keeps the hierarchy between the two rows of cards. On the
   tinted photograph it has to be white — the flat `.lnk` colour is black. */
.panel .lnk { color: var(--dark-ink); }
.panel .lnk:hover, .panel .lnk:focus-visible { color: var(--fire-light); }

/* --- The home trio: the same card, three across ------------------------- */
/* Shorter and lighter than the routes above: three cards to a row, no list,
   and the copy sets the height rather than a tall photographic minimum. */
.panels--trio .panel {
  min-height: 0;
  padding: 20px 18px;
  justify-content: flex-start;
}
.panels--trio .panel > * + * { margin-top: 10px; }
/* The card is the measure here — it is already narrower than a comfortable
   line — so the copy fills it rather than shrink-wrapping inside it. */
.panels--trio .panel p { max-width: none; align-self: stretch; }
.panels--trio .panel .lnk { margin-top: 14px; }
/* These cards are a third of the width of the routes above and carry more
   copy per square inch, so the photograph is held a few points further back:
   the same picture, quieter, with the text still the first thing read. */
.panels--trio .panel::after {
  background: linear-gradient(180deg, rgba(31, 31, 31, 0.80) 0%, rgba(31, 31, 31, 0.94) 78%);
}
/* The cylinder bank is lit hard from the right, and a centred crop put that
   flare under the heading. Biasing the crop left keeps the card's tonal
   weight in step with the two beside it. */
[data-mb="trio-inspections-bg-img"] { object-position: 18% 50%; }

/* Hover: the card rises a little and drifts a couple of pixels after the
   cursor. No overshoot and no bounce — one slow curve each way, so the pair
   reads as weight rather than as a hop.
   `transform` is deliberately untouched — the scroll reveal owns it — so both
   moves ride on `translate`, and registering the three lengths below is what
   lets them be transitioned and eased independently of one another. */
@property --panel-lift { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --panel-x { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --panel-y { syntax: '<length>'; inherits: false; initial-value: 0px; }

@media (hover: hover) and (pointer: fine) {
  .panel {
    translate: var(--panel-x, 0px) calc(var(--panel-y, 0px) - var(--panel-lift, 0px));
    /* Identical timing in and out: nothing snaps when the cursor crosses the
       gap between the two cards. The drift is slower than the lift so the card
       trails the pointer instead of tracking it. */
    --panel-motion:
      --panel-lift 520ms cubic-bezier(0.22, 0.61, 0.36, 1),
      --panel-x 620ms cubic-bezier(0.22, 0.61, 0.36, 1),
      --panel-y 620ms cubic-bezier(0.22, 0.61, 0.36, 1),
      box-shadow 520ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .panel:hover, .panel:focus-within {
    --panel-lift: 5px;
    box-shadow: 0 24px 46px rgba(0, 0, 0, 0.44), 0 6px 14px rgba(0, 0, 0, 0.27);
  }
  /* The reveal below declares `transition` for every .rvl element at a higher
     specificity, and a second `transition` would drop this one wholesale.
     So the hover motion is carried in a variable and spliced into both — the
     reveal keeps its opacity/transform, the card keeps its spring. */
  .panels .panel { transition: var(--panel-motion); }
  .js-rvl .panels .panel.rvl.is-in {
    transition: opacity 460ms var(--ease-crisp), transform 460ms var(--ease-crisp),
                var(--panel-motion);
  }
}

/* --- Inspection / service items ----------------------------------------- */
.items { display: grid; gap: 0; }
.item {
  display: grid;
  gap: 18px;
  padding: 32px 0;
  border-top: 1px solid var(--rule);
  align-items: center;
}
.items > .item:first-child { border-top: 0; padding-top: 0; }
.item__body > * + * { margin-top: 14px; }
.item__body p { font-size: 15px; line-height: 1.5; }
/* Each description carries the full text and a shorter phone version; the
   .u-full / .u-condensed pair at the top of this file swaps them under 768px. */
.item__fig { margin: 0; position: relative; }
.item__fig img {
  width: 100%;
  height: 140px;
  object-fit: cover;
  background: var(--surface-deep);
}
.item__fig--contain img { object-fit: contain; padding: 10px; }

/* 3D models in the item figures: a transparent WebGL canvas sits under the
   photograph it replaces. The photograph is the fallback and is what the
   markup shows on its own, so nothing depends on the script; the moment
   script.js knows the browser can render the model it marks the figure
   .is-3d-pending and the photo goes straight off, rather than sitting there
   through the fetch and then dissolving into the model in front of the
   visitor. It fades back in if the model cannot be built or fetched, and
   .is-3d holds it off once the model is drawn. The sprinkler figure slides
   in from the right edge of the screen and comes to rest with its branch line
   still running out through that edge, so its band clips sideways: script.js
   widens that one canvas past the window edge (the still photograph behind it
   keeps the column's width), and .band--clip cuts the overhang so the page
   itself never grows. */
.item__model {
  display: block;
  width: 100%;
  height: 140px;
  background: none;
}
.item__fig .item__still {
  position: absolute;
  inset: 0;
  transition: opacity var(--t-slow) var(--ease);
}
.item__fig.is-3d-pending .item__still { opacity: 0; pointer-events: none; transition: none; }
.item__fig.is-3d .item__still { opacity: 0; pointer-events: none; }
/* The figure stands empty for as long as the model takes to arrive, so the
   model fades up into it rather than snapping in. Nothing to fade for a
   model built in the page — it is already there on the first frame. */
.item__fig.is-3d-pending .item__model { opacity: 0; }
.item__fig.is-3d .item__model { opacity: 1; transition: opacity var(--t-slow) var(--ease); }
.item__fig--pop { will-change: transform; }

/* The restaurant figure is framed differently from the rest of the list: the
   kitchen stands on the white of the band at a size its row cannot hold, and
   the floor end of it is cut off by the rule under the row. The canvas is
   lifted out of the flow so the row keeps the height every other row has, the
   figure keeps that height as the box the fallback photograph sits in, and
   the row clips whatever hangs below it. On the phone the figure shares its
   row with the copy and the button sits directly beneath, so nothing bleeds
   there — the model is contained like the others. */
.item--bleed { overflow-x: visible; overflow-y: hidden; overflow-y: clip; }

/* Headline cut across a figure. A heading dragged over the extinguisher keeps
   its own colour, and turns white only where its letters actually cross the
   canister. script.js reads the figure's silhouette (the model's alpha once
   it has drawn, the photograph's non-white pixels before that) and hands the
   heading a stack of white strips through the three --cut-* variables; those
   strips and the heading colour underneath are clipped to the glyphs. With
   no overlap the class is absent and the heading paints as normal, so at
   every width — and without JavaScript — nothing can vanish against the
   page. */
.is-cut {
  -webkit-text-fill-color: transparent;
  -webkit-background-clip: text;
  background-clip: text;
  background-color: currentColor;
  background-origin: border-box;
  background-repeat: no-repeat;
  background-image: var(--cut-image);
  background-position: var(--cut-position);
  background-size: var(--cut-size);
}
.band--clip { overflow: hidden; overflow: clip; }

/* --- Brand / equipment list --------------------------------------------- */
.equip { display: grid; gap: 0; list-style: none; }
.equip__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  padding: 20px 0;
  border-top: 1px solid var(--rule);
  align-items: center;
}
.equip > .equip__row:first-child { border-top: 0; padding-top: 0; }
.equip__name { font-family: var(--display); font-size: 20px; color: var(--heading); }
.equip__fig { margin: 0; }
.equip__fig img {
  height: 64px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
}
.equip__link { font-size: 16px; }
/* Manufacturer links sit on their own line, so they can carry a full touch
   height without disturbing the paragraph above them. */
.equip__link a {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
}
/* Phone: each brand sits as a pair — name and link on the left, the mark as a
   small tile on the right — so the four rows share one screen. */
@media (max-width: 767px) {
  .equip__row { grid-template-columns: 1fr 110px; gap: 12px; padding: 12px 0; }
  .equip__fig { width: 110px; justify-self: end; }
  .equip__fig img { height: 48px; max-width: 110px; object-position: right center; }
}

/* --- Video frame -------------------------------------------------------- */
/* The manufacturer animations that ran on the original product pages. Each
   one stays with the system it belongs to, in the page order the original
   used. The frame keeps each film's own shape so the player never bars
   itself, and carries the same corner bracket as a photograph. */
.vframe {
  position: relative;
  margin: 0;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border: 1px solid var(--rule);
}
.vframe--43 { aspect-ratio: 4 / 3; }
.vframe iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.vframe::before {
  content: '';
  position: absolute;
  top: -6px; left: -6px;
  width: 54px; height: 54px;
  border-top: 3px solid var(--heading);
  border-left: 3px solid var(--heading);
  z-index: 1;
}
/* In a brand row the film sits under the name and mark until there is desktop
   width to set it beside them. */
.equip__row--video > div,
.equip__row--vsplit > div { grid-column: 1; grid-row: 1; }
.equip__row--video > .equip__fig { grid-column: 2; grid-row: 1; }
.equip__row--video > .equip__vid,
.equip__row--vsplit > .equip__vid { grid-column: 1 / -1; grid-row: 2; margin-top: 8px; }
/* A single-system page puts its film in the media half of the split, where a
   photograph would otherwise sit — the figure already draws the corner
   bracket, so the frame inside it goes bare. */
.split__fig--video .vframe { border: 0; }
.split__fig--video .vframe::before { content: none; }

/* --- Facts list (about, contact) ---------------------------------------- */
.facts { list-style: none; display: grid; gap: 0; }
.facts__row {
  padding: 16px 0;
  border-top: 1px solid var(--rule);
  display: grid;
  gap: 4px;
}
.facts > .facts__row:first-child { border-top: 0; }
.facts__k {
  font-family: var(--display);
  font-size: 16px;
  /* Black on the light bands — the Phone / Address / Email labels read as ink,
     not as links. Kept literal so it stays legible whatever --primary becomes. */
  color: #000000;
  letter-spacing: 0.02em;
}
.facts__v { font-size: 17px; }
.facts__v a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: none;
  /* Black at rest, flame red on hover — same literals for the same reason. */
  color: #000000;
  border-bottom: 1px solid rgba(0, 0, 0, 0.35);
  transition: color var(--t-fast) var(--ease), border-bottom-color var(--t-fast) var(--ease);
}
.facts__v a:hover,
.facts__v a:focus-visible { color: var(--fire); border-bottom-color: var(--fire); }
.facts__v address a { min-height: 0; }
.band--dark .facts__row { border-top-color: var(--dark-rule); }
.band--dark .facts__k { color: var(--accent); }
/* On a dark band the same pair inverts: light ink at rest, flame red on hover. */
.band--dark .facts__v a { color: var(--dark-ink); border-bottom-color: rgba(255, 255, 255, 0.35); }
.band--dark .facts__v a:hover,
.band--dark .facts__v a:focus-visible { color: var(--fire-light); border-bottom-color: var(--fire-light); }

/* --- Form --------------------------------------------------------------- */
.form { display: grid; gap: 18px; }
.field { display: grid; gap: 6px; }
.field__l {
  font-family: var(--display);
  font-size: 17px;
  color: var(--heading);
  letter-spacing: 0.01em;
}
.field__i {
  font-family: var(--text);
  font-size: 16px;
  line-height: 1.5;
  color: var(--dark);
  background: #fff;
  border: 1px solid #c4ccd2;
  border-radius: 2px;
  padding: 13px 14px;
  min-height: 52px;
  width: 100%;
  transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.field__i::placeholder { color: #767676; }
.field__i:hover { border-color: #9aa6ae; }
.field__i:focus {
  outline: none;
  border-color: #000;
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12);
}
.field__i:focus-visible { outline: none; }
textarea.field__i { min-height: 108px; resize: vertical; }
.form__hp { position: absolute; left: -9999px; }

.form__done {
  display: none;
  border-left: 3px solid var(--heading);
  background: var(--surface);
  padding: 16px 18px;
  font-size: 16px;
}
.form__done:target { display: block; }
.form__done:target + .form { display: none; }

/* --- Post / article ----------------------------------------------------- */
.meta {
  font-size: 15px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.meta a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: none;
}
.meta a:hover { text-decoration: underline; }

.postlist { list-style: none; display: grid; gap: 0; }
.postlist__row { border-top: 1px solid var(--rule); padding: 24px 0; }
.postlist > .postlist__row:first-child { border-top: 0; padding-top: 0; }
.postlist__row h2 { font-size: 22px; }
.postlist__row h2 a { display: inline-block; min-height: 44px; text-decoration: none; }
.postlist__row h2 a:hover { text-decoration: underline; }
.postlist__row > * + * { margin-top: 10px; }

.aside { margin-top: 40px; padding-top: 28px; border-top: 1px solid var(--rule); }
.aside h2 { font-size: 20px; }
.aside__list { list-style: none; margin-top: 8px; }
.aside__list li + li { border-top: 1px solid var(--rule); }
.aside__list a {
  display: flex;
  align-items: center;
  min-height: 48px;
  text-decoration: none;
  font-size: 16px;
}
.aside__list a:hover { text-decoration: underline; }
.aside__empty { font-size: 16px; margin-top: 8px; }

/* --- Closing call to action --------------------------------------------- */
/* A light band (band--tint in the markup) over the black footer. It was a dark
   band, but the editor's palette holds the dark-section colour at white, which
   left its white heading, number and outline button invisible; set light on
   purpose, its words take the page's own greys and black. */
.close__grid { display: grid; gap: 20px; }
.close h2 { color: var(--heading); }
.close .lede { color: var(--body); }
.close .lede a { color: #000; }
.close .btn--ghost { color: #000; border-color: #000; }
.close .btn--ghost:hover,
.close .btn--ghost:focus-visible { background: #000; border-color: #000; color: #fff; }
.close__act { display: flex; flex-wrap: wrap; gap: 12px; }

/* --- Footer ------------------------------------------------------------- */
.footer {
  background: #000;
  color: var(--dark-ink-soft);
  border-top: 3px solid var(--primary);
  padding: 32px 0 24px;
  font-size: 16px;
}
.footer h2 { font-size: 18px; color: #fff; }
.footer__grid { display: grid; gap: 22px; }
/* The footer carries the same wordmark as the masthead, in its light cut for
   the dark band. 5.52:1, the mark's own ratio — sized a little wider than the
   phone masthead so the brand still closes the page with some weight. */
.footer__brand img { width: 210px; height: 38px; }
.footer a { color: var(--dark-ink-soft); }
.footer a:hover { color: #fff; }
.footer__list { list-style: none; display: grid; gap: 4px; margin-top: 10px; }
.footer__list a {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.footer__list a:hover { text-decoration: underline; }
.footer__addr { font-style: normal; margin-top: 10px; line-height: 1.6; }
.footer__tel {
  font-family: var(--display);
  font-size: 22px;
  color: #fff;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.footer__tel:hover { color: var(--accent); }
.footer__bar {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--dark-rule);
  font-size: 14px;
  display: grid;
  gap: 8px;
}

/* ===========================================================================
   Tablet — 768px and up. The phone layout widened: one column, capped
   measure, larger type, 32px gutters.
   =========================================================================== */
@media (min-width: 768px) {
  :root { --gutter: 32px; --band-y: 64px; }

  body { font-size: 18px; }
  h1 { font-size: clamp(36px, 5.4vw, 44px); }
  h2 { font-size: 30px; }
  h3 { font-size: 24px; }
  h4 { font-size: 20px; }

  .wrap--tab { max-width: 704px; margin-inline: auto; }

  .brand img { width: 250px; height: 45px; }
  .emerg {
    --coin: 46px;
    font-size: 23px;
    gap: 12px;
  }

  .lede { font-size: 20px; }

  .stage, .pagehead { --head-h: 122px; }
  .hero__inner { padding: 56px 0 48px; }
  .hero__title { font-size: clamp(40px, 6vw, 50px); }
  .hero__lead { font-size: 19px; }
  .hero__cert-mark { width: 228px; }
  .hero__cert-word { font-size: 21px; }
  .spec__num { font-size: 30px; }
  .spec__who { font-size: 15px; }
  .spec__label { font-size: 17px; }
  .spec__link { min-height: 68px; }

  .pagehead__inner { padding: 56px 0; }
  .pagehead__sub { font-size: 19px; }

  .item__fig img, .item__model { height: 200px; }
  .item__body p { font-size: 17px; line-height: 1.55; }
  /* The restaurant kitchen, drawn larger than its row and cut at the rule */
  .item__fig--bleed { height: 200px; }
  .item__fig--bleed .item__model {
    position: absolute;
    left: 0;
    top: -10px;
    width: 100%;
    height: 272px;
  }
  textarea.field__i { min-height: 132px; }

  .equip__row { grid-template-columns: 1fr 200px; gap: 24px; padding: 26px 0; }
  .equip__fig img { height: 76px; object-position: right center; }
  .equip__name { font-size: 24px; }

  .footer__grid { grid-template-columns: 1fr 1fr; gap: 32px; }
  .footer__brand img { width: 240px; height: 43px; }
  .footer__bar { grid-template-columns: 1fr auto; align-items: center; }
}

/* ===========================================================================
   Desktop — 1024px and up. Inline navigation, two-column composition.
   =========================================================================== */
@media (min-width: 1024px) {
  :root { --gutter: 40px; --band-y: 88px; }

  /* Kitchen brand list, desktop only. The editor arranged these rows with the
     manufacturer mark on the left edge and the name set in beside it, and its
     overrides carry that text offset from the 200px slot at the right of the
     row. So the mark takes the first track, the text block keeps the right
     slot as its anchor, and is allowed to run wider than that slot so long
     names set on one line instead of stacking three deep. */
  .equip__row--lead > .equip__fig--lead { grid-column: 1; grid-row: 1; align-self: center; width: 200px; }
  .equip__row--lead > .equip__fig--lead img { object-position: left center; }
  .equip__row--lead > div { grid-column: 2; grid-row: 1; }
  .equip__row--lead .equip__name { width: max-content; max-width: 680px; }
  /* This mark used to be a product photo nudged by hand in the editor; as a
     logo it aligns on the shared left edge instead. */
  .equip__row--lead [data-mb="kitchen-kidde-img"] { translate: none !important; }

  /* Manufacturer videos, desktop. On the kitchen rows the editor's own offsets
     anchor the mark and the name to the two tracks above, so the film is laid
     into the same grid row and pulled to the right edge instead of being given
     a track of its own — the tracks stay 1fr + 200px and nothing shifts
     sideways underneath it. */
  .equip__row--video > .equip__fig { grid-column: 2; grid-row: 1; }
  .equip__row--lead.equip__row--video > div { grid-column: 2; grid-row: 1; }
  .equip__row--lead.equip__row--video > .equip__fig--lead { grid-column: 1; grid-row: 1; }
  .equip__row--video > .equip__vid {
    grid-column: 1 / -1;
    grid-row: 1;
    justify-self: end;
    width: 440px;
    margin-top: 0;
    z-index: 1;
  }
  .equip__row--video .equip__name { width: max-content; max-width: 430px; }

  /* Rows the editor has not nudged take the plain two-track split the original
     page used: the system named on the left, its film on the right. */
  .equip__row--vsplit { grid-template-columns: minmax(0, 1fr) 460px; }
  .equip__row--vsplit > .equip__vid { grid-column: 2; grid-row: 1; margin-top: 0; }

  /* A brand list broken into a section per screen for the phone reads as one
     unbroken list again once there is desktop height to carry it: the sections
     that run on shed the band padding between them and the first row of each
     takes back the dividing rule. */
  .band--brand-open { padding-bottom: 0; }
  .band--brand { padding-top: 0; }
  .band--brand .equip > .equip__row:first-child {
    border-top: 1px solid var(--rule);
    padding-top: 26px;
  }

  body { font-size: 17px; }
  h1 { font-size: clamp(44px, 3.8vw, 56px); }
  h2 { font-size: 34px; }
  h3 { font-size: 26px; }
  h4 { font-size: 21px; }

  .wrap--tab { max-width: none; margin-inline: 0; }

  .masthead__bar { padding: 20px var(--gutter); }
  .brand img { width: 280px; height: 51px; }
  .emerg {
    --coin: 54px;
    font-size: 27px;
    gap: 14px;
  }

  /* Inline nav */
  .drawer > summary { display: none; }
  /* A closed <details> hides its contents through the ::details-content
     wrapper in current engines, and through the child's display in older
     ones. Both are unwound here so the wide-screen nav is always laid out. */
  .drawer::details-content {
    content-visibility: visible;
    block-size: auto;
  }
  .drawer__panel,
  .drawer:not([open]) .drawer__panel {
    display: block;
    /* Chrome hides the content of a closed <details> with content-visibility,
       which would collapse the inline navigation on wide screens. */
    content-visibility: visible;
    position: static;
    width: auto;
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
    animation: none;
  }
  .nav__list {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    max-width: 1208px;
    margin: 0 auto;
    padding: 0 calc(var(--gutter) - 18px);
  }
  .nav__link {
    padding: 0 18px;
    min-height: 58px;
    font-size: 19px;
    position: relative;
  }
  .nav__link::after {
    content: '';
    position: absolute;
    left: 18px; right: 18px; bottom: 12px;
    height: 2px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--t) var(--ease);
  }
  .nav__link:hover::after, .nav__link:focus-visible::after { transform: scaleX(1); }
  .nav__link[aria-current='page']::after { transform: scaleX(1); background: var(--primary); }
  .nav__link[data-mb='nav-contact']::after { background: #fff; }
  .nav__link:hover { background: transparent; }

  .nav__caret {
    display: block;
    margin-left: 7px;
    transition: transform 420ms var(--ease-bounce);
  }
  .nav__item--has-sub:hover .nav__caret,
  .nav__item--has-sub:focus-within .nav__caret { transform: rotate(180deg); }

  /* Submenu — a lobe that bubbles out of the bar rather than a second card
     floating under it. The box hangs from the trigger's bottom edge, so its
     first --sub-neck pixels sit inside the bar; the visible surface and its
     shadow live on ::after, which starts a couple of pixels above where the
     bar ends. The white therefore always meets the bar with no seam and no
     sliver of photograph between them, and the bar's own hairline simply
     stops for the width of the lobe — the card has grown a tab, not sprouted
     a panel. Opening swells out with a small overshoot; closing is a quick,
     calm fade so the menu is never springy on the way out. */
  .nav__sub {
    --sub-neck: 23px;
    /* Overshoots the travel by ~5%, so the sheet's bottom edge passes its
       resting line by about 5px and settles back — enough to read as a bounce,
       small enough not to wobble. */
    --sub-spring: cubic-bezier(0.22, 1.4, 0.36, 1);
    position: absolute;
    top: 100%;
    left: -9px;
    min-width: 330px;
    /* The neck is transparent padding, so the pointer crosses from the trigger
       into the menu without ever leaving the element. */
    padding: calc(var(--sub-neck) + 10px) 8px 8px;
    isolation: isolate;
    opacity: 0;
    visibility: hidden;
    transition:
      opacity 130ms var(--ease),
      visibility 0s linear 190ms;
    z-index: 30;
  }
  /* The sheet, not the box, is what moves. Scaling the whole submenu squashed
     the words with it; here the surface stretches down out of the bar from its
     own top edge — which sits exactly on the bar's foot — while the links ride
     in untouched at their true size. */
  .nav__sub::after {
    content: '';
    position: absolute;
    inset: var(--sub-neck) 0 0;
    z-index: -1;
    background: #171717;
    border-radius: 0 0 18px 18px;
    box-shadow: 0 18px 30px -12px rgba(0, 0, 0, 0.45);
    transform: scale(0.94, 0.48);
    transform-origin: 28% 0;
    transition: transform 190ms var(--ease);
  }
  .nav__item--has-sub:hover .nav__sub,
  .nav__item--has-sub:focus-within .nav__sub {
    opacity: 1;
    visibility: visible;
    transition: opacity 160ms var(--ease), visibility 0s;
  }
  .nav__item--has-sub:hover .nav__sub::after,
  .nav__item--has-sub:focus-within .nav__sub::after {
    transform: none;
    transition: transform 520ms var(--sub-spring);
  }

  /* The links ride up under the bar's edge and settle a beat behind the sheet,
     so the lobe fills rather than arriving all at once. */
  .nav__sub .nav__item {
    opacity: 0;
    transform: translateY(-7px);
    transition: opacity 110ms var(--ease), transform 110ms var(--ease);
  }
  .nav__item--has-sub:hover .nav__sub .nav__item,
  .nav__item--has-sub:focus-within .nav__sub .nav__item {
    opacity: 1;
    transform: none;
    transition: opacity 240ms var(--ease), transform 420ms var(--sub-spring);
  }
  .nav__item--has-sub:hover .nav__sub .nav__item:nth-child(1),
  .nav__item--has-sub:focus-within .nav__sub .nav__item:nth-child(1) { transition-delay: 90ms; }
  .nav__item--has-sub:hover .nav__sub .nav__item:nth-child(2),
  .nav__item--has-sub:focus-within .nav__sub .nav__item:nth-child(2) { transition-delay: 130ms; }
  .nav__item--has-sub:hover .nav__sub .nav__item:nth-child(3),
  .nav__item--has-sub:focus-within .nav__sub .nav__item:nth-child(3) { transition-delay: 170ms; }
  .nav__item--has-sub:hover .nav__sub .nav__item:nth-child(n + 4),
  .nav__item--has-sub:focus-within .nav__sub .nav__item:nth-child(n + 4) { transition-delay: 210ms; }

  .nav__sub .nav__link { padding: 0 16px; min-height: 46px; font-size: 17px; border-radius: 12px; }
  .nav__sub .nav__link::after { display: none; }
  /* The leading dash belongs to the phone drawer, where the sub-items need to
     read as children of the item above them. In the lobe they are the only
     thing on the surface and stand on their own. */
  .nav__sub .nav__link::before { display: none; }
  .nav__cta { display: none; }

  /* Hero: copy left, photograph right */
  .stage, .pagehead { --head-h: 128px; }
  /* The -12px foot cancels the 12px lift the editor holds on this element in
     Desktop view, so the flat wash still lands flush with the hero's bottom
     edge instead of leaving a bright strip of photograph under it. */
  .stage .hero__veil { inset: calc(-1 * var(--head-h)) 0 -12px; }
  /* Heading and mark left, the three call routes right. Both columns are
     built on one row unit: the calls are three rows of it, the copy is a
     band of two (the heading) over a band of one (the ASTTBC mark), so the
     two sides finish at the same height and the mark squares up with the
     last number. Both rows stretch by the same fractions if the heading
     needs more than its two rows. */
  .hero__inner {
    --hero-row: 94px;
    padding: 88px 0 84px;
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
    gap: 0 64px;
    align-items: stretch;
  }
  .hero__copy {
    display: grid;
    grid-template-rows: minmax(calc(var(--hero-row) * 2), 2fr) minmax(var(--hero-row), 1fr);
    gap: 0;
  }
  .hero__copy > * { align-self: center; margin: 0; }
  .hero__title { font-size: clamp(44px, 3.7vw, 56px); max-width: none; }
  .hero__lead { font-size: 19px; max-width: 46ch; }
  .hero__cert { max-width: none; gap: 10px 18px; }
  .hero__cert-mark { width: 290px; }
  .hero__cert-word { font-size: 26px; }
  .spec { margin-top: 0; grid-template-rows: repeat(3, minmax(var(--hero-row), 1fr)); }
  .spec__num { font-size: 34px; }
  .spec__who { font-size: 17px; }
  .spec__label { font-size: 18px; }
  .spec__link { min-height: 0; padding: 12px 0; }

  .pagehead__inner {
    padding: 80px 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
    gap: 56px;
    align-items: end;
  }
  .pagehead__sub { font-size: 20px; margin-top: 0; }

  .panels { grid-template-columns: 1fr 1fr; gap: 28px; }
  .panel { min-height: 420px; padding: 40px; }

  /* Side by side the two cards share one set of rows, so the rule above each
     heading, the headings themselves, the paragraphs, every list row and the
     two buttons sit on the same lines straight across. The leading 1fr row
     takes up the slack, keeping the block bottom-weighted as it was. */
  @supports (grid-template-rows: subgrid) {
    .panels {
      min-height: 420px;
      grid-template-rows: minmax(0, 1fr) auto auto auto auto;
      column-gap: 28px;
      row-gap: 14px;
    }
    .panel {
      display: grid;
      grid-row: 1 / -1;
      grid-template-rows: subgrid;
      grid-template-columns: minmax(0, 1fr);
      min-height: 0;
    }
    /* Row 1 is the spacer. Each child is placed by hand — left to flow, they
       fill the spacer first and the run comes out one row short. */
    .panel > h2 { grid-row: 2; }
    .panel > p { grid-row: 3; }
    .panel > .panel__list { grid-row: 4; }
    .panel > .btn, .panel > .lnk { grid-row: 5; }
    /* Row gap does the spacing now, so the flow margins would double it. */
    .panel > * + *, .panel .panel__list { margin-top: 0; }
    .panel > h2, .panel > p { align-self: start; }
    .panel .btn, .panel .lnk { justify-self: start; align-self: start; }
  }

  /* Three across, and shorter than the routes above so the pair of rows reads
     as a primary and a secondary. Declared after the generic panel rules so it
     wins on source order at equal specificity. */
  .panels--trio { grid-template-columns: repeat(3, 1fr); gap: 24px; }
  .panels--trio .panel { min-height: 340px; padding: 32px 28px; }

  @supports (grid-template-rows: subgrid) {
    /* The routes above share one set of rows because their lists line up item
       for item. The trio's paragraphs are different lengths, and holding them
       to shared rows opened a dead band in the middle of the short cards, so
       these three opt back out: the copy flows from the top, and only the
       links are pinned to the foot, where they still land on one line. */
    .panels--trio {
      min-height: 0;
      grid-template-rows: none;
      row-gap: 24px;
    }
    .panels--trio .panel {
      display: flex;
      grid-row: auto;
      grid-template-rows: none;
    }
    .panels--trio .panel > * + * { margin-top: 12px; }
    .panels--trio .panel > p { align-self: stretch; }
    .panels--trio .panel > .lnk { margin-top: auto; padding-top: 14px; }
  }

  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 56px; }
  .split--flip .split__body { order: 2; }
  .split__fig img { aspect-ratio: 3 / 2; }

  .item {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: 48px;
    padding: 44px 0;
  }
  .item:nth-child(even) .item__fig { order: -1; }
  .item__fig img, .item__model { height: 260px; }
  .item__fig--bleed { height: 260px; }
  /* A little wider than its column, so the line keeps its length as the model
     drifts; the gap beside it and the page gutter take the overhang, and the
     band clips before the page can grow. */
  .item__fig--bleed .item__model {
    left: -60px;
    top: -40px;
    width: calc(100% + 120px);
    height: 420px;
  }
  .item__body p { font-size: 17px; }

  /* Every inspection item shares one layout, the fire alarm's: copy in one
     column, model in the other, alternating. The extinguisher had been
     composed differently in the editor (headline across the canister, a
     narrower, taller figure); those editor positions still sit in
     mb-overrides.css, so they are set back to the shared layout here with
     the specificity to win. Remove this block to restore that composition. */
  .item [data-mb="item-extinguisher-fig"] {
    translate: none !important;
    width: auto !important;
    max-width: 100% !important;
    height: auto !important;
  }
  .item [data-mb="item-extinguisher-title"] {
    translate: none !important;
    min-height: 0 !important;
    width: auto !important;
    max-width: none !important;
    font-size: 34px !important;
  }
  .item [data-mb="item-extinguisher-text"],
  .item [data-mb="item-extinguisher-cta"] { translate: none !important; }

  .facts { grid-template-columns: repeat(3, 1fr); gap: 0 32px; }
  .facts__row { border-top: 0; border-left: 1px solid var(--rule); padding: 0 0 0 20px; }
  .facts--stack { grid-template-columns: 1fr; gap: 0; }
  .facts--stack .facts__row { border-left: 0; border-top: 1px solid var(--rule); padding: 16px 0; }
  .facts--stack > .facts__row:first-child { border-top: 0; padding-top: 0; }
  .band--dark .facts__row { border-left-color: var(--dark-rule); }

  .close__grid { grid-template-columns: minmax(0, 1fr) auto; gap: 48px; align-items: center; }

  .form--split { grid-template-columns: 1fr 1fr; gap: 18px 24px; }
  .form--split .field--wide { grid-column: 1 / -1; }
  .form--split .form__act { grid-column: 1 / -1; }

  .footer__grid { grid-template-columns: 1.2fr 1fr 1fr 1.1fr; gap: 40px; }
  .footer__brand img { width: 250px; height: 45px; }
}

@media (min-width: 1200px) {
  .nav__list { padding: 0 22px; }
}

/* ===========================================================================
   Phone & tablet fixes
   =========================================================================== */

/* The contact form's honeypot field must stay invisible to real visitors
   without ever affecting page width. The base rule shoves it -9999px off
   canvas with nothing to contain that offset, so the document's scrollWidth
   grows to reach it and the browser reports a horizontal overflow. Below
   1024px, clip it in place instead — the same technique as .vh — and size
   its label and input down too, since their default rendered size would
   otherwise still register as an oversized, invisible tap target. */
@media (max-width: 767px) {
  .form__hp,
  .form__hp * {
    position: absolute;
    left: 0;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  /* The hidden field itself takes no box at all, so nothing measures it as
     a 1px tap target; bots still see it by name. */
  .form__hp input { width: 0; height: 0; min-height: 0; }

  .u-full { display: none; }
  .u-condensed { display: inline; }

  /* The home trio, compacted for the phone: tighter cards, a smaller heading
     and the short version of each card's copy, so three photographic cards
     stack in the space the three ruled columns used to take. */
  [data-mb="trio"] { padding: 40px 0; }
  .panels--trio { gap: 10px; }
  .panels--trio .panel { padding: 14px; }
  .panels--trio .panel h2 { font-size: 22px; }
  .panels--trio .panel .ruled { padding-top: 12px; }
  .panels--trio .panel > * + * { margin-top: 7px; }
  .panels--trio .panel p { font-size: 15px; line-height: 1.38; }
  .panels--trio .panel .lnk { font-size: 17px; margin-top: 0; }

  /* Phone layout for every inspection item, taken from the fire alarm: the
     heading across the top, the description on the left with the model
     floating beside it on the right, and the button underneath. The body
     wrapper dissolves so its children can sit on the article's grid. */
  .item {
    grid-template-columns: minmax(0, 1fr) 140px;
    grid-template-areas: "title title" "text fig" "cta cta";
    gap: 14px 16px;
    align-items: start;
  }
  .item__body { display: contents; }
  .item__body > * + * { margin-top: 0; }
  .item__body h2 { grid-area: title; overflow-wrap: anywhere; }
  .item__body p { grid-area: text; }
  .item__body .btn { grid-area: cta; justify-self: start; }
  .item__fig { grid-area: fig; align-self: center; width: 100%; }
  /* The fire alarm figure and text were dragged into this arrangement in
     the editor; those phone positions in mb-overrides.css are released here
     so the figure takes its grid cell like the others. */
  .item [data-mb="item-fire-alarm-fig"] {
    translate: none !important;
    width: 100% !important;
    height: auto !important;
  }
  .item [data-mb="item-fire-alarm-text"] { width: auto !important; }
}

@media (min-width: 768px) and (max-width: 1023px) {
  /* The trio goes three across on the tablet too — stacked, three photographic
     cards would run well past one screen. The columns are narrow at 820px, so
     the heading and copy step down a size to keep the measure readable. */
  .panels--trio { grid-template-columns: repeat(3, 1fr); gap: 16px; }
  .panels--trio .panel { padding: 22px 18px; }
  .panels--trio .panel h2 { font-size: 24px; }
  .panels--trio .panel p { font-size: 16px; line-height: 1.45; }
  .panels--trio .panel .lnk { font-size: 17px; margin-top: auto; }

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

/* ===========================================================================
   Motion
   =========================================================================== */

/* Page arrival: the crisp slide-in, on every page.
   The page's content slides up into place under the masthead the moment the
   document paints — a direct landing, a click from another page, a reload,
   all the same. Plain CSS on a `both` fill, so the page is fully readable
   with no script and the reduced-motion block collapses it to its end state.
   The travel is vertical so it can never widen the page: a sideways slide on
   the live document would give the window a scrollbar for as long as it ran.
   The masthead is a sibling of <main> and stays put, so the bar reads as one
   fixed frame the pages slide in beneath. */
main, .footer {
  --arrive-from: 0;
  animation: page-arrive 460ms var(--ease-crisp) both;
}
/* The fade is done by 55% (250ms), the slide runs the full length. Starting
   opacity comes from --arrive-from: 0 on a direct landing; 1 while a
   navigation's view transition is active (below), so the incoming page is
   painted solid from its first frame and the outgoing page dissolves straight
   into it — no moment where both are see-through and the pale page background
   glows between them. Chrome sets :active-view-transition on the new document
   before its first render, so the value is right from frame one, and it goes
   back to 0 only after the fade segment has already finished. */
@keyframes page-arrive {
  from { opacity: var(--arrive-from); transform: translateY(var(--slide)); }
  55% { opacity: 1; }
  to { opacity: 1; transform: none; }
}
html:active-view-transition main,
html:active-view-transition .footer { --arrive-from: 1; }
@media (min-width: 768px) {
  :root { --slide: 44px; }
}

/* Page-to-page transition.
   Without this the browser tears down one document and paints the next from
   blank, which on a site of dark photographic headers reads as a white flash
   on every click. Cross-document view transitions keep the outgoing page on
   screen: it dissolves on top, lifting slightly, while beneath it the incoming
   page — already solid, see --arrive-from above — runs its arrival slide. So
   navigation is the same crisp slide as a direct landing, never two entrances
   stacked, and at no point is anything but page content on screen. The
   incoming layer itself does not animate; it is a live view of the new
   document. The dissolve runs on the plain ease, not the crisp one: a fade
   that drops most of its opacity in its first frames is what reads as a
   flash. No script involved: browsers that lack the API navigate exactly as
   they did before, and under reduced motion the at-rule never applies, so
   those visitors get a plain instant navigation. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

::view-transition-old(root),
::view-transition-new(root) {
  mix-blend-mode: normal;
  animation: none;
}
::view-transition-old(root) {
  z-index: 1;
  animation: page-leave 320ms var(--ease) both;
}
@keyframes page-leave {
  to { opacity: 0; transform: translateY(calc(var(--slide) * -0.4)); }
}

/* Scroll reveal. Sections are visible in the CSS by default; the script adds
   .js-rvl to <html> and only then are they held back until they scroll in.
   Same slide, same curve as the page's arrival. */
.js-rvl .rvl {
  opacity: 0;
  transform: translateY(var(--slide));
}
.js-rvl .rvl.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 460ms var(--ease-crisp), transform 460ms var(--ease-crisp);
  transition-delay: var(--rvl-d, 0ms);
}

/* The one authored moment: the home page opening. The logo forms in the
   middle of a white veil and snaps into the bar, whose card wipes out from
   it as the menu pops up (see the intro block near the foot of this file);
   the hero carries the same gesture on down the page — its text pops up in
   turn and the rules of the call list draw out from their own middles.

   All of it is plain CSS on a `both` fill, so every word is on screen in its
   final place whether or not the script runs, and the reduced-motion block
   further down collapses the lot to its end state.

   Two rules hold everywhere below, both of them about the 390px screen:
   every pop travels vertically only, and the rules scale out on the plain
   ease — a sideways or scaling overshoot on a full-width row is exactly the
   thing that makes a phone scroll sideways. */

/* When the hero starts. Under 1024px there is no masthead sequence to wait
   for, so it opens almost at once; on desktop it comes in under the tail of
   the menu popping up. */
.hero { --intro-t0: 220ms; }

.hero__flip,
.hero__title-rest,
.hero__cert-mark,
.hero__cert-word,
.hero__seq--calls .spec__item {
  animation: intro-rise 460ms var(--ease-bounce) both;
}
.hero__flip { animation-delay: calc(var(--intro-t0) + 120ms); }
.hero__title-rest { animation-delay: calc(var(--intro-t0) + 200ms); }
/* The certification mark and its word are a lockup, so they follow each
   other closely rather than taking their own place in the cascade. */
.hero__cert-mark { animation-delay: calc(var(--intro-t0) + 340ms); }
.hero__cert-word { animation-delay: calc(var(--intro-t0) + 400ms); }
.hero__seq--calls .spec__item:nth-child(1) { animation-delay: calc(var(--intro-t0) + 280ms); }
.hero__seq--calls .spec__item:nth-child(2) { animation-delay: calc(var(--intro-t0) + 360ms); }
.hero__seq--calls .spec__item:nth-child(3) { animation-delay: calc(var(--intro-t0) + 440ms); }

/* The call list's rules draw out from their centres, the same move the white
   card makes in the masthead. The borders that are already there go
   transparent and a 1px line sits exactly where each one was, so the rows
   keep their heights and nothing in the layout shifts. Scoped to the hero's
   own list: .spec is also the contact pages' phone table, which does not
   animate. */
.hero__seq--calls { position: relative; border-top-color: transparent; }
.hero__seq--calls .spec__item { position: relative; border-bottom-color: transparent; }
.hero__seq--calls::before,
.hero__seq--calls .spec__item::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--dark-rule);
  animation: intro-rule 420ms var(--ease) both;
}
/* -1px on each: absolute offsets are measured from the padding box, so this
   lands the line over the transparent border rather than beside it. */
.hero__seq--calls::before { top: -1px; animation-delay: calc(var(--intro-t0) + 220ms); }
.hero__seq--calls .spec__item::after { bottom: -1px; }
.hero__seq--calls .spec__item:nth-child(1)::after { animation-delay: calc(var(--intro-t0) + 300ms); }
.hero__seq--calls .spec__item:nth-child(2)::after { animation-delay: calc(var(--intro-t0) + 380ms); }
.hero__seq--calls .spec__item:nth-child(3)::after { animation-delay: calc(var(--intro-t0) + 460ms); }

@keyframes intro-rise {
  from { opacity: 0; transform: translateY(18px); }
  60% { opacity: 1; }
  to { opacity: 1; transform: none; }
}
@keyframes intro-rule {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* Hovering a call route pulls the row in on itself: the label travels right,
   the number and the name who answers it travel left, the two closing on the
   middle of the rule. It is the intro's open-out run backwards, so the hero's
   one gesture reads in both directions.

   The distance grows with the view, and it is always far smaller than the gap
   between the label and the digits (145px at its tightest on desktop, 180px on
   a phone), so the two halves never meet, never wrap and never reach an edge.
   :active carries the same move to a tap, where there is no hover to give.
   Scoped to the hero's own list — .spec is the contact pages' phone table
   too, and that one keeps its existing hover. */
.hero__seq--calls { --pull: 8px; }
.hero__seq--calls .spec__label,
.hero__seq--calls .spec__link > .spec__num,
.hero__seq--calls .spec__call {
  transition: transform var(--t) var(--ease);
}
/* The generic row nudges its whole self right on hover; inside the hero the
   pull replaces that, so the centre it closes on stays put. */
.hero__seq--calls .spec__link:hover,
.hero__seq--calls .spec__link:focus-visible,
.hero__seq--calls .spec__link:active { padding-left: 0; }
.hero__seq--calls .spec__link:hover .spec__label,
.hero__seq--calls .spec__link:focus-visible .spec__label,
.hero__seq--calls .spec__link:active .spec__label {
  transform: translateX(var(--pull));
}
.hero__seq--calls .spec__link:hover > .spec__num,
.hero__seq--calls .spec__link:hover .spec__call,
.hero__seq--calls .spec__link:focus-visible > .spec__num,
.hero__seq--calls .spec__link:focus-visible .spec__call,
.hero__seq--calls .spec__link:active > .spec__num,
.hero__seq--calls .spec__link:active .spec__call {
  transform: translateX(calc(var(--pull) * -1));
}
@media (min-width: 768px) { .hero__seq--calls { --pull: 10px; } }
@media (min-width: 1024px) { .hero__seq--calls { --pull: 14px; } }

/* Desktop only: the hero is two columns here, so the pair opens out from the
   centre line exactly as the masthead's card does. Both columns travel from
   the middle outwards, so neither can reach past the edge of the window on
   the way. Under 1024px the hero is one stacked column with no pair to open,
   and nothing slides sideways. */
@media (min-width: 1024px) {
  .hero { --intro-t0: 780ms; }
  .hero__copy { animation: intro-open-left 640ms var(--ease) var(--intro-t0) both; }
  .hero__seq--calls { animation: intro-open-right 640ms var(--ease) var(--intro-t0) both; }
}
@keyframes intro-open-left {
  from { transform: translateX(110px); }
  to { transform: none; }
}
@keyframes intro-open-right {
  from { transform: translateX(-110px); }
  to { transform: none; }
}

/* The headline word flips to the next system every five seconds: a 15s loop
   shared by the three words, each one offset by a third. It lives inside the
   no-preference query so reduced-motion visitors never start it and the base
   rule above leaves "Kitchen" on screen on its own.

   The page always opens on "Kitchen" and holds it there for nine seconds
   before the first flip; from then on every word takes its regular five.
   Rather than give the first word its own keyframes — which would put its
   flip-in out of step with the word it replaces — all three are parked
   paused, their delays set so the held frame is the one where Kitchen has
   just finished flipping in. script.js releases them together 4.7s later and
   the loop runs on from there untouched: Kitchen 0–9s, Automotive 9–14s,
   CO2 14–19s, Kitchen 19–24s, and so on. If the script never runs the words
   stay parked, which is Kitchen on screen on its own — the same thing the
   base rules above show with no animation at all. */
@media (prefers-reduced-motion: no-preference) {
  .hero__flip-word {
    animation: hero-flip 15s cubic-bezier(0.4, 0, 0.2, 1) infinite both;
    animation-play-state: paused;
    animation-delay: -0.7s;
  }
  .hero__flip-word--2 { animation-delay: 4.3s; }
  .hero__flip-word--3 { animation-delay: 9.3s; }
  .hero__flip--run .hero__flip-word { animation-play-state: running; }
}
@keyframes hero-flip {
  0%      { opacity: 0; transform: rotateX(-90deg); }
  2%      { opacity: 1; }
  4.667%  { transform: rotateX(0deg); }
  33.333% { opacity: 1; transform: rotateX(0deg); }
  36.5%   { opacity: 0; }
  38%,
  100%    { opacity: 0; transform: rotateX(90deg); }
}

/* CSS-only parallax on the dark photographic bands. No fixed attachment:
   iOS ignores it. Disabled below 768px. */
@media (min-width: 768px) {
  .hero__bg img, .pagehead__bg img, .panel__bg img { transform: scale(1.14); }
  @supports (animation-timeline: view()) {
    .hero__bg img, .pagehead__bg img, .panel__bg img {
      animation: parallax linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      will-change: transform;
    }
    @keyframes parallax {
      from { transform: translateY(-7%) scale(1.14); }
      to { transform: translateY(7%) scale(1.14); }
    }
  }

  /* The home photograph is pushed deeper than the other bands: nearly twice
     the travel over the same scroll, so as the opening screen leaves, the
     picture slides down behind it rather than leaving with it. Because the
     photo starts at the very top of the document, the first half of a normal
     cover range can never be scrolled through — so its range starts at
     `cover 50%`, which is scroll position zero here, and the whole -13% →
     13% travel plays out over the one screen you scroll past. The travel
     never exceeds the overflow the scale buys (1.28 → 14% each side), so
     no edge of the photo is ever exposed. The veil stays put. */
  .stage .hero__bg img { transform: scale(1.28); }
  @supports (animation-timeline: view()) {
    .stage .hero__bg img {
      animation-name: parallax-deep;
      animation-range: cover 50% cover 100%;
    }
    @keyframes parallax-deep {
      from { transform: translateY(-13%) scale(1.28); }
      to { transform: translateY(13%) scale(1.28); }
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }
  .js-rvl .rvl { opacity: 1; transform: none; }
  /* The page is simply there — no slide in, no slide away. */
  main, .footer { animation: none; opacity: 1; transform: none; }
  ::view-transition-old(root) { animation: none; }
  .item__fig--pop { transform: none !important; will-change: auto; }
  /* No pop, no cursor drift — the cards stay put. */
  .panel, .panel:hover, .panel:focus-within {
    --panel-lift: 0px; --panel-x: 0px; --panel-y: 0px;
    translate: none;
    animation: none !important;
  }
  .emerg::after { animation: none; opacity: 0; }
  .hero__bg img, .pagehead__bg img, .panel__bg img { transform: scale(1.05); animation: none; }
  .stage .hero__bg img { transform: scale(1.05); animation: none; }
  .drawer[open] .drawer__panel { animation: none; }
  /* No pull on the call routes — with transitions off it would be a jump
     rather than a move. The colour change still marks the hover. */
  .hero__seq--calls { --pull: 0px; }
}

/* ===========================================================================
   Floating masthead — every page (.masthead--float)
   The top bar detaches from the page edges as a card on a soft shadow, and
   carries the site menu inside it instead of a separate dark navigation band
   below. It sits on the hero photo on the home page and on the page-head
   photo everywhere else. The base .masthead above is the fallback for any
   page that has not been given the modifier.
   =========================================================================== */
.masthead--float {
  --float-pad: 12px;
  /* How far the logo's top edge rides above the white card. Held at 0 for the
     wordmark: the lift was there to let the province silhouette stand clear of
     the white, and a mark that is only two words deep reads better set inside
     the card with the menu than hanging half out of it. */
  --logo-lift: 0px;
  background: transparent;
  padding: 14px var(--float-pad) 10px;
  z-index: 60;
}

/* Square-cornered card. --bar-white is the white's alpha: held at 80% in every
   state, scrolled or not, so the bar always reads as a sheet of glass over the
   content moving under it. */
.masthead--float .masthead__bar {
  --bar-white: 0.8;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: grid;
  grid-template-columns: 1fr auto auto;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, var(--bar-white));
  border: 1px solid var(--rule);
  border-radius: 0;
  box-shadow: 0 1px 2px rgba(31, 31, 31, 0.06), 0 10px 28px rgba(31, 31, 31, 0.1);
  transition: background-color 0.25s ease, backdrop-filter 0.25s ease;
}

/* Scrolled: same 80% white and blur as at rest — kept so the .is-scrolled hook
   the script sets stays meaningful if the two states ever diverge again. */
.masthead--float.is-scrolled .masthead__bar {
  --bar-white: 0.8;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.masthead--float .brand img { width: 170px; height: 31px; }

/* The card is shorter than the logo: pulling the brand's top margin negative
   shrinks the box the card sizes itself to, so the mark's top edge stands
   proud of the white by --logo-lift minus the card's own top padding. */
.masthead--float .brand { margin-top: calc(var(--logo-lift) * -1); }

.masthead--float .nav { background: transparent; justify-self: end; }

/* Phone and tablet: two square controls sit together at the card's right
   edge — the flame-red call button and the hamburger. The call button is a
   flat fill of the fire red: no gradient, no gloss ring, no coloured glow —
   one solid plane against the hamburger's flat grey square beside it. */
.masthead--float .call {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 12px;
  color: #fff;
  background: var(--fire);
  box-shadow: none;
  transition: background var(--t) var(--ease);
}
.masthead--float .call:hover {
  color: #fff;
  background: var(--fire-deep);
}
.masthead--float .call:active { transform: translateY(0); }
.masthead--float .call:focus-visible { outline-color: var(--fire); outline-offset: 3px; }
.masthead--float .call__ico { width: 22px; height: 22px; }

/* Hamburger: an icon-only 48px square. Two bars — the lower one shorter and
   flush right at rest — that swing into an X on open, each rotating about its
   own centre. The bounce lives in --ease-bounce on the transform. */
.masthead--float .drawer > summary {
  width: 48px;
  min-height: 48px;
  padding: 0;
  justify-content: center;
  color: var(--dark);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
}
.masthead--float .drawer > summary:hover { background: var(--surface-deep); border-color: var(--surface-deep); }
.masthead--float .drawer__ico line {
  stroke: var(--dark);
  stroke-width: 2.25;
  stroke-linecap: round;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.42s var(--ease-bounce);
}
.masthead--float .drawer__ico .l2 { transform: translateX(3px) scaleX(0.7); }
.masthead--float .drawer:not([open]) > summary:hover .drawer__ico .l2 { transform: none; }
.masthead--float .drawer[open] .drawer__ico .l1 { transform: translateY(4px) rotate(45deg); }
/* The standard three-bar icon fades its middle .l2 out on open; this icon's
   .l2 is the second of two bars and must stay, so its opacity is restated. */
.masthead--float .drawer[open] .drawer__ico .l2 { transform: translateY(-4px) rotate(-45deg); opacity: 1; }

/* The drawer panel hangs clear of the card and shares its corner radius.
   Held under 1024px: above that the panel is the inline menu itself, and
   clipping it would cut off the submenu. */
@media (max-width: 1023px) {
  /* Anchored to the card itself, so it drops clear of the whole bar rather
     than out of the middle of it, and paints over the emergency plate. */
  .masthead--float .nav,
  .masthead--float .drawer { position: static; }
  .masthead--float .drawer__panel {
    top: 100%;
    right: var(--float-pad);
    z-index: 10;
    border: 1px solid var(--dark-rule);
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 16px 36px rgba(31, 31, 31, 0.26);
  }
}

/* ===========================================================================
   Home-page opening: the logo forms, then snaps into the bar
   A white veil covers the page while the logo builds itself in the middle
   of the screen — GREAT WEST is drawn as an outline first, its ink fills in,
   the flame catches in the gap between the two words, the second line sets
   beneath — and then the whole mark snaps into the logo's slot in the bar as
   the veil lifts. The bar's card then wipes out from the logo and the menu
   pops up (wide screens only; on a phone the bar is already there under the
   veil).

   One timeline, in ms from load:
     0–700      letters drawn       550–950   ink fills, stroke fades
     620–1140   flame catches       820–1140  second line sets
     1150–1650  snap + veil lifts   1660      overlay gone
     then, from the landing: card wipes 0–560 (desktop), menu and number
     pop 150–890

   Everything runs on CSS keyframes with `both` fills, so the veil always
   lifts and the overlay always leaves, script or no script. The script
   only aims the snap (--snap-x / --snap-y / --snap-s, measured from the
   bar's own logo), hides that logo until the mark lands (.is-intro without
   .is-landed) and removes the overlay on landing, so the swap is a single
   frame. Reduced motion: the overlay is not shown at all.

   Motion blur: the fast moves carry a blur that ramps up as they leave and
   back to sharp as they arrive — the snap on the whole mark, the flare on
   the flame — so the eye reads one smooth motion rather than a run of
   crisp frames. The blur is its own animation on the plain ease, so the
   snap's overshoot curve never pushes it below zero. */
.logo-intro {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--page);
  pointer-events: none;
  animation: logo-intro-veil 400ms var(--ease) 1150ms both,
             logo-intro-gone 1ms linear 1660ms both;
}
.logo-intro__mark {
  display: block;
  width: min(86vw, 560px);
  height: auto;
  overflow: visible;
  transform-origin: 0 0;
  will-change: transform, filter;
  animation: logo-intro-snap 500ms cubic-bezier(0.34, 1.3, 0.64, 1) 1150ms both,
             logo-intro-snap-blur 500ms var(--ease) 1150ms both;
}
/* GREAT WEST: drawn as a line first. One dash a little longer than the
   longest letter outline (238 user units) covers every one of the eleven
   subpaths, so the dash slides into all of them together and the letters
   come up at one speed — then the ink fills in and the line fades under it. */
.logo-intro__word--1 {
  fill-opacity: 0;
  stroke: var(--body);
  stroke-width: 1.1;
  stroke-linejoin: round;
  stroke-dasharray: 240;
  stroke-dashoffset: 240;
  animation: logo-intro-trace 700ms cubic-bezier(0.4, 0, 0.3, 1) both,
             logo-intro-fill 300ms var(--ease) 550ms both,
             logo-intro-unstroke 260ms var(--ease) 690ms both;
}
/* The flame catches in the gap between the words, with the site's overshoot
   curve, once the letters either side of it have set. */
.logo-intro__fire {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: logo-intro-flame 520ms var(--ease-bounce) 620ms both,
             logo-intro-flame-blur 520ms var(--ease) 620ms both;
}
.logo-intro__word--2 {
  opacity: 0;
  transform-box: fill-box;
  animation: logo-intro-word 320ms var(--ease) 820ms both;
}

@keyframes logo-intro-trace {
  from { stroke-dashoffset: 240; }
  to { stroke-dashoffset: 0; }
}
@keyframes logo-intro-fill {
  from { fill-opacity: 0; }
  to { fill-opacity: 1; }
}
@keyframes logo-intro-unstroke {
  from { stroke-opacity: 1; }
  to { stroke-opacity: 0; }
}
@keyframes logo-intro-flame {
  from { opacity: 0; transform: scale(0.35, 0.2); }
  40% { opacity: 1; }
  to { opacity: 1; transform: none; }
}
@keyframes logo-intro-flame-blur {
  from { filter: blur(3px); }
  55% { filter: blur(1.2px); }
  to { filter: blur(0); }
}
@keyframes logo-intro-word {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@keyframes logo-intro-snap {
  from { transform: none; }
  to { transform: translate(var(--snap-x, 0px), var(--snap-y, 0px)) scale(var(--snap-s, 1)); }
}
/* Blur is measured before the scale, so 4px here is under 2px on screen by
   the time the mark is at navbar size. */
@keyframes logo-intro-snap-blur {
  from { filter: blur(0); }
  30% { filter: blur(4px); }
  75% { filter: blur(1px); }
  to { filter: blur(0); }
}
@keyframes logo-intro-veil {
  from { background-color: var(--page); }
  to { background-color: transparent; }
}
@keyframes logo-intro-gone {
  from { visibility: visible; }
  to { visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) {
  .logo-intro { display: none; }
}

/* The bar's own logo stays out of sight until the mark lands on it. */
.masthead--intro.is-intro:not(.is-landed) .brand img { opacity: 0; }

/* The hero waits for the veil: its copy pops up once the logo has landed,
   under the tail of the menu popping up on desktop. Scoped to the page that
   has the overlay — .hero is every page's opening band. */
.logo-intro ~ main .hero { --intro-t0: 1600ms; }
@media (min-width: 1024px) {
  .logo-intro ~ main .hero { --intro-t0: 1750ms; }
}

/* Wide screens: once the logo has landed, the white card wipes out from it
   and the menu words and the emergency number pop up in turn. The bar is
   held empty and transparent from the start (.is-intro); the wipe and the
   pops are keyed off .is-landed, which the script adds the moment the
   snap's animation ends — so they follow the logo in on the same clock,
   however late the stylesheet started. The classes are removed once the
   sequence ends, so every rule below is temporary; without the script, or
   under reduced motion, the bar is simply there. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .masthead--intro.is-intro .masthead__bar {
    position: relative;
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    transition: none;
  }
  /* The card itself, repainted on a layer that can be unclipped from the
     logo outward without stretching the words on it. */
  .masthead--intro.is-intro .masthead__bar::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: rgba(255, 255, 255, var(--bar-white));
    border: 1px solid var(--rule);
    box-shadow: 0 1px 2px rgba(31, 31, 31, 0.06), 0 10px 28px rgba(31, 31, 31, 0.1);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    clip-path: inset(0 100% 0 0);
  }
  .masthead--intro.is-landed .masthead__bar::before {
    animation: intro-card 560ms var(--ease) both;
  }
  .masthead--intro.is-intro .nav__list > .nav__item,
  .masthead--intro.is-intro .emerg { opacity: 0; }
  .masthead--intro.is-landed .nav__list > .nav__item,
  .masthead--intro.is-landed .emerg {
    animation: intro-pop 420ms var(--ease-bounce) both;
  }
  .masthead--intro.is-landed .nav__list > .nav__item:nth-child(1) { animation-delay: 150ms; }
  .masthead--intro.is-landed .nav__list > .nav__item:nth-child(2) { animation-delay: 220ms; }
  .masthead--intro.is-landed .nav__list > .nav__item:nth-child(3) { animation-delay: 290ms; }
  .masthead--intro.is-landed .nav__list > .nav__item:nth-child(4) { animation-delay: 360ms; }
  .masthead--intro.is-landed .nav__list > .nav__item:nth-child(n + 5) { animation-delay: 430ms; }
  .masthead--intro.is-landed .emerg { animation-delay: 470ms; }
}
@keyframes intro-card {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}
@keyframes intro-pop {
  from { opacity: 0; scale: 0.6; }
  60% { opacity: 1; }
  to { opacity: 1; scale: 1; }
}

/* Every other page (.masthead--slide): the same bar as the home page, but
   in place of the logo sequence it simply slides down into place from just
   above the window as the page arrives, on the same crisp ease the content
   slides up with. Plain CSS on a `both` fill: with no script the bar is
   there, and the reduced-motion block collapses it to its end state. */
.masthead--slide {
  animation: bar-slide-in 520ms var(--ease-crisp) 60ms both;
}
@keyframes bar-slide-in {
  from { opacity: 0; transform: translateY(-110%); }
  45% { opacity: 1; }
  to { opacity: 1; transform: none; }
}

/* Phone and tablet: the call button stands in for the emergency line, so the
   card is one row — logo, call, menu. The full line returns at 1024px. */
@media (max-width: 1023px) {
  .masthead--float .emerg { display: none; }
}

@media (min-width: 768px) {
  /* Wide enough for one line — and from here the card floats with the page. */
  .masthead--float {
    --float-pad: 20px;
    --logo-lift: 24px;
    position: sticky;
    top: 0;
    padding: 16px var(--float-pad) 14px;
  }
  .masthead--float .masthead__bar {
    display: flex;
    flex-wrap: nowrap;
    gap: 12px;
    padding: 10px 14px;
  }
  .masthead--float .brand img { width: 200px; height: 36px; }
  .masthead--float .call { order: 2; margin-left: auto; }
  .masthead--float .nav { order: 3; }
  .masthead--float .emerg {
    order: 2;
    margin-left: auto;
    --coin: 42px;
    font-size: 21px;
    gap: 11px;
  }
}

@media (min-width: 1024px) {
  .masthead--float {
    --float-pad: var(--gutter);
    padding: 18px var(--float-pad);
  }
  .masthead--float .masthead__bar {
    gap: 20px;
    padding: 8px 12px 8px 20px;
  }
  .masthead--float .brand img { width: 215px; height: 39px; }
  .masthead--float .call { display: none; }

  /* Menu inline, centred between the logo and the emergency number. */
  .masthead--float .nav {
    order: 0;
    flex: 1 1 auto;
    display: flex;
    justify-content: center;
  }
  .masthead--float .drawer,
  .masthead--float .drawer__panel,
  .masthead--float .drawer:not([open]) .drawer__panel { width: 100%; }
  .masthead--float .nav__list {
    justify-content: center;
    max-width: none;
    margin: 0;
    padding: 0;
  }
  .masthead--float .nav__link {
    min-height: 46px;
    padding: 0 15px;
    font-size: 18px;
    color: var(--dark);
  }
  .masthead--float .nav__link:hover,
  .masthead--float .nav__link:focus-visible,
  .masthead--float .nav__link[aria-current='page'],
  .masthead--float .nav__link[data-mb='nav-contact']:hover,
  .masthead--float .nav__link[data-mb='nav-contact']:focus-visible { color: #000; }
  .masthead--float .nav__link::after,
  .masthead--float .nav__link[data-mb='nav-contact']::after {
    left: 15px;
    right: 15px;
    bottom: 7px;
    background: #000;
  }
  .masthead--float .emerg {
    --coin: 48px;
    font-size: 24px;
    gap: 12px;
  }

  /* Submenu: the bar's own white carried downward. No border, no second card
     edge — just the sheet swelling out and rounding off at the bottom. The
     neck is the distance from the nav row's foot to the card's bottom edge,
     set two pixels short so the surface laps over the card's hairline rather
     than ever leaving daylight under it. */
  .masthead--float .nav__sub {
    --sub-neck: 23px;
  }
  .masthead--float .nav__sub::after {
    background: #fff;
    box-shadow: 0 16px 30px -14px rgba(31, 31, 31, 0.45);
  }
  .masthead--float .nav__sub .nav__link {
    min-height: 46px;
    padding: 0 16px;
    font-size: 17px;
    color: var(--body);
    border-radius: 12px;
    transition: color var(--t) var(--ease), background var(--t) var(--ease),
                padding 300ms var(--sub-spring);
  }
  /* Black on hover, not the primary token: the editor's palette holds that at
     a near-white, which blanked the hovered item on the card. The word steps
     right inside its pill — padding rather than a transform, so the pill
     itself stays squared up with the ones above and below it. */
  .masthead--float .nav__sub .nav__link:hover,
  .masthead--float .nav__sub .nav__link:focus-visible {
    color: #000;
    background: var(--surface);
    padding-left: 21px;
    padding-right: 11px;
  }
}

/* Phone and tablet: the mark sits inside the card, as it does on desktop,
   rather than standing proud of its top edge. The lift is dropped and the
   logo sized to the card's inner height, so the bar keeps the height it had
   and reads as one clean white rectangle with the mark centred in it. */
@media (max-width: 1023px) {
  /* The wordmark is only 27px deep, so the home link is given a 48px floor
     and centres the mark inside it — the tap target is the bar's full height
     rather than the thickness of the letters. */
  .masthead--float .brand {
    margin-top: 0;
    display: flex;
    align-items: center;
    min-height: 48px;
  }
  .masthead--float .masthead__bar { padding: 6px 12px; }
  .masthead--float .brand img { width: 150px; height: 27px; }
}

@media (min-width: 768px) and (max-width: 1023px) {
  .masthead--float .masthead__bar { padding: 8px 14px; }
  .masthead--float .brand img { width: 175px; height: 32px; }
}

/* Solid white card. The bar was 80% white over the hero photo, which greyed
   the sheet behind the dark logo mark and cost it its contrast; at full white
   the mark reads cleanly. Everything written inside the card is set black to
   match — nav links, the hamburger and the emergency number, which all took
   their colour from the light-on-dark scale that the glass bar assumed. */
.masthead--float .masthead__bar,
.masthead--float.is-scrolled .masthead__bar {
  --bar-white: 1;
  background: #fff;
}

.masthead--float .nav__link,
.masthead--float .emerg,
.masthead--float .emerg .emerg__num,
.masthead--float .drawer > summary { color: #000; }
.masthead--float .drawer__ico line { stroke: #000; }
/* Restated after the black above, which otherwise outranks the base hover. */
.masthead--float .emerg:hover,
.masthead--float .emerg:hover .emerg__num { color: var(--fire); }

/* Drawer menu, phone and tablet: the panel is a light card, so its links and
   their hover state are set the same black rather than the white the base
   dark-panel rules assume. */
@media (max-width: 1023px) {
  .masthead--float .nav__sub .nav__link { color: #000; }
  .masthead--float .nav__link:hover,
  .masthead--float .nav__link:focus-visible,
  .masthead--float .nav__link[data-mb='nav-contact']:hover,
  .masthead--float .nav__link[data-mb='nav-contact']:focus-visible,
  .masthead--float .nav__sub .nav__link:hover { color: #000; background: var(--surface); }
}

@media (min-width: 1024px) {
  .masthead--float .nav__link,
  .masthead--float .nav__link:hover,
  .masthead--float .nav__link:focus-visible,
  .masthead--float .nav__link[aria-current='page'],
  .masthead--float .nav__link[data-mb='nav-contact']:hover,
  .masthead--float .nav__link[data-mb='nav-contact']:focus-visible { color: #000; }
  /* The underline is the hover and current-page cue now that the colour no
     longer shifts — it has to be visible on white. */
  .masthead--float .nav__link::after,
  .masthead--float .nav__link[data-mb='nav-contact']::after { background: #000; }
  .masthead--float .nav__sub .nav__link { color: #000; }
}

@media print {
  .nav, .skip, .hero__bg, .panel__bg { display: none; }
  body { color: #000; background: #fff; }
}
