/* Nat Luby Electrical - site stylesheet.
   Every colour, radius, duration and shadow comes from design/tokens.css.
   A style literal at the point of use is a build error (rk:design-dna). */

*, *::before, *::after { box-sizing: border-box; }
html { scroll-padding-top: calc(var(--hdr-h) + 14px); }

body {
  margin: 0;
  background: var(--field);
  background-image: var(--texture-plate);
  color: var(--ink);
  font: 400 var(--fs-body)/var(--leading-body) var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

img { max-width: 100%; display: block; }
/* [hidden] must beat the component display rules below, or a hidden chip row
   or error line still occupies layout and inflates the pancake band. */
[hidden] { display: none !important; }
a { color: var(--link-on-light); }

/* A CONTACT DETAIL IS ONE COLOUR (rk:one-text-colour). The quote invite used to
   carry three: the list rows in --graphite, the links inside them in --ink, and
   the SAME phone number written into the paragraph beside them in --brand-deep.
   Three colours for one block of business information reads as a bug.
   Levelled UP to --ink, the strongest of the three - the brand stays where a
   mark belongs, on the underline and the icon. Scoped to main so the footer
   keeps its own on-dark colour, and .tag excluded because the call plate is a
   control, not a line of text. */
main :is(a[href^="tel:"], a[href^="mailto:"]):not(.tag),
.contact-lines a {
  color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--brand); padding-bottom: 1px;
  transition: color var(--snap);
}
main :is(a[href^="tel:"], a[href^="mailto:"]):not(.tag):hover,
.contact-lines a:hover { color: var(--brand-deep); }

/* ── HEADINGS ─────────────────────────────────────────────────────────────
   The TYPE is set on the element. The RHYTHM is a separate, deliberate rule
   below, because a heading inside a card is not a section heading and must not
   inherit a section heading's air.

   06.08.26 - the previous version declared `margin-block: var(--head-above)
   var(--head-below)` and then `margin: 0` two lines later. The shorthand wins,
   so the rhythm was computed and thrown away and EVERY heading on the page
   shipped jammed against its own paragraph. That is the standing lesson in a
   different costume: an earlier round satisfied a heading check by taking the
   space off all of them, and this rule then made it permanent. Levelling down
   is never the fix (slots.json _house_global). ──────────────────────────── */
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  text-transform: var(--display-case);
  letter-spacing: var(--display-track);
  line-height: 1.1;
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); letter-spacing: .02em; }

/* Homepage content headings share the What We Do scale. The hero and footer
   keep their own display systems. */
main .sec > .shell > h1 { font-size: var(--fs-h2); }

/* A section heading belongs to what FOLLOWS it: real air underneath, more air
   above (rk:heading-rhythm). Set ONCE on the shared rule, never per section
   (rk:global-rules-live-on-the-base). */
.sec :is(h1, h2, h3),
.band__in :is(h1, h2, h3) {
  margin-block: var(--head-above) var(--head-below);
}
/* a heading that OPENS its block has nothing above it to relate to - the
   section's own padding is its air */
.sec :is(h1, h2, h3):first-child,
.band__in :is(h1, h2, h3):first-child { margin-top: 0; }
/* a heading inside a card or a control keeps the same relationship at card
   scale: still more above than below, never zero below */
.plate h3, .faq summary h3, .sent h3, .ftr h4 { margin-block: 0 var(--head-below); }
p  { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }

.shell { max-width: var(--shell); margin-inline: auto; padding-inline: var(--pad-gutter); }
.shell--hero { max-width: var(--shell-hero); }
.nowrap { white-space: nowrap; }

.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 90;
  background: var(--brand-deep); color: var(--paper);
  border-radius: var(--r-control);
  padding: 12px 18px; font: 600 var(--fs-small) var(--font-body);
}
.skip:focus { left: 0; }

:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--brand-deep);
  outline-offset: 2px;
}
.on-deep :where(a, button, summary, input, textarea):focus-visible {
  outline-color: var(--link-on-deep);
}

/* ── scroll reveal: SHIPPED REVEALED, so the page is complete with JS off ─ */
[data-reveal] { opacity: 1; }
.js [data-reveal] {
  opacity: 0; transform: translateY(var(--reveal-rise));
  transition: opacity var(--reveal), transform var(--reveal);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }
/* once settled the transition is REMOVED, or it hijacks every later hover */
.js [data-reveal].is-settled { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ══════════ THE ONE BUTTON SHAPE ══════════
   SYMMETRICAL: every corner matches every other corner, and it echoes the
   header's curvature one step tighter (--r-control under --r-chrome). The
   chamfer this build first shipped with is gone from every control - a cut
   corner on a button reads as a rendering fault (rk:button-symmetry). ───── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font: var(--display-weight) var(--fs-btn)/1 var(--font-display);
  text-transform: uppercase; letter-spacing: .04em;
  min-height: var(--ctl-h); padding: 0 26px;
  border: 0; cursor: pointer; text-decoration: none;
  /* A PUCK OF THE SAME GLASS AS THE HEADER. Blurred, saturated backdrop; a
     bright top lip and a soft inner floor for thickness; a two-layer shadow so
     it sits above the page. The tint is high because a control has to hold its
     label over any surface it lands on - the header can afford 26%, a button
     cannot. */
  background: color-mix(in srgb, var(--btn-bg) 95%, transparent);
  color: var(--btn-ink);
  -webkit-backdrop-filter: blur(var(--puck-blur)) saturate(var(--puck-sat));
          backdrop-filter: blur(var(--puck-blur)) saturate(var(--puck-sat));
  border-radius: var(--r-control);
  box-shadow: var(--shadow-puck), var(--puck-sheen);
  transition: background var(--snap), transform var(--snap), box-shadow var(--snap);
}
.btn:hover {
  background: color-mix(in srgb, var(--btn-bg-hover) 94%, transparent);
  transform: translateY(-2px);
  box-shadow: var(--shadow-puck-lift), var(--puck-sheen);
}
.btn:active { transform: translateY(0); }
.btn--lg { min-height: var(--ctl-h-lg); }
.btn--ghost {
  background: transparent; color: var(--ink); box-shadow: none;
  border: 1px solid var(--rule-hair);
}
.btn--ghost:hover { border-color: var(--brand-deep); color: var(--brand-deep); background: transparent; }

