/* Space Heroes — responsive layer.

   The design guide already specifies these breakpoints; they were simply never
   built. Before this file the site shipped with zero width-based media queries:
     readme.md - "Two-column marketing rows collapse to one at 1000px;
                  nav collapses to a full-screen cosmic sheet at 1080px."
                 "The phone number is never hidden behind a menu above mobile."
                 "4px spacing base. Controls 58 / 48 / 44px; nothing tappable
                  under 44px on mobile."

   Why !important: every layout value in the templates lives in an inline
   style attribute, which outranks any stylesheet rule. These overrides can all
   drop the flag once the templates move from inline styles to classes. */

:root {
  --bp-nav: 1080px;     /* inline nav -> sheet */
  --bp-stack: 1000px;   /* two-column marketing rows -> one column */
  --bp-mobile: 640px;   /* phone tier: chrome shrinks, labels drop */
}

/* ============================================================
   0 - THE DISPLAY FONT'S STAND-IN, MEASURED AGAINST THE REAL ONE

   PageSpeed put desktop CLS at 0.379 and named one element for 0.364 of it:
   the hero artwork column. The artwork is not the cause. It sits in a grid
   with align-items:center opposite the headline, so when the headline changes
   height the artwork slides, and the shift gets recorded against whichever box
   moved furthest.

   The headline changes height because of the font. --font-display reads
   "American Captain", "Anton", "Arial Narrow", sans-serif, and American
   Captain does not exist: no @font-face anywhere in the repo, and the design
   system's own readme still asks for the .otf files to be dropped into
   assets/fonts/. So the headline is set in Anton, which arrives from Google
   with display=swap, and until it lands the browser uses whatever comes next
   in the stack.

   That "whatever" is the problem. Arial Narrow is close enough by luck on a
   Mac or a Windows machine. Lighthouse runs on Linux, which has no Arial
   Narrow, so it falls through to sans-serif, and sans-serif is 30% wider than
   Anton. Measured at 100px on the hero headline: Anton 2355px, sans-serif
   3349px. A headline that wide wraps to more lines, and when Anton finally
   swaps in, every line below it jumps.

   Measured wrapped heights for the hero H1, Anton against each stand-in:

     container    Anton    this face    sans-serif    Arial Narrow
     620px        451px    451px        721px         541px
     900px        270px    270px        451px         361px
     480px        541px    541px        811px         721px

   size-adjust was swept rather than derived, because what matters is not
   matching the advance width, it is breaking the line in the same place. The
   headline wraps identically to Anton at every one of ten container widths
   from 420px to 1100px anywhere in 71.4% to 72.2%; 71.8% is the middle of
   that range, so a font-metric difference on some other machine has room to
   move before it changes a line break. The two overrides then reproduce
   Anton's line box exactly, 151px at line-height:normal and 100px, against
   115px for bare sans-serif.

   All of it came from measuring both faces in a browser, not from a table.
   Re-measure if Anton is ever swapped for the real American Captain.

   The body font was checked the same way and left alone: Figtree is within
   0.6% of sans-serif and its wrapped heights match at every width tested.
   ============================================================ */

/* Anton itself, served from this repo rather than fetched from Google.

   The face below is the stand-in for the moment before Anton lands. Once the
   hero stopped waiting for the runtime, that moment became visible on the
   most important text on the page, and a stand-in that matches Anton's
   metrics cannot also match its look: reproducing the advance width of a
   heavy condensed face using Arial means shrinking Arial to 71.8%, so the
   headline reads thin and small and then snaps. The fix is not a better
   stand-in, it is for Anton to already be here.

   12 KB for the latin subset, preloaded from the static head, so it arrives
   with the stylesheets instead of after a round trip to fonts.googleapis.com
   and another to fonts.gstatic.com. Anton is OFL licensed; self-hosting is
   what that licence is for. Refreshing it means re-downloading the subset
   Google serves and re-measuring the stand-in below against it. */
/* block, not swap. swap paints the stand-in the moment the first frame is
   ready and replaces it whenever the font turns up, and that replacement is a
   layout shift however small the gap: Anton starts downloading at 19ms and
   PageSpeed still caught two shifts of 0.078 against this h1, 0.156 of a 0.162
   CLS. block asks for the opposite deal, up to three seconds of invisible
   headline in exchange for never showing the wrong one.

   That is a bad trade for a font three round trips away and a good one for
   this file: same origin, 12 KB, preloaded from the static head, cached for a
   year. If it ever fails to arrive the headline is blank for three seconds and
   then falls to the stand-in below, which is worse than a swap and is the
   price of the bet. Move it back to swap if the font stops being preloaded. */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("/assets/fonts/anton-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Anton Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica");
  size-adjust: 71.8%;
  ascent-override: 164%;
  descent-override: 46%;
  line-gap-override: 0%;
}