/* ══════════ SIGNATURE: the lockout-tag call plate ══════════
   A die-cut steel tag: radiused at every corner exactly as a real lockout tag
   is, so it takes --r-signature at the same step as every other control. The
   house conic shine runs as its ring at inset 0, so the animated edge cannot
   seam (rk:button-effect-edge-coverage). ───────────────────────────────── */
@property --ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

.tag {
  position: relative; display: inline-flex; isolation: isolate;
  text-decoration: none; color: var(--on-deep);
  min-height: var(--ctl-h);
  border-radius: var(--r-signature);
  box-shadow: var(--shadow-puck);
  transition: transform var(--snap), box-shadow var(--snap);
}
.tag:hover { box-shadow: var(--shadow-puck-lift); }
.tag__ring {
  position: absolute; inset: 0; z-index: 0;
  border-radius: inherit;
  /* METALLIC GREEN, not a pale wash (Josh 07.08.26: "like ironman metallic
     green"). A metal flake reads as a dark body, a fast saturated ramp, a hot
     specular core and a fast fall away - so the sweep runs brand -> accent ->
     a bright core -> back down, over a deep brand body rather than a grey one.
     Every stop stays under 85% alpha so it reads as a SHINE travelling across
     the edge rather than a solid coloured bar pinned to it
     (rk:no-button-edge-bar). */
  background: conic-gradient(from var(--ang),
      transparent 0deg,
      color-mix(in srgb, var(--brand) 34%, transparent) 24deg,
      color-mix(in srgb, var(--accent-2) 68%, transparent) 46deg,
      color-mix(in srgb, var(--brand-soft) 82%, transparent) 56deg,
      color-mix(in srgb, var(--accent-2) 68%, transparent) 66deg,
      color-mix(in srgb, var(--brand) 34%, transparent) 88deg,
      transparent 114deg, transparent 360deg),
    color-mix(in srgb, var(--brand-deep) 58%, transparent);
  animation: angspin var(--spin-ring);
}
@keyframes angspin { to { --ang: 360deg; } }
.tag__face {
  position: relative; z-index: 1; margin: 1px;
  display: inline-flex; align-items: center; gap: 11px;
  padding: 0 22px;
  border-radius: inherit;
  background: color-mix(in srgb, var(--steel-800) 88%, transparent);
  background-image: var(--texture-plate-dark);
  -webkit-backdrop-filter: blur(var(--puck-blur)) saturate(var(--puck-sat));
          backdrop-filter: blur(var(--puck-blur)) saturate(var(--puck-sat));
  box-shadow: var(--puck-sheen);
  font: var(--display-weight) var(--fs-btn)/1 var(--font-display);
  text-transform: uppercase; letter-spacing: .03em;
}
.tag svg { width: 17px; height: 17px; flex: none; color: var(--brand); }
.tag:hover { transform: translateY(-2px); }
.tag:hover .tag__face { background-color: var(--steel-700); }
@media (prefers-reduced-motion: reduce) { .tag__ring { animation: none; } }

/* ══════════ HEADER ══════════ a CENTRED masthead: the logo also leads the
   hero, so it is centred here too, with the nav items centred beneath it
   (slots:header house rule). ─────────────────────────────────────────────── */
.hdr {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  /* DETACHED is the fleet default: a floating bar inset from the edges with
     curved corners, never edge-to-edge (slots:header). The green glass opacity
     is unchanged - it is only detached and rounded. */
  background: transparent; border: 0; backdrop-filter: none;
  padding-inline: var(--hdr-inset); padding-top: var(--hdr-inset);
  color: var(--on-deep); pointer-events: none;
}
.hdr__bar {
  position: relative;
  pointer-events: auto;
  max-width: var(--hdr-max-w); margin-inline: auto;
  /* REAL GLASS, not a translucent rectangle. Three things do the work that a
     lower opacity alone never did: a THIN tint, a DEEP blur, and a saturation
     lift, so the photograph behind it reads as refracted rather than dimmed.
     The gradient plus --glass-sheen give the pane a bright top lip and a soft
     inner floor, which is what makes it read as having thickness.
     --hdr-tint ramps with scroll (site.js): glass over the hero photograph,
     filling to solid brand green by the time the near-white sections arrive
     underneath it, because chrome still has to be legible down there. The ring
     is an INSET shadow, not a border, so the panel stays exactly the 96px the
     house spec fixes. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--on-deep) 12%, transparent) 0%,
      transparent 52%),
    color-mix(in srgb, var(--feature-chrome) var(--hdr-tint-rest), transparent);
  -webkit-backdrop-filter: blur(var(--hdr-blur)) saturate(var(--hdr-sat));
          backdrop-filter: blur(var(--hdr-blur)) saturate(var(--hdr-sat));
  border-radius: var(--r-chrome);
  box-shadow: var(--shadow-plate), var(--glass-sheen);
  overflow: hidden;
}
/* the collapsed mobile nav panel is part of the same pane, so it takes the same
   glass rather than a solid slab hanging off a transparent bar */
@media (max-width: 900px) {
  .nav { -webkit-backdrop-filter: blur(var(--hdr-blur)) saturate(var(--hdr-sat));
                 backdrop-filter: blur(var(--hdr-blur)) saturate(var(--hdr-sat)); }
}
.hdr__in {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 22px;
  min-height: var(--hdr-bar-h); padding-block: 8px;
}
/* ── THE INK, not the glass ───────────────────────────────────────────────
   The pane is identical the whole way down the page. Only what sits ON it
   changes, and only when the thing behind it is light: near-white nav text on
   26% glass over a white section measures 1.36:1, and holding it would mean
   taking the tint to ~80%, which is the solid bar this design replaced.
   The logo swaps with it - the on-dark mark has a WHITE bolt that would
   disappear against pale glass (rk:logo-knockout-band-only: use the client's
   real variant, never recolour the mark). */
.hdr__bar.is-on-light { color: var(--ink); }
.hdr__bar.is-on-light :is(.nav a, .nav span) { color: var(--ink); }
.hdr__bar.is-on-light :is(.nav a, .nav span):hover { color: var(--brand-deep); }
.hdr__bar.is-on-light .burger span { background: var(--ink); }
.hdr__bar.is-on-light .burger { border-color: color-mix(in srgb, var(--ink) 34%, transparent); }
.hdr__logo img { transition: opacity var(--snap); }
.hdr__logo .hdr__logo--light { position: absolute; opacity: 0; }
.hdr__bar.is-on-light .hdr__logo .hdr__logo--dark { opacity: 0; }
.hdr__bar.is-on-light .hdr__logo .hdr__logo--light { opacity: 1; }
.hdr__logo { position: relative; }

/* the scroll-progress rule fills LEFT TO RIGHT. Positioned INSIDE the panel,
   not stacked under it: in flow it added its own 2px to the bar and the panel
   measured 98px against a house spec of 96 (slots.json
   header.reference_measurements). Chrome height is a stated number now, so
   nothing may quietly add to it. */
.hdr__rule {
  position: absolute; left: 0; bottom: 0;
  height: 2px; background: var(--brand); width: 0;
  transition: width var(--snap-slow);
}
.hdr__logo {
  display: flex; align-items: center; justify-content: center; flex: none;
  /* the mark shrinks on a small phone, the TAP TARGET does not */
  min-height: 44px; min-width: 44px;
}
/* rendered artwork keeps visible clearance top and bottom in both states
   (rk:header-logo-clearance) */
.hdr__logo img { width: auto; height: 28px; filter: var(--shadow-drop-contact); }

.hdr__row { display: flex; align-items: center; gap: 26px; }
/* the LINKS are centred on the bar, not the container that holds them */
.nav {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; gap: 26px; align-items: center;
}
.nav a, .nav span {
  /* PURE WHITE, not --on-deep (Josh 07.08.26). --on-deep carries a green tint
     that read as "light green" against the glass. The phone plate keeps its own
     colour; this is the nav only. */
  font: 600 .82rem/1 var(--font-display); text-transform: uppercase;
  letter-spacing: .09em; text-decoration: none; color: var(--paper);
  padding: 10px 0; position: relative; transition: color var(--snap);
}
/* every nav item is the SAME colour: one odd-coloured link reads as a bug,
   not as emphasis (slots:header house rule). The unbuilt item is distinguished
   by having no href and no pointer, never by going grey. */
.nav .is-unbuilt { cursor: default; }
.nav a::after, .nav span::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 2px;
  height: 2px; background: var(--brand); transition: right var(--snap);
}
.nav a:hover, .nav span:hover { color: var(--paper); }
.nav a:hover::after, .nav span:hover::after { right: 0; }

.burger {
  display: none; width: 46px; height: 46px;
  background: transparent; border: 1px solid color-mix(in srgb, var(--on-deep) 34%, transparent);
  border-radius: var(--r-control); cursor: pointer;
  flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  transition: border-color var(--snap);
}
.burger span { display: block; width: 20px; height: 2px; background: var(--on-deep); transition: transform var(--snap), opacity var(--snap); }
.burger:hover { border-color: var(--link-on-deep); }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 900px) {
  /* one centred row on a phone: menu left, the logo centred, the call plate */
  .hdr__in { justify-content: space-between; gap: 10px; padding-block: 0; }
  /* static so the collapsed nav panel positions against the HEADER and spans
     the full bar, not against the row that only wraps the call button */
  .hdr__row { flex: none; position: static; }
  /* relative, NOT static: the conic ring is inset:0 on this element, so a
     static position sends it out to the fixed header and it paints the bar */
  .hdr__call { position: relative; right: auto; top: auto; transform: none; }
  .hdr__call:hover { transform: translateY(-2px); }
  .hdr__logo img { height: 26px; }
  .burger { display: flex; }
  .hdr__call .tag__face { padding: 0 14px 0 24px; font-size: .9rem; }
  .hdr__call svg { width: 15px; height: 15px; }
  .nav {
    /* transform:none, or the desktop rule's translateX(-50%) still shifts the
       full-width panel half its own width off the bar */
    position: absolute; top: 100%; left: 0; right: 0; transform: none;
    flex-direction: column; align-items: stretch; gap: 0;
    background: color-mix(in srgb, var(--feature-chrome) var(--hdr-tint-rest), transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--on-deep) 18%, transparent);
    max-height: 0; overflow: hidden; visibility: hidden;
    transition: max-height var(--snap-slow), visibility 0s linear var(--snap-slow);
  }
  .nav.is-open { max-height: 70vh; visibility: visible; transition-delay: 0s; }
  .nav a, .nav span {
    padding: 17px var(--pad-gutter);
    border-top: 1px solid color-mix(in srgb, var(--on-deep) 14%, transparent);
    text-align: center;
  }
  .nav a::after, .nav span::after { left: 50%; right: 50%; bottom: 8px; }
  .nav a:hover::after, .nav span:hover::after { left: 30%; right: 30%; }
}
@media (max-width: 380px) {
  .hdr__logo img { height: 22px; }
  .hdr__call .tag__face { padding: 0 11px 0 22px; font-size: .8rem; letter-spacing: .02em; }
}

/* ══════════ HERO ══════════ SPLIT-PLATE.
   Every real client photo is portrait (0.46 to 0.77), so there was never a
   landscape frame to crop to - forcing one is what cut the ute off at both
   ends. The variant is chosen to fit the assets that exist (slots:hero): on a
   phone the viewport is portrait too, so the photo runs full-bleed and nothing
   is lost; from 900px up it becomes a PLATE on the right at its own aspect,
   with the content beside it and the brand centred across the whole hero.
   Fills the FIRST VIEWPORT to the pixel, pancake border showing as the ~3px
   sliver (rk:hero-viewport-fold). ─────────────────────────────────────────── */