/* Slotted directly after Anton, so it is what the swap period actually uses.
   American Captain stays at the front: it costs nothing while it is missing,
   and the day the files land it should win.

   !important for the reason given below about inline styles, plus one of its
   own: ds-base.js appends the design system's stylesheet to the head at
   runtime, which lands it after this file and hands the cascade back to the
   token definition. Without the flag this override silently does nothing,
   which is exactly how it behaved on the first attempt. */
:root {
  --font-display: "American Captain", "Anton", "Anton Fallback", "Arial Narrow", sans-serif !important;
}

/* ============================================================
   1 - TABLET AND BELOW: the nav becomes a sheet
   The .sh-nav-inline class is added by sh-nav.js. Gating on it means that if
   the script fails to load the original nav stays visible and usable rather
   than disappearing behind a button that was never built.
   ============================================================ */

.sh-navtoggle { display: none }

@media (max-width: 1080px) {
  header nav.sh-nav-inline { display: none !important }

  .sh-navtoggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-left: auto;
    margin-right: 0;
    flex: 0 0 44px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--action-ghost-border);
    color: var(--sh-white);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
  }
  .sh-navtoggle:hover { background: var(--action-ghost-hover-bg); color: var(--text-highlight); box-shadow: inset 0 0 0 1px var(--border-hairline-strong) }
  .sh-navtoggle svg { width: 22px; height: 22px }
}

/* ============================================================
   2 - THE SHEET ITSELF
   "a full-screen cosmic sheet" - cosmos gradient, star texture, blurred floor.
   ============================================================ */

.sh-sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  padding: calc(var(--utility-bar-height) + var(--space-6)) var(--gutter) var(--space-10);
  background: var(--sh-gradient-cosmos-deep);
  overflow-y: auto;
  overscroll-behavior: contain;
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base);
}
.sh-sheet[data-open='true'] {
  visibility: visible;
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-out), visibility 0s;
}
.sh-sheet__head {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-10);
}
.sh-sheet__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--action-ghost-border);
  color: var(--sh-white);
  cursor: pointer;
}
.sh-sheet__close:hover { background: var(--action-ghost-hover-bg); color: var(--text-highlight) }
.sh-sheet__close svg { width: 22px; height: 22px }
.sh-sheet__nav {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.sh-sheet__nav a {
  display: flex;
  align-items: center;
  min-height: 56px;
  padding: 0 var(--space-5);
  border-radius: var(--radius-pill);   /* pill for anything clickable, like the header nav */
  font-family: var(--font-display);
  font-size: var(--text-display-3);
  line-height: var(--leading-heading);
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--sh-white);
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.sh-sheet__nav a:hover { background: var(--action-ghost-hover-bg) }
.sh-sheet__nav a[aria-current='page'] { color: var(--text-signal) }
.sh-sheet__foot {
  position: relative;
  margin-top: auto;
  padding-top: var(--space-8);
  display: grid;
  gap: var(--space-3);
}
.sh-sheet__rule {                        /* the Arc rule closes the sheet, as it closes heroes */
  height: 6px;
  margin-bottom: var(--space-6);
  border-radius: var(--radius-pill);
  background: var(--sh-gradient-arc);
}

/* ============================================================
   3 - TWO-COLUMN MARKETING ROWS COLLAPSE AT 1000px
   The templates write these inline as `minmax(0,1.1fr) minmax(0,.9fr)` etc.
   23 other grids already use auto-fit and need no help.
   ============================================================ */

/* Both spellings are needed: the source templates write `minmax(0,1.1fr)`, but
   the DC runtime re-serialises the style attribute to `minmax(0px, 1.1fr)`
   (space, and 0 grows a unit), and the attribute selector matches whichever
   string is actually on the element at the time. */
@media (max-width: 1000px) {
  [style*='grid-template-columns:minmax(0,'],
  [style*='grid-template-columns: minmax(0,'],
  [style*='grid-template-columns:minmax(0px,'],
  [style*='grid-template-columns: minmax(0px,'] { grid-template-columns: minmax(0, 1fr) !important }

  [style*='grid-template-columns:1fr 1fr'],
  [style*='grid-template-columns: 1fr 1fr'] { grid-template-columns: 1fr !important }
}

/* ============================================================
   4 - PHONE TIER
   ============================================================ */

/* The emergency strip is height:38px + white-space:nowrap + overflow:hidden, so
   its own phone number gets silently cut off once the viewport is narrower than
   the line. It is hidden below 640px (see the phone tier), but it is still on
   screen above that, so it needs to wrap there rather than clip. */
@media (max-width: 1080px) {
  header > div:first-child {
    height: auto !important;
    min-height: var(--utility-bar-height);
    padding: var(--space-2) var(--space-4) !important;
    white-space: normal !important;
    text-align: center;
    line-height: var(--leading-body-tight);
  }
}

@media (max-width: 560px) {
  /* Three office blocks side by side inside a 335px phone column is 100px each,
     which breaks "400 Slaughter Rd, Suite 308" across four lines. Two-up, and
     the third wraps under. */
  .sh-footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)) !important }
}