.hero {
  position: relative; overflow: hidden;
  min-height: calc(100vh - var(--fold-rule));
  min-height: calc(100dvh - var(--fold-rule));
  display: flex; flex-direction: column;
  padding-top: var(--hdr-h);
  color: var(--on-deep);
  text-align: center;
  background: var(--steel-950);
}
.hero__img { position: absolute; inset: 0; }
.hero__img img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.hero__scrim { position: absolute; inset: 0; background: var(--hero-scrim); }

.hero__in {
  position: relative; flex: 1;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: var(--hero-gap);
  padding-block: clamp(30px, 5vh, 66px);
}
/* centred on the HERO, measured on the drawn ink, not the element box */
.hero__brand { display: flex; justify-content: center; width: 100%; }
/* THE HERO WORDMARK, set rather than placed (Josh 06.08.26). Type at this size
   holds its edge at any viewport where a raster mark would soften, and it lets
   the two-tone of the real logo carry through: the mark is green letters with a
   WHITE bolt, so the wordmark answers it with white leading and the light green
   closing. Weight and tracking are pulled tighter than the body display setting
   because a wordmark is a lockup, not a heading. */
.hero__wordmark {
  margin: 0; text-align: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.1rem, 1.1rem + 4.4vw, 4.6rem);
  line-height: .96;
  letter-spacing: -.015em;
  text-transform: uppercase;
  color: var(--paper);
  text-shadow: var(--shadow-text);
  text-wrap: balance;
}
.hero__wordmark em { font-style: normal; color: var(--brand); }
.hero__body {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--hero-gap); width: 100%;
}

/* every service point sits on ONE line at desktop: the columns size to their
   content so the two-word entries are not forced to wrap while their siblings
   sit comfortably (slots:hero) */
.svcs {
  /* 2xN (gate item 2). Four auto columns measure ~858px: with the plate gone
     the hero column is the full 1080px shell, so four genuinely FIT at desktop
     instead of only appearing to by overflowing on both sides. Two below. */
  display: grid; grid-template-columns: repeat(2, auto);
  justify-content: center;
  gap: clamp(15px, 2.4vh, 26px) clamp(22px, 2.8vw, 40px);
  margin: 0; padding: 0; text-align: left;
}
.svcs li {
  display: flex; align-items: center; gap: 11px; list-style: none;
  font: 500 .93rem/1.3 var(--font-body);
  text-shadow: var(--shadow-text-sm);
}
.svcs li span { white-space: nowrap; }
.svcs svg {
  width: 22px; height: 22px; flex: none; color: var(--brand);
  filter: var(--shadow-drop-icon);
}
.cta-pair { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }

/* ── the SPLIT: from 900px the portrait photo becomes a plate on the right ── */
/* NO SPLIT AT ANY WIDTH. rk:homepage-hero-fullbleed, Josh 06.08.26: the
   homepage hero is edge-to-edge photography, never a split, a third, a panel or
   an inset. The 05.08.26 "pick a variant that fits your assets" reading produced
   a hero cut into thirds with the photo shoved into one of them, and it was
   rejected outright. The asset constraint did not disappear, it moved to where
   it belongs: the landscape master is now an OUTPAINT of the real frame, not a
   crop of it. Fix the photo, not the layout.
   The grid still may not exceed its container. */
.svcs { max-width: 100%; }
@media (min-width: 1180px) { .svcs { grid-template-columns: repeat(4, auto); } }
@media (max-width: 560px) { .svcs li { font-size: .86rem; } .svcs li span { white-space: normal; } }
/* the header's permanent click-to-call already covers this channel, so the
   hero's own call button is DELETED rather than shrunk (rk:cta-overload-mobile) */
@media (max-width: 900px) { .hero__call { display: none; } }
@media (max-width: 720px) {
  .hero__brand img { width: min(340px, 82%); }
  .cta-pair .btn { width: 100%; }
}
@media (max-height: 700px) {
  .hero__in, .hero__body { gap: clamp(12px, 2.2vh, 22px); }
  .hero__in { padding-block: clamp(14px, 2.4vh, 26px); }
  .hero__brand img { width: min(330px, 54%); }
  .svcs li { font-size: .85rem; }
  .svcs svg { width: 18px; height: 18px; }
}

/* ── INTERIOR HERO ────────────────────────────────────────────────────────
   The H1 LEADS on an internal page (rk:h1-in-hero); the homepage is the
   deliberate exception with its brand mark first. Everything else - full
   height, the scrim, the fold sliver, the CTA pair - is inherited from the
   shared .hero rule above, NOT re-declared here. A page type must inherit a
   global behaviour rather than restate it (rk:global-rules-live-on-the-base):
   restating the height is exactly how a service page once shipped at 631px
   while every other page was 897px. */
.hero h1 { color: var(--on-deep); text-shadow: var(--shadow-text); }
.hero__lede {
  color: var(--on-deep); text-shadow: var(--shadow-text-sm);
  max-width: var(--measure); margin: 0;
}
.hero--interior .hero__body { max-width: 54rem; }
.contact-hero-title {
  display: grid;
  justify-items: center;
  gap: clamp(8px, 1.4vh, 16px);
  margin: 0;
  text-transform: uppercase;
  line-height: .9;
  letter-spacing: -.025em;
}
.contact-hero-title__lead {
  font-size: clamp(3.2rem, 2rem + 4.8vw, 6.6rem);
}
.contact-hero-title__brand {
  font-size: clamp(1.5rem, .85rem + 2.7vw, 4.25rem);
  white-space: nowrap;
}
.contact-hero-title__brand em {
  color: var(--brand);
  font-style: normal;
}
@media (max-width: 430px) {
  .contact-hero-title__lead { font-size: clamp(3rem, 15vw, 4rem); }
  .contact-hero-title__brand { font-size: clamp(1.28rem, 5.7vw, 1.55rem); }
}

/* ── THE ONE EARNED LIST ──────────────────────────────────────────────────
   Five parallel, discrete things a reader ticks off before sending an enquiry.
   It is the single formatted list in this batch by design, so it is styled as a
   real checklist rather than left as browser bullets. The mark takes --brand
   like every other mark on the site. */