@media (max-width: 640px) {
  /* Emergency strip off on phones. The number it carries is not lost: the bar
     directly below shows it at 24px, centred, one tap — which is what the guide
     asks for ("the phone number is never hidden behind a menu"). */
  header > div:first-child { display: none !important }

  header > div:nth-child(2) {
    gap: var(--space-3) !important;
    padding-left: var(--space-4) !important;
    padding-right: var(--space-4) !important;
  }
  header > div:nth-child(2) img { height: 46px !important }

  /* Keep the number - the guide is explicit that it never hides behind the
     menu - but drop the "24/7 dispatch" label that sits above it, and centre
     what is left in the bar on both axes. Absolute rather than auto margins:
     the logo and the toggle are different widths, so auto margins centre the
     number between them, which is 24px off the bar's real centre.
     !important because the size lives in an inline style attribute on the <b>,
     which outranks any stylesheet rule - without it this does nothing. */
  header > div:nth-child(2) { position: relative }
  header a[href^='tel:'] {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  header a[href^='tel:'] small { display: none }
  header a[href^='tel:'] b { font-size: var(--text-display-4) !important; line-height: 1 }

  .sh-sheet__nav a { font-size: var(--text-display-4) }
}

/* Below ~380px the centred number runs into the logo — the number holds its
   size and its centre, so the logo is what gives way. */
@media (max-width: 380px) {
  header > div:nth-child(2) img { height: 38px !important }
}

/* Very narrow: the CTA in the bar squeezes out the logo and the number, and
   it leaves no free space for the toggle's auto margin, so the toggle spills
   past the padding. It stays reachable in the sheet and in the hero.
   The button is wrapped in a runtime host element, so match on the descendant
   rather than on the wrapper's tag name. */
@media (max-width: 460px) {
  header > div:nth-child(2) > x-import,
  header > div:nth-child(2) > .sh-btn,
  header > div:nth-child(2) > *:has(> .sh-btn),
  header > div:nth-child(2) > *:has(.sh-btn) { display: none !important }
}

/* ============================================================
   5 - TYPE OVER SECTION ARTWORK
   `background-size: cover` scales the artwork to fill the box, so the crop
   follows the box's aspect. On a phone these sections are narrow and very tall,
   which zooms the art enormously and pushes whatever sits at the anchor edge
   straight under the copy. The guide's answer to type over imagery is a scrim,
   so image-backed sections get one below the tablet tier.
   ============================================================ */
@media (max-width: 1000px) {
  section[style*="assets/bg"]::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: var(--scrim-full);
  }
  section[style*="assets/bg"] > * { position: relative; z-index: 1 }
}

/* ---------------------------------------------------------------------------
   The Explore column, on touch.

   base.css gives every footer link min-height:44px below 1080px, which is the
   guide's tap-target minimum and is correct. The list it applies to has grown
   from five destinations to eight over the life of this site — About, Blog and
   Contact all arrived later — and eight 44px rows plus their gaps is a 422px
   tower next to three columns that are 75 to 110px tall.

   Two columns rather than a shorter tap target: the 44px is an accessibility
   floor, not a style, so the list wraps instead of the links shrinking. Four
   rows lands it at roughly the height of the columns beside it.
   --------------------------------------------------------------------------- */
@media (pointer: coarse), (max-width: 1080px) {
  /* The three office blocks stay three across here — 3 x 170px plus their gaps
     is 574px against a 706px rail, so they fit and stacking them would only
     make the footer taller. They go two-up on the phone tier below. */

  .sh-explore {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-5);
  }
}

/* ---------------------------------------------------------------------------
   Print / Save-as-PDF.

   The scroll reveal writes `opacity: 0; transform: translate(0, 26px)` as
   INLINE styles, and an inline style outranks any ordinary rule. support.js
   already ships a print baseline, but it only neutralises CSS animations and
   transitions — it cannot reach these. Without the override below, everything
   that has not crossed its play line prints blank, which on a fresh load is
   the entire document past the first screenful.
   --------------------------------------------------------------------------- */
@media print {
  section, footer, section *, footer * {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ---------------------------------------------------------------------------
   Card hover.

   The tokens for this state already existed and were simply never wired up:
   --surface-glass-strong, --border-hairline-strong and --shadow-card-lifted sit
   in the design system next to the resting values. This uses those, plus the
   button's own timing (--dur-fast / --ease-out), so a card hover speaks the
   same language as a button hover instead of inventing a second one.

   WHY THE CARD ITSELF DOES NOT LIFT. The scroll reveal animates these very
   cards, and GSAP writes BOTH `transform` and `translate: none` to them as
   inline styles — inline wins, so a CSS lift on the card would either be
   ignored or, forced with !important, would fight the reveal mid-animation and
   snap the card to its final position while the pointer is over it. The icon
   inside is untouched by GSAP, so that is what moves. The card carries the
   change in depth and light; the illustration carries the motion.

   background and box-shadow need !important because the resting values are
   inline style attributes on each card.

   hover:hover keeps all of this off touch screens, where :hover latches after
   a tap and leaves the card stuck in its hovered state.
   --------------------------------------------------------------------------- */
.sh-card {
  transition: background-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.sh-card img,
.sh-card svg {
  transition: transform var(--dur-base) var(--ease-launch);
}

@media (hover: hover) and (pointer: fine) {
  .sh-card:hover {
    background: var(--surface-glass-strong) !important;
    box-shadow: inset 0 0 0 1px var(--border-hairline-strong),
                var(--shadow-card-lifted) !important;
  }
  /* the spot illustrations are drawn to be looked at — let them answer */
  .sh-card:hover img,
  .sh-card:hover svg {
    transform: translateY(-6px) scale(1.06);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sh-card, .sh-card img, .sh-card svg { transition: none }
  .sh-card:hover img, .sh-card:hover svg { transform: none }
}

/* ---------------------------------------------------------------------------
   NAV DROPDOWNS — added with the commercial pages (Heating, Cooling, Service
   Area, Financing).

   Bri, 5 Aug: "service page design to retain the tabs for Heating & subpages,
   Cooling & subpages, Financing, Service Area & each city page." The prototype
   nav had none of that structure — it listed the brand pages and offered no
   route to a service, a city or Financing at all. Twenty-seven destinations do
   not fit on one bar, so the four that have children became groups.

   Only above --bp-nav. Below it sh-nav.js takes the same markup into the
   full-screen sheet, where the groups render as headings instead.

   Hover AND focus-within: a dropdown that only answers the mouse is a dropdown
   a keyboard cannot reach. The parent stays a real link to the hub page, so
   the group is never a dead end for anyone who just clicks it.
   --------------------------------------------------------------------------- */
.sh-navgroup { position: relative; display: inline-flex }
.sh-navgroup__chev { transition: transform var(--dur-fast, .15s) var(--ease-out, ease) }

.sh-navmenu {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translate(-50%, -6px);
  min-width: 248px;
  padding: 10px;
  display: grid;
  gap: 2px;
  border-radius: var(--radius-lg);
  background: rgba(10, 17, 48, .97);
  box-shadow: inset 0 0 0 1px var(--border-hairline), 0 24px 60px rgba(3, 11, 28, .6);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-fast, .15s) var(--ease-out, ease),
              transform var(--dur-fast, .15s) var(--ease-out, ease),
              visibility 0s linear var(--dur-fast, .15s);
  z-index: 50;
}
/* the bridge: without it the 8px gap between the tab and the panel closes the
   menu the moment the pointer crosses it */
.sh-navmenu::before {
  content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px;
}
.sh-navmenu a {
  display: block;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: var(--text-body-sm);
  line-height: 1.35;
  text-decoration: none;
  transition: background-color var(--dur-fast, .15s) var(--ease-out, ease);
}
.sh-navmenu a:hover,
.sh-navmenu a:focus-visible { background: var(--action-ghost-hover-bg); color: var(--sh-yellow) }

@media (min-width: 1081px) {
  .sh-navgroup:hover .sh-navmenu,
  .sh-navgroup:focus-within .sh-navmenu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translate(-50%, 0);
    transition-delay: 0s;
  }
  .sh-navgroup:hover .sh-navgroup__chev,
  .sh-navgroup:focus-within .sh-navgroup__chev { transform: rotate(180deg) }

  /* Service Area and About sit near the right edge; pinning their panels to the
     right stops a 248px menu running off the viewport at 1100px. */
  .sh-navgroup:nth-last-child(-n+3) .sh-navmenu { left: auto; right: 0; transform: translate(0, -6px) }
  .sh-navgroup:nth-last-child(-n+3):hover .sh-navmenu,
  .sh-navgroup:nth-last-child(-n+3):focus-within .sh-navmenu { transform: translate(0, 0) }
}

/* Six tabs plus a logo, a phone block and a button do not fit at 1080–1280px.
   The number is the one thing that must never hide (it is in the emergency
   strip above and the sheet below), so the stacked "24/7 dispatch" block folds
   first and the tabs tighten. */
@media (max-width: 1280px) and (min-width: 1081px) {
  .sh-headphone { display: none !important }
  .sh-nav a { padding-left: 9px !important; padding-right: 9px !important; font-size: 15px !important }
}

@media (max-width: 1080px) {
  .sh-navmenu { display: none }
}

@media (prefers-reduced-motion: reduce) {
  .sh-navmenu, .sh-navgroup__chev { transition: none }
}

/* Sheet: group headings, added by sh-nav.js when it finds .sh-navgroup. The
   heading is a toggle button now — it expands its subpages, which are
   collapsed by default so the phone menu lists the sections, not every page. */
.sh-sheet__group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 48px;
  margin: 4px 0 0;
  padding: 8px 18px;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--sh-gold);
}
.sh-sheet__group svg {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  transition: transform var(--dur-fast, .15s) var(--ease-out, ease);
}
.sh-sheet__group[aria-expanded="true"] svg { transform: rotate(180deg) }
.sh-sheet__panel { display: grid }
.sh-sheet__panel[hidden] { display: none }
.sh-sheet__nav a.sh-sheet__child {
  font-size: var(--text-body-lg);
  color: var(--text-body-soft);
  padding-left: 34px;
}
@media (prefers-reduced-motion: reduce) {
  .sh-sheet__group svg { transition: none }
}