.checklist { list-style: none; margin: 0 0 1.05em; padding: 0; display: grid; gap: 12px; }
.checklist li {
  position: relative; padding-left: 32px; color: var(--ink);
}
.checklist li::before {
  content: ""; position: absolute; left: 0; top: .34em;
  width: 16px; height: 9px; border: solid var(--brand);
  border-width: 0 0 2px 2px; transform: rotate(-45deg);
}

/* ══════════ PANCAKE ══════════ two rows on desktop, ~140px tall ══════════ */
.pancake {
  position: relative;
  z-index: 10;
  /* a feature surface, not field: a white form on a white page is invisible
     (rk:field-vs-feature). The fold rule is the hi-vis live edge, which is
     exactly where the fluoro still earns its place: on dark plate. */
  background: var(--feature-chrome);
  background-image: var(--texture-plate-dark);
  border-top: var(--fold-rule) solid var(--brand);
  padding-block: 22px;
  color: var(--on-deep);
  box-shadow: var(--shadow-plate);
  overflow: visible;
}
/* the same glint language as the FAQ items */
.pancake::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(116deg, transparent 40%,
              color-mix(in srgb, var(--brand-soft) 15%, transparent) 50%, transparent 60%);
  background-size: 220% 100%; background-position: 180% 0;
  transition: background-position var(--glint);
}
.pancake:hover::after { background-position: -80% 0; }
.qf__grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px; }
.pancake .f--wide { grid-column: span 3; }
.qf__err { margin: 8px 0 0; color: var(--brand-deep); font-size: var(--fs-small); font-weight: 600; }
.qf__chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--field); border: var(--border-plate);
  border-radius: var(--r-field);
  padding: 5px 6px 5px 5px; font-size: var(--fs-small); color: var(--graphite);
}
.chip img { width: 30px; height: 30px; object-fit: cover; }
.chip button {
  all: unset; width: 22px; height: 22px; flex: none; cursor: pointer;
  display: grid; place-items: center; color: var(--graphite);
  border: 1px solid var(--rule-hair); border-radius: var(--r-field);
  transition: color var(--snap), border-color var(--snap);
}
.chip button:hover { color: var(--brand-deep); border-color: var(--brand-deep); }

/* ONE font family across the whole form - placeholders, the option list and
   the buttons (slots:pancake). Two faces inside one form reads as two forms. */
.qf, .qf input, .qf textarea, .qf .btn, .qf .ms > summary, .qf .ms__opt, .qf .upload {
  font-family: var(--font-body);
}
.qf .btn { letter-spacing: .02em; font-weight: 700; }

input, textarea {
  width: 100%; min-height: var(--ctl-h); padding: 0 13px;
  /* a light brand tint, never white-on-white (slots:pancake) */
  background: var(--feature-tint); color: var(--ink);
  border: 1px solid var(--rule-hair); border-radius: var(--r-field);
  font: 400 .95rem var(--font-body);
  transition: border-color var(--snap);
}
textarea { min-height: 110px; padding: 12px 13px; resize: vertical; line-height: 1.5; }
input::placeholder, textarea::placeholder { color: var(--ink-quiet); }
input:focus, textarea:focus { outline: 0; border-color: var(--brand-deep); }
.f { display: flex; }
/* the visually-hidden label must NOT be stretched to the field width: it is
   absolutely positioned, so a 100% width sends it off the page and extends the
   document's horizontal scroll area on every desktop viewport. */
.f > :not(.sr):not(.hp) { width: 100%; }
/* the button flips to its on-deep fill here: --brand-deep on the deep-green
   pancake would sink into the band. Same shape, same face, declared pair. */
.pancake .btn { width: 100%; background: var(--btn-bg-on-deep); color: var(--btn-ink-on-deep); }
.pancake .btn:hover { background: var(--btn-bg-on-deep-hover); }

/* ── SERVICE: multi-select, never a single-choice control. A job is rarely one
      service (slots:pancake + slots:cta house rule). Opens DOWNWARD, shows
      every option at once with no inner scrollbar, closes on click-away and
      Escape, stays open while ticking, and the trigger reports the live
      selection so nothing has to be reopened to check it. ───────────────── */
.ms { position: relative; }
.ms > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: var(--ctl-h); padding: 0 13px;
  background: var(--feature-tint); color: var(--ink-quiet);
  border: 1px solid var(--rule-hair); border-radius: var(--r-field);
  font: 400 .95rem/1.2 var(--font-body);
  transition: border-color var(--snap);
}
.ms > summary::-webkit-details-marker { display: none; }
.ms:hover > summary, .ms[open] > summary { border-color: var(--brand-deep); }
.ms.has-pick > summary { color: var(--ink); }
.ms__chev {
  width: 9px; height: 9px; flex: none;
  border-right: 2px solid var(--brand);
  border-bottom: 2px solid var(--brand);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--snap);
}
.ms[open] .ms__chev { transform: rotate(-135deg) translate(-3px, -3px); }
.ms__panel {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30;
  background: var(--field-2); border: 1px solid var(--rule-hair);
  border-radius: var(--r-card); box-shadow: var(--shadow-plate);
  padding: 8px; display: grid; gap: 2px; min-width: 250px;
}
.ms__opt {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  border-radius: var(--r-field);
  padding: 9px 10px; font-size: .93rem; color: var(--ink);
  transition: background var(--snap);
}
.ms__opt:hover { background: var(--field-3); }
.ms__opt input { position: absolute; opacity: 0; width: 0; height: 0; }
/* square box because the behaviour is multi-select, styled through every state
   rather than left browser-default */
.ms__box {
  width: 18px; height: 18px; flex: none; position: relative;
  border: 1px solid var(--graphite); border-radius: var(--r-field);
  background: var(--field-2);
  transition: background var(--snap), border-color var(--snap);
}
.ms__opt:hover .ms__box { border-color: var(--brand-deep); }
.ms__opt input:checked + .ms__box {
  background: var(--brand-deep); border-color: var(--brand-deep);
}
.ms__opt input:checked + .ms__box::after {
  content: ""; position: absolute; left: 5px; top: 1px; width: 5px; height: 10px;
  border: solid var(--paper); border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.ms__opt input:focus-visible + .ms__box { outline: 3px solid var(--brand-deep); outline-offset: 2px; }

@media (max-width: 900px) {
  .qf__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pancake .f--wide { grid-column: span 2; }
  /* phone pancake = the core six controls only (rk:pancake-form-spec item 7) */
  .pancake .only-wide { display: none; }
}

/* ── a real UPLOAD control, not a bare button labelled Photos ─────────── */
.upload {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: var(--ctl-h); padding: 0 14px; cursor: pointer;
  background: var(--feature-tint); color: var(--graphite);
  border: 1px dashed var(--brand); border-radius: var(--r-control);
  font-size: .92rem; text-align: center;
  transition: background var(--snap), border-color var(--snap), color var(--snap);
}
.upload:hover { background: var(--field-2); border-color: var(--brand-deep); color: var(--ink); }
.upload svg { width: 18px; height: 18px; flex: none; color: var(--brand); }
.upload--lg { min-height: 76px; }
.upload__txt { overflow: hidden; text-overflow: ellipsis; }
.upload.has-files { border-style: solid; color: var(--ink); }

/* a slow, subtle pulse so the submit draws the eye without shouting */
@keyframes submit-pulse {
  0%, 84%, 100% { box-shadow: var(--shadow-plate); }
  92% { box-shadow: var(--shadow-plate), 0 0 0 6px color-mix(in srgb, var(--brand) 22%, transparent); }
}
.btn--pulse { animation: submit-pulse var(--pulse-submit); }
@media (prefers-reduced-motion: reduce) { .btn--pulse { animation: none; } }

/* ══════════ SECTIONS ══════════
   THE FIELD LADDER, in the order the page walks it. The classes are named for
   the step they ARE: the old set had a `.sec--paper` that rendered --field-2
   while a --paper token meant pure white, which is exactly the kind of drift
   that makes the next session guess (rk:page-field). ────────────────────── */
.sec { padding-block: var(--pad-section); position: relative; }
.sec--field      { background: var(--field); }    /* #FFFFFF, the base */
.sec--step       { background: var(--field-2); }  /* first near-white step */
.sec--step-deep  { background: var(--field-3); }  /* second step down */
/* the brand-tinted step: it exists so WHITE cards have something to sit on and
   differ in value from (slots:services house rule) */
.sec--tint       { background: var(--feature-tint); }

/* full-bleed photo band. A solid section sits on BOTH sides of every one of
   these (rk:no-adjacent-photo-bands) - asserted by scripts/build.js. */
/* rk:photo-band-parity - every photo band on the page is ONE height, set here
   on the shared rule rather than left to fall out of how long each section's
   copy happens to be (rk:global-rules-live-on-the-base). About ran 559px against
   Where We Work's 429px because About carries a third paragraph. Matched UP to
   the taller, and the content centres inside it so neither band has to grow. */
.sec--photo {
  background: var(--steel-950); overflow: hidden; color: var(--on-deep);
  min-height: var(--band-min-h);
  display: flex; flex-direction: column; justify-content: center;
}
/* rk:content-column-alignment. Making the band a column flex container to
   centre its copy vertically turned .shell into a flex ITEM, and an auto
   CROSS-axis margin on a flex item makes it shrink to content and centre
   itself - so both photo bands were 780px wide at x=330 while every other
   section was 1180px at x=130, indenting their copy 200px at every viewport.
   width:100% restores the normal shell behaviour: fill, cap at --shell, centre.
   On the shared rule, so any band added later inherits it. */
.sec--photo > .shell { width: 100%; }
.sec--photo h2 { color: var(--on-deep); }
.band__img { position: absolute; inset: -2% 0; }
.band__img img { width: 100%; height: 104%; object-fit: cover; object-position: 50% 46%; }
.band__scrim { position: absolute; inset: 0; background: var(--band-scrim); }
.band__in { position: relative; }
.sec--photo::before, .sec--photo::after {
  content: ""; position: absolute; left: 0; right: 0; height: 6px;
  background: var(--hazard); opacity: .45;
}
.sec--photo::before { top: 0; }
.sec--photo::after { bottom: 0; }

/* body copy is ONE left-stacked column: unequal columns read as a mistake
   (slots:intro-1 + slots:intro-2 house rule) */
.lede { max-width: var(--measure); }

/* ── THE INTRO H1 IS ITS OWN ROW (Josh 07.08.26, third pass on this) ──────
   In the MB Air Conditioning reference the heading is a full-width row and the
   photograph begins BELOW its baseline. It runs on past the text column to the
   right, but nothing collides, because the image simply starts underneath it.

   I had read "spills over" as OVERLAP and left the figure starting level with
   the heading, which printed the last word across a switchboard. The heading
   now sits above the grid entirely, so it gets the whole shell to be one line
   in and the photo is untouched below it. No block, no halo, no overlap - none
   of which were ever needed once the heading was in the right place. */
.sec > .shell > h1 { white-space: normal; }
@media (min-width: 900px) {
  .sec > .shell > h1 { white-space: nowrap; }
}
.split__text { min-width: 0; }

/* text | figure rows: the TEXT drives the row height and the photo fills it,
   so the two columns end level (rk:text-image-balance) */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: stretch; }
.split__text { align-self: center; }
.split .fig {
  position: relative; min-height: 340px; margin: 0;
  border-radius: var(--r-plate); overflow: hidden;
}
.split .fig img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%; }
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; gap: 28px; }
  .split .fig { min-height: 0; }
  .split .fig img { position: static; height: auto; aspect-ratio: 4 / 3; }
}

/* 8 service plates -> 4 + 4, never an orphan row (rk:balanced-grids).
   They sit DIRECTLY under the section heading: no image band in between
   (slots:services house rule). */
.grid8 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 1040px) { .grid8 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .grid8 { grid-template-columns: 1fr; } }

.plate {
  position: relative; background: var(--paper); border: var(--border-plate);
  border-radius: var(--r-card); overflow: hidden;
  padding: 24px 22px;
  transition: border-color var(--snap), transform var(--snap), box-shadow var(--snap);
}
/* the edge run: --brand, like every other mark on the site. A hover rule in a
   secondary green reads as a near-miss, not as variety - secondaries belong on
   surfaces, not on marks (rk:marks-take-the-primary). */