/* ---------------------------------------------------------------------------
   SERVICE TILES — the illustrated Heating/Cooling pair.

   These rules used to live in ServiceLanding's own <style>, scoped to
   #services, so the city pages could not reuse them and shipped a flat glass
   card with a coloured bar instead of the tile with the moon illustration.
   Moved here and given a second selector, .sh-tilerow, so any page can lay the
   pair out and get the identical treatment. The #services selector is kept so
   the homepage markup did not have to change.

   The design system's own .sh-tile is a left-aligned card on --surface-card;
   everything below is the homepage's override of it — centred, glass, huge
   display title, artwork overhanging the top edge, and the CTA as a gold pill.
   --------------------------------------------------------------------------- */
#services .sh-tile, .sh-tilerow .sh-tile{overflow:visible;display:flex;flex-direction:column;align-items:center;text-align:center;background:var(--surface-glass);box-shadow:inset 0 0 0 1px var(--border-hairline),var(--shadow-card)}
#services .sh-tile__art, .sh-tilerow .sh-tile__art{height:250px;overflow:visible;margin-bottom:16px}
#services .sh-tile__art img, .sh-tilerow .sh-tile__art img{max-height:none;height:300px;margin-top:-76px;filter:drop-shadow(0 18px 32px rgba(6,24,52,.55))}
#services .sh-tile:hover .sh-tile__art img, .sh-tilerow .sh-tile:hover .sh-tile__art img{transform:translateY(-16px) scale(1.07) rotate(-2deg)}
/* The design system gives every tile a 4px accent bar that wipes in from the
   left on hover (.sh-tile::before, scaleX 0->1). Switched off here, not in the
   DS bundle — that is the client's library and the page already overrides tile
   styles at this level. The tile is not short of hover feedback without it: the
   card lifts 7px into a deeper shadow, the artwork rises and scales, and the
   CTA changes colour and lifts too. */
#services .sh-tile::before, .sh-tilerow .sh-tile::before{content:none}
#services .sh-tile__title, .sh-tilerow .sh-tile__title{font-size:clamp(44px,4.5vw,72px);line-height:.92}
#services .sh-tile__more, .sh-tilerow .sh-tile__more{display:inline-flex;align-items:center;justify-content:center;gap:10px;height:52px;padding:0 30px;border-radius:var(--radius-pill);background:var(--action-primary);color:var(--action-primary-text);font-family:var(--font-display);font-size:1.1875rem;letter-spacing:.03em;text-transform:uppercase;text-decoration:none;box-shadow:var(--shadow-sticker);transition:transform var(--dur-fast) var(--ease-launch),background-color var(--dur-fast) var(--ease-out),box-shadow var(--dur-fast) var(--ease-out)}
#services .sh-tile:hover .sh-tile__more, .sh-tilerow .sh-tile:hover .sh-tile__more{background:var(--action-primary-hover);color:var(--action-primary-text);transform:translateY(var(--lift-hover));box-shadow:var(--shadow-sticker-lg)}

/* the pair itself: two up on desktop, stacked on a phone. The extra top space
   is for the artwork, which deliberately overhangs the card. */
.sh-tilerow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: clamp(20px, 3vw, 40px);
  margin-top: 96px;
}
@media (max-width: 700px) {
  .sh-tilerow { margin-top: 84px }
}

/* ---------------------------------------------------------------------------
   SERVICE ILLUSTRATIONS — the cooling artwork, on the hub cards and in the
   subpage hero.

   The hero becomes two columns where there is room: copy left, illustration
   right. Below 900px the artwork drops under the copy and shrinks, and below
   560px it goes entirely — on a phone the first screen belongs to the headline
   and the two CTAs, not to a picture pushing them under the fold.

   The card plate is a quiet inset behind the illustration so seven cards read
   as one set even though the artworks have different silhouettes and margins.
   --------------------------------------------------------------------------- */
.sh-hero { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(24px, 4vw, 56px) }
.sh-hero__art { align-self: center; justify-self: end; max-width: 420px; width: 100% }
.sh-hero__art img { filter: drop-shadow(0 22px 40px rgba(6, 24, 52, .55)) }

@media (min-width: 900px) {
  .sh-hero { grid-template-columns: minmax(0, 1fr) minmax(0, 400px); align-items: center }
}
@media (max-width: 899px) {
  .sh-hero__art { justify-self: start; max-width: 300px; margin-top: 8px }
}
@media (max-width: 559px) {
  .sh-hero__art { display: none }
}

/* Card artwork. object-fit keeps the seven different aspect ratios on one
   baseline so the titles below them line up across a row. */
.sh-svccard__art { aspect-ratio: 16 / 11 }
.sh-svccard__art img {
  height: 100% !important;
  object-fit: contain;
  padding: 10px;
  transition: transform var(--dur-base) var(--ease-launch);
}
@media (hover: hover) and (pointer: fine) {
  .sh-svccard:hover .sh-svccard__art img { transform: translateY(-6px) scale(1.05) }
}
@media (prefers-reduced-motion: reduce) {
  .sh-svccard__art img { transition: none }
  .sh-svccard:hover .sh-svccard__art img { transform: none }
}

/* ---------------------------------------------------------------------------
   FOOTER COLUMNS — four of them since 6 Aug (Heating, Cooling, Service Area,
   Explore), because the footer is the only place the whole site is reachable
   from one screen: the nav hides thirteen services and ten cities behind
   hover, and a crawler that never hovers needs the flat list.

   Four -> two at 1000px, two -> one at 560px. The brand rail carries the
   address and phone, so it stacks above them rather than beside.
   --------------------------------------------------------------------------- */
/* Brand band across the top, link columns beneath. It was a 240px left rail
   with the columns beside it, which worked at four columns and broke at five:
   Air Quality made a fifth group and Explore dropped to a second row on its
   own. Moving the brand block horizontal gives the columns the full width and
   the row back. */
@media (max-width: 1180px) {
  .sh-footer-cols { grid-template-columns: repeat(3, minmax(0, 1fr)) !important }
}
@media (max-width: 900px) {
  .sh-footer-brand { grid-template-columns: minmax(0, 1fr) !important; gap: 20px !important }
  .sh-footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)) !important }
}
@media (max-width: 520px) {
  .sh-footer-cols { grid-template-columns: minmax(0, 1fr) !important }
}