.plate::before {
  content: ""; position: absolute; left: 0; top: 0; width: 0; height: 3px;
  background: var(--brand); transition: width var(--snap-slow);
}
.plate:hover { border-color: var(--brand); transform: translateY(-3px); box-shadow: var(--shadow-plate); }
.plate:hover::before { width: 100%; }
.plate svg {
  display: block; width: 28px; height: 28px; color: var(--brand);
  margin-bottom: var(--head-above-card);
}
.plate p { font-size: .94rem; color: var(--graphite); margin: 0; }

/* ══════════ GALLERY ══════════ 3 real job photos, 3 across, no orphan row */
/* MOSAIC: unequal tiles, varied aspect, and NO words over the photos - a
   gallery shows the work (slots:gallery house rule). */
/* Three real photos, so the repeating unit is a WIDE feature over two squares:
   2 columns x 2 rows, every cell filled, no orphan row at any width. Unequal
   tiles and varied aspect is the point; a lone tile beside a hole is not. */
.gallery {
  display: grid; gap: 16px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: var(--gal-row);
}
/* MOSAIC, balanced at five tiles (rk:balanced-grids): a wide feature across
   the top, then two rows of two. Every cell filled, no orphan row. */
.gallery figure:nth-child(1) { grid-column: 1 / span 2; grid-row: span 1; }
@media (max-width: 720px) {
  .gallery { grid-template-columns: 1fr; }
  .gallery figure:nth-child(1) { grid-column: 1; }
}
.gallery figure {
  margin: 0; position: relative; overflow: hidden; border: var(--border-plate);
  border-radius: var(--r-card);
  /* the tile itself responds, not only the photo inside it */
  transition: border-color var(--snap), transform var(--snap), box-shadow var(--snap);
}
.gallery figure:hover {
  border-color: var(--brand); transform: translateY(-3px);
  box-shadow: var(--shadow-plate);
}
.gallery img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--snap-slow);
}
.gallery figure:hover img { transform: scale(1.04); }


/* ══════════ FAQ ══════════ dark cards on the light field, accent ONLY on the
   toggle (rk:faq-tinted). The expand affordance sits on the RIGHT and animates
   so it visibly invites a click (slots:faqs house rule). ────────────────── */
.faq { display: grid; gap: 12px; }
.faq details {
  /* THE LOGO'S GREEN (Josh 06.08.26), one relative deeper. --brand itself is
     #67A051, and near-white copy on it measures 2.5:1 - a fail. --brand-deep is
     the same hue at 4.9:1, so the cards read unmistakably as his green AND stay
     legible. Five cards at full --brand would also bring back the "the whole
     thing is green" note from 05.08.26. */
  position: relative; background: var(--brand-deep);
  background-image: var(--texture-plate-dark),
                    linear-gradient(90deg, var(--brand-deep) 0%, var(--feature-faq-deep) 100%);
  border: 1px solid color-mix(in srgb, var(--brand) 52%, transparent);
  border-radius: var(--r-card); overflow: hidden;
  box-shadow: var(--shadow-puck);
  transition: border-color var(--snap), background-color var(--snap), transform var(--snap);
}
/* DARKER on hover and open, never lighter (Josh 07.08.26). Going up to --brand
   put near-white copy on a mid green at 2.5:1; --feature-chrome is the deep
   green the buttons already use and lands the body copy at 8.5:1. */
.faq details:hover, .faq details[open] {
  border-color: var(--brand); background-color: var(--feature-chrome);
  background-image: var(--texture-plate-dark),
                    linear-gradient(90deg, var(--feature-faq-deep) 0%, var(--feature-chrome) 100%);
  box-shadow: var(--shadow-puck-lift);
}
.faq details:hover { transform: translateY(-2px); }
/* the glint: a fluoro sweep across the card, immediate on pointer entry */
.faq details::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(116deg, transparent 40%,
              color-mix(in srgb, var(--on-deep) 26%, transparent) 50%, transparent 60%);
  transform: translateX(-100%); transition: transform var(--glint);
}
.faq details:hover::after { transform: translateX(100%); }
.faq summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 19px 22px;
}
.faq summary::-webkit-details-marker { display: none; }
/* the control itself responds, not just the card around it */
.faq summary { transition: background var(--snap), color var(--snap); }
.faq summary:hover { background: color-mix(in srgb, var(--on-deep) 8%, transparent); }
.faq summary:hover h3 { color: var(--paper); }
.faq summary h3 {
  font-family: var(--font-body); font-weight: 600; font-size: 1.02rem;
  text-transform: none; letter-spacing: 0; line-height: 1.35; color: var(--on-deep);
}
.tog {
  width: 30px; height: 30px; flex: none; position: relative;
  border: 1px solid color-mix(in srgb, var(--on-deep) 62%, transparent);
  border-radius: var(--r-field);
  transition: background var(--snap), transform var(--snap-slow);
}
.tog::before, .tog::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%); background: var(--on-deep);
}
.tog::before { width: 13px; height: 2px; }
.tog::after { width: 2px; height: 13px; transition: opacity var(--snap); }
/* the invitation: the toggle swells on hover, then rotates to a minus on open */
.faq details:hover .tog { background: color-mix(in srgb, var(--on-deep) 22%, transparent); transform: scale(1.14); }
.faq details[open] .tog { background: color-mix(in srgb, var(--on-deep) 22%, transparent); transform: rotate(90deg); }
.faq details[open] .tog::after { opacity: 0; }
.faq__body { display: grid; grid-template-rows: 1fr; }
.js .faq__body { grid-template-rows: 0fr; transition: grid-template-rows var(--snap-slow); }
.js .faq details[open] .faq__body { grid-template-rows: 1fr; }
.faq__body > div { overflow: hidden; }
.faq__body p { padding: 0 22px 21px; margin: 0; color: var(--on-deep); max-width: var(--measure); }

/* ══════════ QUOTE ══════════ */
/* THE FORM STARTS AT THE FIRST LINE OF THE COPY (Josh 06.08.26, asked twice).
   Offset by exactly one H2 line plus the heading's own bottom margin, computed
   from the same tokens the heading uses, so the two can never drift apart. */
.quote { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: start; }
@media (min-width: 901px) {
  .quote > .card { margin-top: calc(var(--fs-h2) * 1.1 + var(--head-below)); }
}
@media (max-width: 900px) { .quote { grid-template-columns: 1fr; gap: 30px; } }
.card {
  background: var(--feature-card); border: 1px solid var(--brand);
  border-radius: var(--r-card);
  padding: var(--card-pad); box-shadow: var(--shadow-plate);
}
.card .qf { display: grid; gap: 12px; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 420px) { .pair { grid-template-columns: 1fr; } }
.card .btn { width: 100%; }

.contact-lines { display: grid; gap: 12px; margin: 30px 0 0; padding: 0; list-style: none; }
.contact-lines li {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: 15px;
  align-items: center;
  min-height: 46px;
  color: var(--ink);
}
.contact-lines svg {
  width: 46px;
  height: 46px;
  box-sizing: border-box;
  padding: 10px;
  color: var(--brand-deep);
  background: color-mix(in srgb, var(--brand) 12%, var(--field));
  border: 1px solid color-mix(in srgb, var(--brand) 42%, transparent);
  border-radius: var(--r-control);
  box-shadow: var(--shadow-contact);
}
@media (max-width: 430px) {
  .contact-lines li { grid-template-columns: 42px minmax(0, 1fr); gap: 12px; }
  .contact-lines svg { width: 42px; height: 42px; padding: 9px; }
}
/* colour and underline come from the shared contact-detail rule at the top of
   this file, so the number in the list and the same number in the paragraph
   beside it can never drift apart again */

/* ══════════ SENT ══════════ the success card replaces its OWN form in place */
.sent {
  display: grid; gap: 14px; padding: 30px 26px;
  background: var(--field-2); border: var(--border-live);
  border-radius: var(--r-card);
}
.pancake .sent { padding: 20px 22px; }
.sent__tick { width: 46px; height: 46px; color: var(--brand-deep); }
.sent__tick path { stroke-dasharray: 34; stroke-dashoffset: 34; animation: draw var(--draw-tick) forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } }
.sent h3 { font-size: var(--fs-h3); }
.sent p { margin: 0; color: var(--graphite); }
.sent .tag { align-self: start; }
@media (prefers-reduced-motion: reduce) { .sent__tick path { animation: none; stroke-dashoffset: 0; } }

/* ══════════ FOOTER ══════════ */
.ftr {
  position: relative; background: var(--steel-950); color: var(--on-deep);
  background-image: var(--texture-plate-dark);
  padding-block: clamp(44px, 4.6vw, 68px) 22px; overflow: hidden;
}
.ftr__in { position: relative; z-index: 2; padding-bottom: clamp(40px, 4vw, 64px); }
.ftr__cols { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.15fr; gap: 34px; }
@media (max-width: 940px) { .ftr__cols { grid-template-columns: 1fr 1fr; row-gap: 40px; } }
@media (max-width: 560px) { .ftr__cols { grid-template-columns: 1fr; } }
.ftr__logo { width: auto; height: 46px; margin-bottom: 18px; }
.ftr__legalcopy { color: var(--on-deep-soft); font-size: .86rem; margin: 0; text-shadow: var(--shadow-text-ftr); }
.ftr h4 {
  font-size: .78rem; letter-spacing: .14em; color: var(--brand);
  margin: 0 0 16px; text-shadow: 0 1px 4px var(--nle-ink);
}
.ftr ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.ftr li {
  display: flex; gap: 10px; align-items: flex-start;
  color: var(--on-deep-soft); font-size: .93rem; text-shadow: 0 1px 4px var(--nle-ink);
}
.ftr li svg { width: 15px; height: 15px; color: var(--brand); flex: none; margin-top: 4px; }
.ftr .dot { width: 5px; height: 5px; background: var(--brand); flex: none; margin-top: 8px; }
.ftr a { color: var(--on-deep-soft); text-decoration: none; transition: color var(--snap); }
.ftr a:hover { color: var(--on-deep); }
.ftr__tel { border-bottom: 1px solid var(--brand); padding-bottom: 1px; }

/* The Monster wordmark: their real traced mark laid on the ground plane. It
   sits FULLY in frame (never clipped at either side), reads as GROUNDED rather
   than squashed, and fades in vertically - near-invisible at the top of the
   footer, fully present at its base (slots:footer house rule). */
.ftr__mark {
  position: absolute; left: 50%; bottom: 2%; z-index: 1;
  width: min(1180px, 92vw); transform: translateX(-50%);
  pointer-events: none; user-select: none;
  perspective: 1200px;
  /* STRONG gradient: barely there at the letter tops, full at their base, and
     the transition runs UP BEHIND the footer columns rather than finishing
     below them (slots:footer house rule) */
  -webkit-mask-image: var(--wordmark-fade);
          mask-image: var(--wordmark-fade);
}
.ftr__mark img {
  width: 100%; height: auto; opacity: .30;
  transform: rotateX(46deg);
  transform-origin: 50% 100%;
}

.legal {
  position: relative; z-index: 2;
  border-top: 1px solid color-mix(in srgb, var(--on-deep) 12%, transparent);
  padding-block: 20px; font-size: .83rem; color: var(--on-deep-soft);
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  text-shadow: 0 1px 4px var(--nle-ink);
}

/* ── the icon sprite: defined ONCE, referenced with <use> everywhere ─────── */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.svcs svg, .plate svg, .ftr li svg, .contact-lines svg, .tag svg {
  fill: none; stroke: currentColor;
}

/* The Web Genie widget appends its own link as a direct child of <footer>,
   BELOW the legal bar, at a 41x20 hit area - under the WCAG 2.2 AA 24px floor
   on a phone. The page that hosts it gives it a real tap target; the footer's
   own padding-bottom gives it clear space without fighting its inline margin. */
.ftr > #cr-footer-link {
  position: relative; z-index: 2;
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding-inline: var(--pad-gutter);
  text-shadow: 0 1px 4px var(--nle-ink);
}
