/* ==========================================================================
   Breakara Demolition Staten Island
   Breakara family visual system. Inherited from the family's design-reference sibling, which is
   named once, in DESIGN-DNA.md section 1, and deliberately not in bytes this site serves.

   What is inherited is the BRAND: hazard-on-white with a graphite ground,
   one neutral grotesque across everything, 2px buttons, the bordered image
   mat, the '//' page-title motif, the chevron text link, the native FAQ
   disclosure, the hazard copy bar, the graphite footer, the sticky mobile
   call bar.

   V-14 REPAIR (2026-08-16). This header used to claim the family was carried
   partly "by … tick lists" and the file styled a `.list_ok` that rendered on
   zero pages, alongside a complete form stylesheet for a site with no <form>,
   a #get_quote band this site deliberately does not render, and six other
   never-emitted classes. A claim in the build record that is not true of the
   build is worse than a missing one; both the claim and the styles are gone.
   Every selector below now has a rendering path in scripts/build-site.mjs.

   What is NOT inherited is the SITE: this file styles Staten Island's own
   composition — alternating image/text service rows, a display-scale address
   band, and a services index the sibling does not have. Same brand, different
   site. See DESIGN-DNA.md.

   The previous Adova dark system was retired 2026-08-16 and is preserved at
   tag design/adova-dark-v1. Nothing of it survives here: no near-black
   ground, no spruce, no display/body type pairing, no 10px radius, no
   hand-drawn shapes, no SVG step marks.

   Dependency-free: no framework, no plugin CSS, no remote font request.
   ========================================================================== */

/* -------- 0. Type ------------------------------------------------------
   Roboto, self-hosted from this origin. The cluster's face, and the reason it is served from
   here rather than requested from Google is that this site's single-external-origin property
   (the map, and nothing else) is worth more than the convenience: a webfont request would make
   every page depend on a third party to render its own headings, and it would need a second
   entry in the Content-Security-Policy. 100 KB for five weights, swap-displayed so text paints
   immediately in the fallback.

   V1 used the platform system stack. That was defensible on its own and wrong for the cluster:
   the three stronger Breakara properties all set Roboto, so this site rendered the same brand
   in a different voice on every heading. */
@font-face{font-family:Roboto;font-style:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/roboto-300.woff2) format("woff2")}
@font-face{font-family:Roboto;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/roboto-400.woff2) format("woff2")}
@font-face{font-family:Roboto;font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/roboto-500.woff2) format("woff2")}
@font-face{font-family:Roboto;font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/roboto-700.woff2) format("woff2")}
@font-face{font-family:Roboto;font-style:normal;font-weight:900;font-display:swap;src:url(/assets/fonts/roboto-900.woff2) format("woff2")}

/* -------- 1. Tokens ----------------------------------------------------
   RE-POINTED 2026-08-21, V1 to V2 remediation.

   These were not arbitrary before and they are not arbitrary now. They were WRONG, and the
   proof is in this repository rather than in a preference: the dominant non-neutral pixels in
   `assets-src/brand/logo.png` are #343333 and #f6bd19, byte-for-byte the same as the Hub's and
   Manhattan's logos, and the stylesheet declared #2b2e31 and #f2c300. The header served a
   #f6bd19 wordmark on a #f2c300 interface, on every page, since 2026-08-16.

   So the hue is not being changed to match a sibling. It is being corrected to match this
   site's own brand asset, and the cluster alignment falls out of that for free. */
:root {
  --hazard: #f6bd19;
  --hazard-dark: #dfa806;
  --hazard-ink: #7a5f00;      /* the hue made legible as text on white, 5.9:1 */
  --hazard-line: #f7d97e;
  --hazard-tint: #fdf3d8;
  --graphite: #222;
  --graphite-soft: #3d3c3c;
  --steel: #565a5c;
  --muted: #7b7f81;
  --ink: #343333;
  --concrete: #f9f9f9;
  --rule: #e2e2e0;
  --white: #fff;

  /* The family's hazard hatching. One saturated colour, used structurally, and the only place
     it tints a large area is a call-to-action band or the copy bar. */
  --stripes: repeating-linear-gradient(135deg, rgba(0,0,0,.05) 0 14px, rgba(0,0,0,0) 14px 28px);
  --stripes-light: repeating-linear-gradient(135deg, rgba(255,255,255,.06) 0 14px, rgba(255,255,255,0) 14px 28px);

  /* V-03 / V-04 REPAIR (2026-08-16). Two colours in this system depend on the surface
     underneath them rather than on the component, and both had been solved once and then
     re-broken by the next component that landed on a dark ground: --hazard-ink is a light-
     ground colour (5.81:1 on white, 2.61:1 on graphite) and --hazard is a dark-ground one
     (9.10:1 on graphite, 1.67:1 on white). Scoping them per component is what produced
     F-01 and then V-03 — a .tel-link fixed and a .text-btn missed in the same wrapper.
     They are tokens now, re-pointed ONCE per dark surface below. The custom property
     inherits, so every focusable and every chevron link inside those surfaces is correct
     without naming it. Mechanic taken from the family reference, which does the same. */
  --focus: var(--hazard-ink);
  --link-accent: var(--hazard-ink);
  --link-accent-hover: var(--ink);

  --font: Roboto, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;

  --container: 1170px;
  --gutter: 30px;
  --section: 72px;
  --radius: 10px;
  --radius-sm: 3px;

  --shadow-sticky: 0 2px 10px -2px rgba(0, 0, 0, .41);
  --ease: color .2s ease, background-color .2s ease, border-color .2s ease, box-shadow .2s ease, opacity .2s ease;
}

/* -------- 2. Base ------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

/* 17px/1.65 rather than V1's 15px/1.6. The three stronger properties in this cluster all read
   at 17px, and 15px is a 2015 measure for body text on a page a homeowner reads on a phone
   while standing in the room they want gone. */
body {
  margin: 0;
  background: var(--white);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.65;
  color: var(--steel);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  color: var(--ink);
  margin: 0 0 16px;
  line-height: 1.2;
  font-weight: 700;
}
h1 { font-size: clamp(2rem, 4.2vw, 3.1rem); font-weight: 900; letter-spacing: -.02em; }
h2 { font-size: clamp(1.55rem, 2.6vw, 2.15rem); letter-spacing: -.01em; }
h3 { font-size: 1.22rem; }
h4 { font-size: 1.02rem; }

p { margin: 0 0 22px; }
p:last-child { margin-bottom: 0; }

a { color: var(--hazard-ink); text-decoration: none; transition: var(--ease); }
/* V-03/V-04: this used to resolve to --ink on every surface, which meant a link on the hero,
   the closing band or the footer turned near-black the moment it was focused — dark on dark,
   and worse than the state it came from. It follows the surface token, so it is --ink on the
   light ground it was written for and --hazard on the graphite ones. */
a:hover, a:focus { color: var(--link-accent-hover); }

/* B-04 REPAIR (2026-08-16). An inline link inside running prose was separated from the text
   around it by colour alone — rgb(122,99,0) against rgb(86,90,92), 1.23:1, same weight, no
   underline — which fails WCAG 1.4.1 Use of Colour (technique G183 wants 3:1 when colour is the
   only cue). Scoped to unclassed anchors inside a paragraph or list item, so every designed
   control keeps its own treatment: .btn, .text-btn, .tel-inline and .tel-link all carry a class
   and are untouched, and .tel-inline already carries its non-colour cue as weight 700. */
.prose p a:not([class]), .prose li a:not([class]),
.measure p a:not([class]), .measure li a:not([class]) {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

img { max-width: 100%; height: auto; display: block; }

ul { margin: 0 0 25px; padding-left: 20px; }
li { margin-bottom: 9px; }

hr { margin: 45px 0; border: 0; border-top: 1px dotted var(--rule); }

/* Every dark surface in the site, in one place. Anything placed on one of them takes the
   dark-ground values of both surface tokens. */
.hero, .page-head, .cta, .side-panel, .site-footer {
  --focus: var(--hazard);
  --link-accent: var(--white);
  --link-accent-hover: var(--hazard);
}

/* V-04 REPAIR (2026-08-16). The ring was hazard yellow on every surface: 1.67:1 on white,
   1.51:1 on the concrete panels and 1:1 against the primary button, which is filled with the
   same yellow. Non-text contrast needs 3:1, so a keyboard user saw a usable ring only on the
   graphite bands. It now follows the surface token: 5.81:1 on white, 5.24:1 on concrete,
   3.48:1 against the yellow button fill, 9.10:1 on graphite. */
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The hazard-filled controls are the one case a single colour cannot cover, because the ring
   has to separate from the fill AND from the ground, and those grounds are white on eight
   pages and graphite inside the closing band. Two concentric bands do it: ink against the
   yellow fill (8.18:1) and a white halo outside it (13.66:1 on the yellow, 9.44:1 on
   graphite). An outline paints over a box-shadow, so the shadow shows only as the outer 3px. */
.btn:focus-visible,
.primary-nav .nav-cta a:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 0;
  box-shadow: 0 0 0 6px var(--white);
}

/* The skip link is a graphite chip that appears over the top of whatever page it is on, so it
   has the same two-neighbour problem inverted, and takes the same two-band answer: hazard
   against the chip (9.10:1), ink against the page behind it (13.66:1 on white). */
.skip-link:focus-visible {
  outline: 3px solid var(--hazard);
  outline-offset: 0;
  box-shadow: 0 0 0 6px var(--ink);
}

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 20px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100000;
  background: var(--graphite); color: var(--white);
  padding: 12px 20px; font-weight: 700;
}
.skip-link:focus { left: 0; color: var(--white); }

.measure { max-width: 66ch; }
/* The FAQ answers want a little more room than a prose column but must not run the full
   container; the family reference sets the same block at 820px. */
.measure-wide { max-width: 820px; }

/* F-10 REPAIR: the reset was scoped to .footer-address, so the contact page's <address>
   rendered in browser-default italic. The family reference resets it globally. */
address { font-style: normal; }

/* Family motif: small uppercase label above a heading. */
.eyebrow {
  font-size: 12px;
  font-weight: 900;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--hazard-ink);
  margin: 0 0 10px;
}

.section { padding: var(--section) 0; }
.section--tight { padding: 40px 0; }
.section--flush-top { padding-top: 0; }

.section-head { margin-bottom: 34px; }
.section-head h2 { margin-bottom: 10px; }

/* -------- 3. Buttons and links ----------------------------------------- */
.btn {
  display: inline-block;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .02em;
  padding: 13px 30px;
  border-radius: 2px;
  background: var(--hazard);
  color: var(--ink);
  transition: var(--ease);
  text-align: center;
}
/* A button is not a text link, so the generic `a:focus` colour must not reach it: on the
   graphite bands that token resolves to hazard, which would have set a yellow label on the
   yellow fill. Declared BEFORE the hover rule on purpose — equal specificity, so hovering a
   focused button still gets the inverted white-on-ink pair rather than ink on ink. */
.btn:focus { color: var(--ink); }
/* Hover only. V-04: the inversion used to fire on :focus as well, which turned the button ink
   at exactly the moment the focus ring was drawn — and the ring that reads against the yellow
   fill is itself ink, so the indicator vanished into the state it was meant to mark. Focus is
   expressed by the ring; hover is expressed by the fill; neither cancels the other. */
.btn:hover { background: var(--ink); color: var(--white); }

/* Family motif: a "read more" link with a drawn chevron that steps out on hover.
   The sibling uses the same construction; it is one of the clearest family cues.

   V-03 REPAIR (2026-08-16). The colour was hard-coded to --hazard-ink, a light-ground
   olive, and /contact/ puts one of these inside .hero-actions on the graphite-masked hero,
   where it measured 1.37:1 to 3.31:1 composited against the photograph. It reads the
   surface token now, so the hero, the closing band and the footer get the dark-ground
   value without a per-page override — the fix F-01 applied to .tel-link and missed here. */
.text-btn {
  display: inline-block;
  font-weight: 700;
  font-size: 14px;
  color: var(--link-accent);
}
.text-btn::after {
  content: "";
  display: inline-block;
  width: .5em; height: .5em;
  margin-left: .45em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg) translateY(-1px);
  transition: var(--ease), margin-left .2s ease;
}
.text-btn:hover { color: var(--link-accent-hover); }
.text-btn:hover::after { margin-left: .75em; }

.tel-link { font-weight: 700; color: var(--ink); }
.tel-link:hover { color: var(--hazard-ink); }

/* -------- 4. Top utility line ------------------------------------------ */
#top_line {
  height: 30px;
  font-size: 13px;
  border-bottom: 1px solid rgba(0, 0, 0, .1);
  background: var(--white);
  overflow: hidden;
  transition: height .2s ease, opacity .2s ease, visibility .2s ease;
}
#top_line .container { display: flex; align-items: center; justify-content: space-between; height: 30px; }
#tag_line { color: var(--steel); }
#top_links { display: flex; gap: 14px; list-style: none; margin: 0; padding: 0; font-weight: 600; }
#top_links a { color: var(--ink); }
#top_links a:hover { color: var(--hazard-ink); }

/* V-12 REPAIR (2026-08-16). The mobile rule `a[href^="tel:"] { min-height: 44px }` also matched
   this link, which lives in a 30px bar with `overflow: hidden` — so at 768-991 the enlarged
   target was clipped from 44px back to 30px and the rule achieved nothing, while the glyphs
   sat ~4px above the bar's optical centre because the inherited `li { margin-bottom: 9px }`
   was never reset and skewed the flex centring. The link now FILLS the bar instead of trying
   to exceed it: a 30px target that really is 30px, which clears WCAG 2.5.8's 24px floor. The
   comfortable 44px target at those widths is the 54px fixed call bar, which is where the
   enlargement belongs. The reset below is the missing half of the `#top_links` margin reset. */
#top_links { align-self: stretch; }
#top_links li { margin: 0; display: flex; }
#top_links a { display: flex; align-items: center; height: 100%; }
/* The bar clips to animate its height, and an outline is drawn outside the box by definition,
   so a ring with a positive offset would be clipped away on the site's utility-line phone
   link. Drawn inside instead, where the clip cannot reach it. */
#top_links a:focus-visible { outline-offset: -3px; }

/* Collapsed means gone, not merely transparent: an opacity-0 box still holds its link in the
   tab order, so a keyboard user could focus a phone action inside an invisible bar and see a
   focus ring on nothing. `visibility` is discrete, so it flips at the end of the collapse and
   at the start of the expansion, which is what the transition wants either way. */
.site-header.is-stuck #top_line { height: 0; opacity: 0; border-bottom: 0; visibility: hidden; }

/* -------- 5. Header and navigation ------------------------------------- */
/* Sticky, not fixed. A fixed header leaves the flow, so the document has to reserve its
   height from script — and this site's CSP is style-src 'self', which blocks a
   JS-applied inline style outright. Sticky pins the header identically without leaving
   the flow, so there is nothing to reserve and nothing to block. */
.site-header {
  width: 100%;
  background: var(--white);
  position: sticky;
  top: 0;
  z-index: 9999;
}
.site-header.is-stuck { box-shadow: var(--shadow-sticky); }

/* Scroll lock while the off-canvas panel is open. A class, not an inline style, for
   the same CSP reason. */
html.is-locked, html.is-locked body { overflow: hidden; }

/* V-01 REPAIR (2026-08-16). This declared `padding: 14px 0` on the element that also carries
   .container. Both are single-class selectors, so the later shorthand won outright and wiped
   the container's `padding: 0 20px`: computed paddingLeft on .container.header-in was 0px.
   The logo's leftmost pixels touched x=0 and the menu button touched the right edge at every
   width below 1140, while the utility line one row above, the breadcrumb, main and the footer
   all sat at 20px; above 1140 the header content sat 20px OUTSIDE the page's content column.
   Longhand only, so the gutter the container owns is never overwritten by a vertical rhythm
   declaration. The same trap is why the stuck state below is longhand too. */
.header-in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px;
  padding-top: 14px;
  padding-bottom: 14px;
  transition: padding .2s ease;
}
.site-header.is-stuck .header-in { padding-top: 8px; padding-bottom: 8px; }

/* The designed lockup is the whole brand element: the business name lives inside the
   artwork, so nothing is typed beside it.

   Constrained by HEIGHT, not width. The family reference is a 3.9:1 lockup and this one is
   3.2:1, so sizing both to the same width would render this one shorter and it would read
   as a thin strip rather than as the brand. Height is what carries optical weight in a
   header; width follows the aspect ratio, so the mark stays correct if the artwork is ever
   re-cut. */
.brand { display: inline-block; line-height: 0; flex: none; }
.brand-logo {
  display: block;
  height: 46px;
  width: auto;
  max-width: 100%;
  transition: opacity .2s ease;
}
.brand:hover .brand-logo { opacity: .82; }
.site-header.is-stuck .brand-logo { height: 40px; }

/* V-02 REPAIR (2026-08-16). Between 992 and ~1090 this row did not fit, and because nothing
   stopped it wrapping, every two-word item broke across two lines ("Structure / Demolition")
   and the CTA — which computed to `display: inline` with 11px/20px padding — fragmented into
   TWO offset yellow rectangles with the label split across them. `getClientRects().length`
   returned 2 for the pill at client widths 977 and 1045.

   Restoring the container gutter in V-01 takes 40px off the row, so the band would have grown
   rather than shrunk; both had to be fixed together. Three rules make fragmentation
   structurally impossible, then the band below buys the width to honour them:

     nowrap on the list   an item can never drop to a second line
     nowrap on the label  a two-word item can never break inside itself
     inline-block on the  an inline box fragments into one rectangle per line; a block-level
       CTA                box is one rectangle by definition and cannot come apart

   DIVERGENCE FROM THE REFERENCE, STATED. The design-reference sibling avoids this with five nav items
   and no separate header phone (its number lives in the utility line). This site keeps six
   items and the header phone, and the reason is that neither is spare here: the sixth item is
   /services/, the hub the sibling does not have and the parent of every service route in this
   site's breadcrumb trail; and the header phone is the only phone action in the sticky chrome
   above 991, because this site's utility line collapses on scroll rather than compressing.
   Dropping either would cost a real path to save a row that measurement shows fits. The cost
   of keeping them is the sizing band below — a smaller nav between 992 and 1199, which is the
   ordinary way a desktop header handles a narrow desktop. */
.primary-nav ul {
  display: flex; align-items: center; flex-wrap: nowrap; gap: 18px;
  list-style: none; margin: 0; padding: 0;
}
.primary-nav li { margin: 0; }
.primary-nav a {
  color: var(--ink);
  font-size: 14px; font-weight: 500;
  padding: 6px 0;
  position: relative;
  white-space: nowrap;
}
.primary-nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; background: var(--hazard);
  transform: scaleX(0); transform-origin: left; transition: transform .2s ease;
}
.primary-nav a:hover::after,
.primary-nav a[aria-current="page"]::after { transform: scaleX(1); }

.primary-nav .nav-cta a {
  display: inline-block;
  color: var(--ink); background: var(--hazard);
  padding: 11px 18px; border-radius: 2px; font-weight: 700;
}
.primary-nav .nav-cta a::after { display: none; }
.primary-nav .nav-cta a:hover { background: var(--ink); color: var(--white); }

.header-phone {
  font-size: 16px;
  font-weight: 900;
  color: var(--ink);
  white-space: nowrap;
  border-bottom: 3px solid var(--hazard);
  padding-bottom: 2px;
}
.header-phone:hover { color: var(--hazard-ink); }

/* The narrow-desktop band. The row carries a 180px lockup, six nav items, the CTA pill and the
   phone, and the content column is 952px at 992 against 1100px at 1140 and above — so the same
   type scale cannot serve both ends. Everything steps down by one notch here and returns at
   1200, where there is room for it. Measured fit, not estimated: see the repair record. */
@media (min-width: 992px) and (max-width: 1199px) {
  .header-in { gap: 14px; }
  .brand-logo, .site-header.is-stuck .brand-logo { height: 40px; }
  .primary-nav ul { gap: 12px; }
  .primary-nav a { font-size: 13px; }
  .primary-nav .nav-cta a { padding: 10px 13px; }
  .header-phone { font-size: 15px; }
}

.menu-toggle {
  display: none;
  background: none; border: 0; cursor: pointer;
  padding: 10px; color: var(--ink);
  min-width: 44px; min-height: 44px;
}
.menu-toggle span {
  display: block; width: 24px; height: 2px; background: var(--ink); margin: 5px 0;
  transition: background-color .2s ease;
}

/* -------- 6. Off-canvas panel ------------------------------------------
   Closed means display:none, mounted by JS only while in use. A panel that is
   merely translated off-screen keeps its links in the tab order and the
   accessibility tree while reporting itself closed. */
.side-panel {
  display: none;
  position: fixed; inset: 0;
  z-index: 10000;
  background: var(--graphite);
  padding: 22px 0 40px;
  overflow-y: auto;
  opacity: 0;
  transition: opacity .25s ease;
}
/* Mounted by JS only while in use; .is-open then fades it in on the next frame. */
.side-panel.is-mounted { display: block; }
.side-panel.is-open { opacity: 1; }

.side-panel-top { display: flex; justify-content: flex-end; align-items: center; min-height: 46px; }
.panel-close {
  background: none; border: 0; color: var(--white);
  font-size: 30px; line-height: 1; cursor: pointer; padding: 4px 10px;
}
.panel-close:hover { color: var(--hazard); }

.side-panel nav ul { list-style: none; margin: 18px 0 0; padding: 0; }
.side-panel nav li { border-bottom: 1px solid rgba(255, 255, 255, .12); margin: 0; }
.side-panel nav a { display: block; color: var(--white); padding: 14px 0; font-size: 16px; font-weight: 500; }
.side-panel nav a:hover { color: var(--hazard); }
.side-panel nav a[aria-current="page"] { color: var(--hazard); }

.panel-contact { margin-top: 26px; color: rgba(255, 255, 255, .8); }
.panel-contact .eyebrow { color: var(--hazard); }
.panel-contact a { color: var(--white); font-size: 24px; font-weight: 900; border-bottom: 3px solid var(--hazard); }
.panel-contact a:hover { color: var(--hazard); }

/* -------- 7. Hero ------------------------------------------------------
   Staten Island's own composition, not the sibling's. The sibling runs a
   left-aligned statement over a horizontal gradient with a meta strip; this
   is centred over a flat mask, with the address band carrying the weight
   immediately beneath it. Same tokens, same type, different page. */
/* REBUILT 2026-08-22. The old hero was a 480px floor plus 72px of padding at each end, centred
   copy at max-width 760 and the form stacked underneath it, which put roughly 1000px between the
   top of the page and the first thing below the hero. The floor is gone, the padding is roughly
   halved, and the copy and the form are two columns, so the section under the hero now breaks the
   fold on a 1280x800 laptop instead of sitting a scroll below it. */
.hero {
  position: relative;
  overflow: hidden;
  background: var(--graphite);
  display: flex; align-items: center;
}
.hero-media { position: absolute; inset: 0; z-index: 0; }
.hero-media img,
.hero-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: rgba(18, 20, 22, .74);
}
.hero .container { position: relative; z-index: 2; padding-top: 34px; padding-bottom: 34px; }

/* One column until there is genuinely room for two. Below 60rem the form follows the copy, which
   is the same order a screen reader and a keyboard get, so nothing depends on the grid. */
.hero-grid { display: grid; grid-template-columns: 1fr; gap: 1.6rem; align-items: center; }
.hero-copy { color: var(--white); }
.hero-copy h1 {
  color: var(--white);
  font-size: clamp(28px, 3.6vw, 40px);
  font-weight: 900;
  letter-spacing: -.015em;
  line-height: 1.08;
  margin-bottom: 12px;
}
.hero-standfirst {
  font-size: 17px;
  color: rgba(255, 255, 255, .9);
  margin: 0 0 18px;
  max-width: 46ch;
}

/* Trust line. Three short facts, never a badge graphic: a drawn crest asserts a credential the
   way a certificate does, and what stands behind these is an operator statement and nothing more.
   The tick is CSS content so the words stay the words in the markup and in the copy sweep. */
.hero-trust {
  display: flex; flex-wrap: wrap; gap: 8px 18px;
  list-style: none; margin: 0 0 20px; padding: 0;
}
.hero-trust li {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 0;
  font-size: 14px; font-weight: 700; letter-spacing: .01em;
  color: var(--white);
}
.hero-trust li::before {
  content: "\2713";
  color: var(--hazard);
  font-size: 15px; font-weight: 900; line-height: 1;
}

/* Call or type, and the "or" says so. The number is the button's own second line rather than a
   separate link, so the whole control is one tap target at the size a thumb needs. */
.hero-call { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.hero-call-btn {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 1px;
  padding-top: 10px; padding-bottom: 10px;
  line-height: 1.15;
}
.hero-call-kicker { font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; opacity: .82; }
.hero-call-number { font-size: 21px; font-weight: 900; letter-spacing: -.01em; }
.hero-call-or { margin: 0; font-size: 14px; color: rgba(255, 255, 255, .82); }

.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
/* F-01 REPAIR (2026-08-16). This was `.hero-actions .tel-link`, unscoped, and the wrapper was
   reused on the --concrete address band, so the phone number rendered white-on-near-white at
   1.11:1 — invisible, on the primary conversion. White is a hero treatment because only the
   hero has the graphite mask behind it; scope it there. The 404's .error-actions still reuses
   this wrapper on white, so the scope is still load-bearing. */
.hero .hero-actions .tel-link { color: var(--white); align-self: center; }
.hero .hero-actions .tel-link:hover { color: var(--hazard); }
/* Everywhere else the wrapper sits on a light ground and the link keeps the default ink. */
.hero-actions .tel-link { align-self: center; }

/* -------- 8. Interior page head ----------------------------------------
   REMOVED 2026-08-21. `.page-head` was a flat graphite bar and it rendered on
   every interior page. It is replaced by the photographic `.pagehead` in section
   23.12, which carries the same `//` motif at three times the weight. The old
   rules are gone rather than left behind: a stylesheet that keeps the component
   it replaced is a stylesheet nobody can read. */

/* -------- 8b. Breadcrumb (family #position) ----------------------------- */
#position { background: var(--concrete); border-bottom: 1px solid var(--rule); }
#position ol {
  display: flex; flex-wrap: wrap; gap: 8px;
  list-style: none; margin: 0; padding-top: 10px; padding-bottom: 10px;
  font-size: 13px; color: var(--steel);
}
#position li { margin: 0; }
#position li + li::before { content: "/"; margin-right: 8px; color: var(--rule); }
#position a { color: var(--hazard-ink); }
#position a:hover { color: var(--ink); }
#position [aria-current="page"] { color: var(--ink); font-weight: 700; }

/* -------- 9. Address band ---------------------------------------------
   V-14 REPAIR: the family's #get_quote hazard band was styled here and is rendered on no
   page — deliberately, and the reason is recorded in scripts/build-site.mjs at the footer.
   A band the site has decided not to render does not need a stylesheet.

   Staten Island keeps its own strongest argument. The measured position is a
   verified island address in a market whose competitors run from another
   borough or from no address at all; the sibling has no equivalent block.
   Re-expressed in family language — concrete ground, hazard rule, weight-900
   display scale — rather than the retired system's oversized title slot. */
.address-block {
  background: var(--concrete);
  border-left: 6px solid var(--hazard);
  padding: 34px 30px;
}
.address-line {
  display: block;
  font-size: clamp(26px, 4vw, 38px);
  font-weight: 900;
  letter-spacing: -.015em;
  color: var(--ink);
  margin: 0 0 4px;
}
.address-sub { font-size: 17px; font-weight: 700; color: var(--ink); margin: 0 0 16px; }
.address-why { margin: 0; max-width: 62ch; }

/* V-06 REPAIR (2026-08-16). The band's actions borrowed the .hero-actions wrapper, and with it
   `justify-content: center` — a hero treatment. At 1280 the eyebrow, headline, sub-line and
   body all began at x=119 while the two actions floated at x=531…740, unrelated to anything:
   three alignments inside one block. At <=767 the borrowed wrapper switched to
   column/stretch while `.hero-actions .tel-link` kept `align-self: center`, so the number was
   centred above a left-aligned link. This block is left-aligned prose, so its actions are
   left-aligned prose actions — one alignment, at every width. It is also the site's signature
   local module, which is where sloppiness costs most. */
.address-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 24px; margin-top: 20px; }

/* -------- 10. Service rows ---------------------------------------------
   REMOVED 2026-08-21. `.srow` and its four modifiers presented THREE services as
   alternating image/text rows, and the fourth canonical service had no card at
   all. The V2 plan gives all four a page and the home page a four-up grid, so
   the rows render nowhere. Their two genuinely good ideas were kept rather than
   dropped: the bordered image mat lives on in `.figure` and `.card-media`, and
   the alternating beat lives on in `.feature` / `.feature--flip`, which is the
   same composition applied where a page actually has two things to alternate. */

/* -------- 11. Split, figures, bands ------------------------------------ */
.split { display: grid; grid-template-columns: 1fr 1.5fr; gap: 40px; align-items: start; }
.split--stack { grid-template-columns: 1fr; gap: 16px; }

.figure { margin: 0; }
.figure img {
  border: 1px solid var(--rule);
  padding: 4px;
  background: var(--white);
  width: 100%;
}
/* V-07 REPAIR (2026-08-16). This rendered 1100x825 at the full container — 92% of a 900px
   viewport, 23% of the whole /about/ page, and the tallest thing on four pages: taller than
   the 480px hero and taller than the 542px band that was designed to be the biggest element
   there. An inset supporting photograph half again taller than the deliberately full-bleed
   one is an inverted hierarchy. The 4:3 ratio was correct where it came from — .srow-media
   uses it at half width for a 483x362 image — and was reused at full width without being
   re-chosen. Re-chosen here: a 16:9 crop at 760px renders 760x428, which sits below the hero
   and well below the band, and reads as the supporting inset it is. It keeps the family's
   bordered mat and stays flush with the left edge of the copy it supports. */
.figure--contained { max-width: 760px; }
.figure--contained img { aspect-ratio: 16 / 9; object-fit: cover; }
/* Emitted by scripts/build-site.mjs whenever an asset carries a caption. Every caption in
   data/media-registry.json is currently null, so this renders on no page today — it is kept,
   unlike the residue V-14 removed, because it is a live data-driven branch: deleting it would
   either drop a capability the registry documents or leave the build silently ignoring a
   caption someone had written. */
.figure-caption { margin: 10px 0 0; font-size: 13.5px; color: var(--steel); }

.band { position: relative; }
.band img { width: 100%; aspect-ratio: 21 / 9; object-fit: cover; }
.band-caption { margin: 10px 0 0; font-size: 13.5px; color: var(--steel); }

/* -------- 12. Steps ---------------------------------------------------- */
.steps { display: grid; gap: var(--gutter); grid-template-columns: repeat(4, 1fr); }
.step { border-top: 3px solid var(--hazard); padding-top: 16px; }
.step-number {
  display: block;
  font-size: 12px; font-weight: 900; letter-spacing: .18em; text-transform: uppercase;
  color: var(--hazard-ink);
  margin-bottom: 6px;
}
.step h3 { font-size: 17px; margin-bottom: 8px; }
.step p { font-size: 14px; margin: 0; }

/* -------- 13. Callout, info grid, related ------------------------------ */
.callout {
  background: var(--concrete);
  border-left: 3px solid var(--hazard);
  padding: 18px 22px;
  margin: 0 0 25px;
}
.callout p { margin: 0; }

/* V-09 REPAIR (2026-08-16). This was a three-up, and its middle cell reprinted the same
   `<address>` that the location block directly above it already carries, verbatim, under a
   heading that repeats that block's own heading. The restatement is gone; what is left is the
   two facts the page states once. */
.info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px var(--gutter); }
.info-grid p { margin: 0; }

.related { border-top: 1px dotted var(--rule); padding-top: 22px; display: flex; flex-wrap: wrap; gap: 8px 26px; align-items: baseline; }
.related .eyebrow { margin: 0; width: 100%; }

/* -------- 13b. Contact: the call panel and the location split ---------- */
/* Calling is this page's entire job, so the number is set at display scale rather than as a
   line of body copy. The family reference gives its phone the same treatment on its contact
   page; the panel around it is this site's own. */
.call-panel {
  background: var(--concrete);
  border-left: 6px solid var(--hazard);
  padding: 30px;
}
.call-panel .eyebrow { margin-bottom: 6px; }
.phone-xl {
  display: inline-block;
  font-size: clamp(26px, 4vw, 38px);
  font-weight: 900;
  letter-spacing: -.015em;
  color: var(--ink);
  line-height: 1.1;
}
.phone-xl:hover { color: var(--hazard-ink); }
.call-panel-note { margin: 12px 0 0; }

.split--location { grid-template-columns: 1fr 1.2fr; align-items: start; }
.address-block--tight { padding: 26px 24px; }

/* -------- 14. Closing band (family .bg_content) ------------------------ */
.cta {
  background: var(--graphite);
  padding: 76px 0;
  text-align: center;
  color: rgba(255, 255, 255, .88);
}
.cta-inner { max-width: 760px; margin: 0 auto; }
.cta-title { color: var(--white); font-size: 30px; font-weight: 700; margin: 0 0 15px; line-height: 1.25; }
/* Family motif: short rules flanking the band heading. */
.cta-title::before, .cta-title::after {
  content: ""; display: inline-block; width: 34px; height: 2px;
  background: var(--hazard); vertical-align: middle; margin: 0 16px;
}
.cta p { color: rgba(255, 255, 255, .88); max-width: 62ch; margin: 0 auto 25px; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; align-items: center; }
.cta-actions .tel-link { color: var(--white); }
.cta-actions .tel-link:hover { color: var(--hazard); }

/* -------- 15. FAQ ------------------------------------------------------
   Family motif, native disclosure. `<details>`/`<summary>` rather than a scripted accordion,
   so the keyboard handling, the focus behaviour and the expanded state announced to a screen
   reader all come from the element. The chevron is the same drawn mark as .text-btn, rotated,
   which keeps one chevron language across the site.

   V-14 REPAIR (2026-08-16) removed the section that stood here: a complete form stylesheet —
   .form-note, .form-group, label, label .req, .form-control, textarea.form-control, .hp — for
   a site that ships no <form> anywhere, correctly, because leadCapture.destinationEmail is
   null and a form submitting nowhere would be a dead control. The styles shipped regardless. */
.faq-item {
  border-bottom: 1px dotted var(--rule);
  padding: 16px 0;
}
.faq-item summary {
  cursor: pointer; font-weight: 700; color: var(--ink); font-size: 16.5px;
  list-style: none; display: flex; justify-content: space-between; gap: 16px; align-items: flex-start;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: ""; flex: none; width: 9px; height: 9px; margin-top: 6px;
  border-right: 2px solid var(--hazard-dark); border-bottom: 2px solid var(--hazard-dark);
  transform: rotate(45deg); transition: transform .2s ease;
}
.faq-item[open] summary::after { transform: rotate(-135deg); }
.faq-answer { margin-top: 12px; }
.faq-answer p { margin: 0 0 14px; }
.faq-answer p:last-child { margin-bottom: 0; }

/* -------- 16. Map ------------------------------------------------------ */
.map { line-height: 0; border: 1px solid var(--rule); padding: 4px; background: var(--white); }
.map iframe { display: block; width: 100%; aspect-ratio: 16 / 10; border: 0; }
.map--large iframe { aspect-ratio: 16 / 9; }

/* On graphite the white mat reads as a pasted-on panel. Same component, footer surface. */
.footer-map .map { background: transparent; border-color: rgba(255, 255, 255, .22); padding: 3px; }

/* -------- 17. Footer --------------------------------------------------- */
.site-footer {
  background: var(--graphite);
  color: rgba(255, 255, 255, .78);
  padding: 50px 0 30px;
  font-size: 14.5px;
}
.site-footer h3 { font-size: 17px; font-weight: 700; color: var(--white); margin-bottom: 16px; }
.site-footer a { color: rgba(255, 255, 255, .88); }
.site-footer a:hover { color: var(--hazard); }
/* B-01 REPAIR (2026-08-16). The rule directly above is (0,1,1) and beat `.btn { color: var(--ink) }`
   at (0,1,0), so the one filled hazard button that lives inside the footer lost its own label
   colour and rendered near-white on yellow at 1.57:1, on all nine pages at every width — while the
   identical button in the header and the closing band measured 8.18:1. A button carries its own
   ground, so the surface rule for footer TEXT must not reach it. These two declarations give the
   component its own colours back and change nothing else about the footer.
   Third occurrence of this pattern: F-01 fixed .tel-link, V-03/V-04 fixed the surface tokens. */
.site-footer .btn { color: var(--ink); }
.site-footer .btn:hover { color: var(--white); }
.site-footer .eyebrow { color: var(--hazard); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 9px; }

/* V-11 REPAIR (2026-08-16). The three columns ran contact | map | nav, and because the 347x220
   map column ended ~180px short of the ~400px contact column and the two compressed nav lists
   ended higher still, the whole bottom-right of the band was empty — the last thing the eye
   reaches on every page. The family reference does not have the problem because it keeps the
   map underneath the address in one "Where we are" column and puts that column last, so the
   tallest block closes the row. Same arrangement here: conversion | navigation | location.
   The ragged edge now falls where a reader has already stopped looking. */
.footer-top { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gutter); }
.footer-logo { height: 54px; width: auto; margin-bottom: 16px; }
.footer-contact .tel-link { color: var(--white); font-size: 20px; font-weight: 900; }
.footer-contact .tel-link:hover { color: var(--hazard); }
.footer-address { font-style: normal; margin: 0 0 16px; }
.footer-cta { margin: 18px 0 0; }
.footer-nav-cols { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }

.footer-bottom {
  margin-top: 34px; padding-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  display: flex; flex-wrap: wrap; gap: 10px 24px; justify-content: space-between; align-items: center;
}
.footer-bottom ul { display: flex; gap: 18px; }
.footer-bottom li { margin: 0; }

/* Family motif: the hazard copy bar closing every page. */
#copy {
  background: var(--hazard);
  color: var(--ink);
  padding: 12px 0;
  text-align: center;
  font-size: 12.5px;
  font-weight: 500;
}

/* -------- 18. Back to top, call bar ------------------------------------ */
.scrollup {
  position: fixed; right: 16px; bottom: 16px; z-index: 9997;
  width: 42px; height: 42px;
  border: 0; border-radius: 2px;
  background: rgba(0, 0, 0, .6);
  color: var(--white);
  cursor: pointer;
  display: none;
  align-items: center; justify-content: center;
}
.scrollup.is-visible { display: flex; }
.scrollup:hover { background: var(--hazard); color: var(--ink); }

#call_bar { display: none; }

/* -------- 19. Prose and 404 -------------------------------------------- */
/* V-08 REPAIR (2026-08-16). .prose carried no measure, so on /privacy/ and /terms/ the
   paragraphs ran the full container: measured 1100px at 15px on /privacy/ at 1280, which is
   136 characters a line against the 66ch this site sets everywhere else. Legal pages are the
   quiet end of the site, not a different site, so they get the site's own measure. */
.prose { max-width: 66ch; }
.prose h2 { margin-top: 34px; }
.prose h3 { margin-top: 26px; }

/* A phone number written into a sentence is still a phone action to a reader. The build wraps
   the one display form wherever approved copy states it (see linkifyPhone in build-site.mjs),
   so there is no rendering of the number on this site that a visitor cannot tap. It is set as
   a prose link, not as a .tel-link, because it sits inside a line of body copy. */
.tel-inline { font-weight: 700; }

.error-page { padding: 90px 0; text-align: center; }
.error-actions { margin-top: 34px; }

/* Utility classes rather than style attributes. The CSP is style-src 'self', so an
   inline style is not a shortcut here — it is a rule the browser refuses to apply. */
.panel-address { margin-top: 20px; }
.spacer { height: 56px; }

/* -------- 20. Scroll reveal -------------------------------------------- */
.js [data-reveal] { opacity: 0; transform: translateY(18px); transition: opacity .5s ease, transform .5s ease; }
.js [data-reveal].is-in { opacity: 1; transform: none; }

/* -------- 21. Responsive ----------------------------------------------- */
@media (max-width: 991px) {
  .hero { min-height: 420px; }
  .steps { grid-template-columns: repeat(2, 1fr); }
  /* Conversion and location take the row; the two nav lists, which are the shortest blocks,
     drop underneath and span it. `order` rather than a DOM change, because the source order
     is the one a screen reader and a keyboard follow. */
  .footer-top { grid-template-columns: 1fr 1fr; }
  .footer-nav-cols { grid-column: 1 / -1; order: 3; }
  .srow { gap: 30px; }
  /* Scoped to .site-header on purpose. The mobile tap-target rule below matches
     a[href^="tel:"], which is MORE specific than a bare .header-phone class and would
     otherwise resurrect the hidden header phone at ≤767px — crowding the logo and the
     menu button on exactly the widths where the sticky call bar already carries the
     number. Two classes beat one class plus an attribute. */
  .site-header .primary-nav, .site-header .header-phone { display: none; }
  .menu-toggle { display: block; }

  /* F-06 REPAIR (2026-08-16). The nav went touch-only at 991 while the sticky call bar and the
     44px tap-target rule were scoped to 767, leaving a 768–991 dead band — iPad portrait, most
     phones in landscape — with collapsed navigation, no persistent phone action and 24px phone
     links. The family reference keeps both on ONE boundary; this now does too.

     The rule: the width at which navigation collapses is the width at which the persistent
     phone path must appear. There is no width where a visitor has neither. */
  #call_bar {
    display: block;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 9996;
    background: var(--hazard);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .18);
  }
  #call_bar a {
    display: block; text-align: center;
    padding: 14px 16px;
    color: var(--ink); font-weight: 900; font-size: 16px;
  }
  /* Space for the sticky call bar is reserved ONLY when there is a call bar. A Point that has not
     been given its dedicated number renders no bar, and an unconditional 64px would leave a dead
     strip at the bottom of every mobile page and float the scroll-up button above nothing. */
  body.has-call-bar { padding-bottom: 64px; }
  body.has-call-bar .scrollup { bottom: 74px; }

  /* A tappable number should never be a hairline. The exemptions are the controls that set
     their own height and the two that must not be enlarged: a number inside a line of body
     copy (a 44px line-height would tear the paragraph apart) and the utility-line link, whose
     bar is 30px tall and clips — see the V-12 note on #top_links. */
  a[href^="tel:"] { display: inline-block; min-height: 44px; line-height: 44px; font-weight: 700; }
  #call_bar a, .btn[href^="tel:"], #top_links a, .tel-inline { min-height: 0; line-height: inherit; }
  .tel-inline { display: inline; }
  .btn[href^="tel:"] { line-height: 1.4; }
}

@media (max-width: 767px) {
  :root { --section: 44px; --gutter: 24px; }

  body { font-size: 16px; }

  #top_line { display: none; }

  .brand-logo { height: 42px; }
  .site-header.is-stuck .brand-logo { height: 38px; }
  .footer-logo { height: 48px; }

  h1, h2 { font-size: 25px; }
  h3 { font-size: 20px; }
  .cta-title { font-size: 25px; }

  .hero { min-height: 0; }
  .hero .container { padding-top: 52px; padding-bottom: 52px; }

  .page-head { padding: 34px 0 30px; }
  .page-head h1 { font-size: 22px; }

  .srow, .srow--flip, .split, .split--location, .info-grid,
  .footer-top, .footer-nav-cols { grid-template-columns: 1fr; }
  .call-panel { padding: 24px 22px; }
  .srow--flip .srow-media { order: 0; }
  .steps { grid-template-columns: 1fr; }

  .address-block { padding: 26px 22px; }
  /* Stacked, and still one alignment: the actions keep the left edge the copy above them
     sits on rather than stretching or centring. See the V-06 note. */
  .address-actions { flex-direction: column; align-items: flex-start; gap: 14px; }

  .cta { padding: 54px 0; }
  .cta-title::before, .cta-title::after { width: 20px; margin: 0 10px; }

  .map iframe, .map--large iframe { aspect-ratio: 4 / 3; }

  /* A one-line question is a 26px target for a thumb. The padding that carries the row's
     rhythm moves onto the summary, where it is tappable, so the control reaches 44px without
     the block growing. Same construction as the family reference. */
  .faq-item { padding: 6px 0; }
  .faq-item summary { min-height: 44px; padding: 9px 0; }

  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { display: block; width: 100%; }
}

@media (max-width: 480px) {
  .hero-copy h1 { font-size: 26px; }
  .address-line { font-size: 24px; }
  /* The call control keeps its two lines but stops stretching the number past the card edge. */
  .hero-call-btn { width: 100%; align-items: center; }
  .hero-call-number { font-size: 19px; }
}

/* -------- 22. Motion and print ----------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .js [data-reveal] { opacity: 1; transform: none; }
}

@media print {
  .site-header, .site-footer, .scrollup, #call_bar, #copy, .side-panel, #position { display: none; }
  /* Without this, every block still waiting on its IntersectionObserver prints blank. */
  .js [data-reveal] { opacity: 1 !important; transform: none !important; }
  body { color: #000; padding-bottom: 0; }
}

/* ==========================================================================
   23. V2 COMPONENTS — added 2026-08-21, V1 to V2 remediation
   ==========================================================================
   These are the sections the V2 plan asks for and the V1 build had no vocabulary
   for: the four-up service grid, the measured local figures, the process band,
   the price/date columns, and the shared cluster coverage treatment.

   They are written in this site's own language rather than copied from a sibling.
   What is shared with the cluster is the BRAND layer above: the hazard hue, the
   ink, Roboto, the hatching, the card radius. What is this site's own is the
   composition: the address block still leads, the local figures are this
   borough's, and there is no borough switcher in the header.
   ========================================================================== */

/* -------- 23.1 Section heads ------------------------------------------- */
.section--tight { padding-top: 34px; padding-bottom: 34px; }
.section-head { max-width: 62ch; margin: 0 0 38px; }
.section-head h2 { margin-bottom: 10px; }
.section-intro { margin: 0; color: var(--muted); font-size: 1.05rem; }
.section-head--onband h2 { color: var(--white); }
.section-head--onband .section-intro { color: rgba(255, 255, 255, .74); }

/* -------- 23.2 Service cards -------------------------------------------
   Four canonical services, four cards. V1 showed three as alternating rows and
   the fourth as a paragraph on an index page that no longer exists. */
.cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gutter); }
.card {
  display: flex; flex-direction: column;
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  color: inherit;
  transition: var(--ease), transform .2s ease;
}
.card:hover, .card:focus-visible { border-color: var(--hazard); transform: translateY(-3px); }
.card-media { display: block; }
.card-media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.card-body { display: block; padding: 20px 20px 22px; border-top: 3px solid var(--hazard); }
.card-title { display: block; font-size: 1.15rem; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
.card-text { display: block; font-size: .95rem; color: var(--steel); margin-bottom: 14px; }
.card-more {
  display: inline-block; font-size: .82rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--hazard-ink);
}
.card-more::after { content: " \2192"; display: inline-block; transition: transform .2s ease; }
.card:hover .card-more::after { transform: translateX(4px); }

/* -------- 23.3 Feature: image beside text ------------------------------ */
.feature { display: grid; grid-template-columns: 1fr 1fr; gap: 46px; align-items: center; }
.feature--flip { grid-template-columns: 1fr 1fr; }
.feature--flip .figure { order: 2; }
.feature .figure { max-width: none; margin: 0; }
.feature .figure img { aspect-ratio: 4 / 3; }
.feature-text h2 { margin-bottom: 14px; }

/* -------- 23.4 Measured local figures ----------------------------------
   Every tile is a borough count from a named source. The source line is part of
   the component rather than an optional extra, because a figure without one is
   exactly what three earlier sessions on this repository refused to publish. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--gutter); }
.stat {
  background: var(--white);
  border: 1px solid var(--rule);
  border-top: 4px solid var(--hazard);
  border-radius: var(--radius-sm);
  padding: 24px 22px;
}
.stat-figure {
  margin: 0 0 4px;
  font-size: clamp(2.1rem, 3.6vw, 2.9rem);
  font-weight: 900; line-height: 1; letter-spacing: -.02em;
  color: var(--ink);
}
.stat-label { margin: 0 0 12px; font-weight: 700; font-size: .95rem; color: var(--ink); }
.stat-body { margin: 0; font-size: .92rem; color: var(--steel); }
.stats-source { margin: 26px 0 0; font-size: .82rem; color: var(--muted); max-width: 92ch; }

/* -------- 23.5 Process band --------------------------------------------
   The family puts a process on a graphite ground. --hazard-ink is a light-ground
   colour, so both accent tokens are re-pointed once here and every focusable and
   chevron inside inherits the correction. */
.steps-band {
  background: var(--graphite);
  background-image: var(--stripes-light);
  padding: var(--section) 0;
  --link-accent: var(--hazard);
  --link-accent-hover: var(--white);
  --focus: var(--hazard);
}
.steps-band .steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gutter); list-style: none; margin: 0; padding: 0; }
.steps-band .step {
  background: var(--graphite-soft);
  border-top: 3px solid var(--hazard);
  border-radius: var(--radius-sm);
  padding: 22px 20px 24px;
}
.steps-band .step-number {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--hazard); color: var(--graphite);
  font-size: .9rem; font-weight: 900; letter-spacing: 0;
  margin-bottom: 14px;
}
.steps-band .step-title { color: var(--white); font-size: 1.02rem; margin-bottom: 8px; }
.steps-band .step p { color: rgba(255, 255, 255, .78); font-size: .92rem; margin: 0; }

/* -------- 23.6 Price / date columns ------------------------------------ */
.columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 34px var(--gutter); }
.column { border-top: 3px solid var(--hazard); padding-top: 18px; }
.column h3 { font-size: 1.08rem; margin-bottom: 10px; }
.column p { font-size: .95rem; margin-bottom: 14px; }

/* -------- 23.7 Cluster coverage ----------------------------------------
   Every covered area, identical wording and identical prominence. The ONLY
   difference between one tile and another is whether it is a link, and nothing
   in this component draws attention to that. The current locality is not a link
   because a link to the page you are on is a dead control. */
.coverage {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px;
}
.coverage li { display: flex; }
.coverage-item {
  display: flex; align-items: center; gap: 9px;
  width: 100%;
  padding: 16px 18px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--ink);
  font-weight: 700;
  transition: var(--ease);
}
a.coverage-item:hover, a.coverage-item:focus-visible { border-color: var(--hazard); background: var(--hazard-tint); color: var(--ink); }
.coverage-item--here { border-color: var(--hazard); border-left-width: 4px; background: var(--hazard-tint); }
.coverage-pin { width: 18px; height: 18px; flex: 0 0 18px; color: var(--hazard-ink); }
.coverage-name { flex: 1 1 auto; }
.coverage-tag {
  font-size: .68rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--hazard-ink); white-space: nowrap;
}
.coverage-note { margin: 22px 0 0; color: var(--steel); }

/* -------- 23.8 Contact: call panel and location ------------------------ */
.call-panel {
  display: grid; grid-template-columns: 1.1fr 1fr; gap: 0;
  border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
}
.call-panel-main { padding: 34px 32px; border-left: 6px solid var(--hazard); }
.call-panel-side { padding: 34px 32px; background: var(--concrete); }
.call-panel-side h2 { font-size: 1.15rem; margin-bottom: 14px; }
.call-number { margin: 0 0 18px; }
.call-number-link {
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  font-weight: 900; letter-spacing: -.02em; color: var(--ink);
}
.call-number-link:hover { color: var(--hazard-ink); }
.ready-list { list-style: none; margin: 0; padding: 0; }
.ready-list li { position: relative; padding-left: 24px; margin-bottom: 10px; font-size: .95rem; }
.ready-list li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 10px; height: 10px; background: var(--hazard); border-radius: 2px;
}
.location { display: grid; grid-template-columns: 1fr 1.1fr; gap: 42px; align-items: start; }
.location-address { font-style: normal; font-weight: 700; color: var(--ink); line-height: 1.7; }

/* -------- 23.9 Hero and page-head additions ---------------------------- */
.hero-eyebrow {
  display: inline-block;
  background: var(--hazard); color: var(--ink);
  font-size: .72rem; font-weight: 900; letter-spacing: .16em; text-transform: uppercase;
  padding: 6px 12px; border-radius: 2px;
  margin: 0 0 18px;
}
.pagehead-standfirst { max-width: 62ch; margin: 0; }
.btn--ghost { background: transparent; color: var(--white); border: 2px solid var(--white); }
.btn--ghost:hover, .btn--ghost:focus-visible { background: var(--white); color: var(--ink); border-color: var(--white); }
.error-head { text-align: left; }
.error-head .eyebrow { color: var(--hazard-ink); }

/* -------- 23.10 Responsive --------------------------------------------- */
@media (max-width: 1099px) {
  .cards { grid-template-columns: repeat(2, 1fr); }
  .steps-band .steps { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 991px) {
  .feature, .feature--flip { grid-template-columns: 1fr; gap: 28px; }
  .feature--flip .figure { order: 0; }
  .call-panel { grid-template-columns: 1fr; }
  .location { grid-template-columns: 1fr; gap: 28px; }
}
@media (max-width: 767px) {
  :root { --section: 52px; }
  .cards { grid-template-columns: 1fr; }
  .steps-band .steps { grid-template-columns: 1fr; }
  .coverage { grid-template-columns: 1fr 1fr; }
  .section-head { margin-bottom: 28px; }
  .call-panel-main, .call-panel-side { padding: 26px 22px; }
}
@media (max-width: 479px) {
  .coverage { grid-template-columns: 1fr; }
}

/* -------- 23.11 Alternating ground -------------------------------------
   The section rhythm the three stronger properties in this cluster use: a quiet
   tinted ground under the sections that carry secondary weight, so a long page
   has a beat rather than a single unbroken white column. */
.section--tint { background: var(--concrete); }

/* -------- 23.12 Interior page head -------------------------------------
   REPLACES `.page-head` (removed). V1 opened every interior page with a flat
   graphite bar, which read as a breadcrumb strip with a headline in it and gave
   a service page no visual weight at all. This is the hero mechanic at three
   quarters scale, so the pages beneath the home page look like they belong to
   the same site.

   The `//` motif stays. It is this repository's own mark, it survived the Adova
   retirement on purpose, and it costs one pseudo-element. */
.pagehead {
  position: relative;
  overflow: hidden;
  background: var(--graphite);
  min-height: 340px;
  display: flex; align-items: flex-end;
  --link-accent: var(--hazard);
  --link-accent-hover: var(--white);
  --focus: var(--hazard);
}
.pagehead .hero-media { position: absolute; inset: 0; z-index: 0; }
.pagehead .hero-media img { width: 100%; height: 100%; object-fit: cover; }
.pagehead::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to bottom, rgba(18, 20, 22, .58) 0%, rgba(18, 20, 22, .84) 72%, rgba(18, 20, 22, .9) 100%);
}
.pagehead .container { position: relative; z-index: 2; padding-top: 64px; padding-bottom: 46px; }
.pagehead-inner { max-width: 780px; color: var(--white); }
.pagehead-inner h1 { color: var(--white); font-size: clamp(1.9rem, 3.6vw, 2.7rem); margin-bottom: 14px; }
.pagehead-inner h1::before { content: "//"; color: var(--hazard); margin-right: 10px; }
.pagehead-inner .pagehead-standfirst { color: rgba(255, 255, 255, .9); font-size: 1.08rem; }

/* -------- 23.13 Statement band -----------------------------------------
   V1's band was a photograph with an optional caption printed underneath it in
   13.5px grey, which is how a statement ends up looking like a photo credit.
   The statement goes ON the band. */
.band { position: relative; display: block; }
.band img { width: 100%; aspect-ratio: 21 / 9; object-fit: cover; display: block; }
.band--statement::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to right, rgba(18, 20, 22, .88) 0%, rgba(18, 20, 22, .74) 38%, rgba(18, 20, 22, .4) 72%, rgba(18, 20, 22, .28) 100%);
}
.band--statement .container {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center;
  left: 50%; transform: translateX(-50%);
}
.band-inner { max-width: 46ch; color: var(--white); }
.band-title {
  margin: 0 0 12px;
  font-size: clamp(1.4rem, 2.6vw, 2.1rem);
  font-weight: 900; line-height: 1.15; letter-spacing: -.015em;
  color: var(--white);
}
.band-inner p { margin: 0; color: rgba(255, 255, 255, .88); }

/* -------- 23.14 FAQ wrapper -------------------------------------------- */
.faq { max-width: 82ch; border-top: 1px dotted var(--rule); }

/* -------- 23.15 Footer -------------------------------------------------
   Superseded by 23.18, which had to solve the row-height problem at the same
   time. Kept as a pointer rather than as a second, weaker declaration of the
   same grid. */

@media (max-width: 991px) {
  .band--statement .container { position: relative; inset: auto; left: auto; transform: none; }
  .band--statement { background: var(--graphite); }
  .band--statement img { position: absolute; inset: 0; height: 100%; }
  .band--statement .container { padding-top: 46px; padding-bottom: 46px; }
  .pagehead { min-height: 260px; }
}
@media (max-width: 767px) {
  .footer-nav-cols { grid-template-columns: 1fr 1fr; }
  .pagehead .container { padding-top: 44px; padding-bottom: 34px; }
}

/* -------- 23.16 Split heading column -----------------------------------
   The heading column of a `.split` held a two-line heading and then 350px of
   nothing, on nine sections. It now carries a kicker and a hazard rule, so the
   column terminates instead of trailing off, and the kicker states something the
   body does not repeat. */
.split { align-items: start; }
.split > div:first-child { position: relative; padding-bottom: 4px; }
.split > div:first-child::after {
  content: ""; display: block; width: 54px; height: 3px;
  background: var(--hazard); margin-top: 18px;
}
.split-kicker { margin: 14px 0 0; color: var(--muted); font-size: 1rem; max-width: 34ch; }

/* -------- 23.17 FAQ block ----------------------------------------------
   Left-aligned at 82ch, the FAQ left the right half of the page empty under a
   heading that spanned it. Centred, which is what the three stronger properties
   in this cluster do with the same block. */
.faq { max-width: 82ch; margin: 0 auto; border-top: 1px dotted var(--rule); }
.section--faq .section-head { margin-left: auto; margin-right: auto; text-align: center; }
.section--faq .section-head .section-intro { margin-left: auto; margin-right: auto; }

/* -------- 23.18 Footer ------------------------------------------------
   Three problems, all from a grid whose row height was set by a 450px map:
   the conversion column trailed 400px of empty graphite, the three navigation
   columns were too narrow to hold "Building Demolition" on one line, and the
   whole band was taller than the FAQ above it. */
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 40px var(--gutter);
  align-items: start;
}
.footer-nav-cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.footer-nav-cols ul { list-style: none; margin: 0; padding: 0; }
.footer-nav-cols li { margin-bottom: 9px; }
.footer-nav-cols a { font-size: .95rem; }
.footer-map .map { margin-top: 16px; }
.footer-map .map iframe { width: 100%; height: 220px; display: block; border: 0; }
.footer-cta { margin-top: 18px; }

/* -------- 23.19 Contained measures -------------------------------------
   Long single paragraphs were running the full 1170px container on the address
   band and in prose. Nothing on this site should ask a reader to track a line
   longer than about 75 characters. */
.address-block p { max-width: 72ch; }
.prose { max-width: 66ch; }   /* the V-08 measure, kept: 78ch overrode a repair */
.stats-source { max-width: 88ch; }

/* -------- 23.20 Hero balance -------------------------------------------
   Two buttons rather than two buttons and a bare link, so they share a baseline
   and a height. */
.hero-actions .btn { min-height: 48px; display: inline-flex; align-items: center; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.cta-actions .btn { min-height: 48px; display: inline-flex; align-items: center; }

/* -------- 23.21 Responsive corrections, last in the cascade ------------
   These have to sit AFTER section 21's media queries, not inside section 23's,
   because the new footer grid in 23.18 is written later in the file than the
   original responsive rules and would otherwise win at every width. The probe
   caught it as a real 138px horizontal overflow at 390, which is the kind of
   defect that only exists because of source order. */
@media (max-width: 991px) {
  .footer-top { grid-template-columns: 1fr 1fr; gap: 32px var(--gutter); }
  .footer-nav-cols { grid-column: 1 / -1; order: 3; grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .footer-top { grid-template-columns: 1fr; }
  .footer-nav-cols { grid-template-columns: 1fr 1fr; }
  .footer-map .map iframe { height: 200px; }
}
@media (max-width: 479px) {
  .footer-nav-cols { grid-template-columns: 1fr; gap: 8px; }
}

/* -------- 23.22 The hazard strip ---------------------------------------
   One line, one action, on the brand's own yellow with its hatching. It exists
   because the probe measured 9.7 viewports of the home page at 320 with no way
   to act, and because the sticky call bar does not count: a bar a reader has
   learned to ignore is not a call to action.

   Yellow measures 1.67:1 on white, so it never carries text anywhere else on
   this site. Here it is a large ground with --ink on it, which is 9.4:1. */
.strip {
  background: var(--hazard);
  background-image: var(--stripes);
  padding: 26px 0;
  --link-accent: var(--ink);
  --link-accent-hover: var(--ink);
  --focus: var(--ink);
}
.strip-inner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 18px 30px;
}
.strip-title {
  margin: 0; color: var(--ink);
  font-size: clamp(1.1rem, 1.9vw, 1.4rem); font-weight: 900; letter-spacing: -.01em;
}
.strip-body { margin: 6px 0 0; color: rgba(52, 51, 51, .82); font-size: .98rem; max-width: 62ch; }
.btn--onstrip {
  background: var(--ink); color: var(--white); border: 2px solid var(--ink);
  min-height: 48px; display: inline-flex; align-items: center; white-space: nowrap;
}
.btn--onstrip:hover, .btn--onstrip:focus-visible { background: var(--white); color: var(--ink); }

/* -------- 23.23 Mobile tap spacing -------------------------------------
   The probe counted 18 to 20 controls under 44px, and most of them are the
   footer link lists: 18px of text with a 9px margin is about 27px of target.
   Ordinary links are not enlarged to 44px across the site, because a 44px
   line-height inside a paragraph tears it apart. A footer link list is not a
   paragraph, so it gets the room. */
@media (max-width: 767px) {
  .footer-nav-cols li { margin-bottom: 4px; }
  .footer-nav-cols a { display: block; padding: 13px 0; line-height: 1.35; }
  .footer-bottom li { margin: 0; }
  .footer-bottom a { display: inline-block; padding: 10px 0; }
  .strip-inner { flex-direction: column; align-items: flex-start; }
  .btn--onstrip { width: 100%; justify-content: center; white-space: normal; }
}

/* -------- 23.24 Tap targets, measured rather than guessed ---------------
   Every control the probe named at under 44px on a touch width, fixed at the
   control rather than with a blanket rule. The blanket rule is deliberately NOT
   used: `a { min-height: 44px }` inside running prose tears a paragraph apart,
   which is why the existing tel: rule exempts `.tel-inline`.

   Reported at 390: a.brand 164x42, a.text-btn 115x23, a 143x19 (the utility
   line and the footer's bottom row), and the coverage tiles at 42px. */
@media (max-width: 991px) {
  .brand { display: inline-flex; align-items: center; min-height: 46px; }
  .coverage-item { min-height: 50px; }
  .text-btn { display: inline-flex; align-items: center; min-height: 44px; }
  .address-actions .text-btn { min-height: 44px; }
  #top_links a { display: inline-flex; align-items: center; min-height: 30px; }
}

/* -------- 23.25 One primary action -------------------------------------
   The footer's conversion button was the last filled-hazard control pointing
   somewhere other than the phone. It is the phone now, and /contact/ keeps its
   place in the header navigation and in the footer's Company column. */
.footer-cta .btn { min-height: 48px; display: inline-flex; align-items: center; }

/* ------------------------------------------------------------- lead form --
   The second conversion path. Styled as a quiet panel, never as a rival to the
   phone: the number keeps the filled hazard button, the form's submit is the
   same button at the same weight but it sits inside its own bordered card, so
   the page still has one obvious primary action per region. */
.lead-form {
  background: var(--white);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--hazard);
  padding: 1rem 1rem .85rem;
  /* So .lead-row can ask how wide THIS card is. Where container queries are unsupported the row
     simply stays one column, which is the narrow layout and is never broken. */
  container-type: inline-size;
}
.lead-form-heading { font-size: 1.05rem; margin: 0 0 .7rem; }
.lead-form-lede { color: var(--steel); font-size: .92rem; margin: -.45rem 0 .9rem; }

/* Name and phone share a row wherever the card is wide enough for two usable inputs. 24rem is
   the card width at which each half still holds a formatted phone number without clipping, not a
   viewport breakpoint: the hero form and the contact form are different widths at the same
   viewport, so this has to answer to the container. The hero card is 27rem and pairs; a 375px
   phone gives the card about 21rem and it stays one column. */
.lead-row { display: grid; grid-template-columns: 1fr; gap: 0 .6rem; }
@container (min-width: 24rem) { .lead-row { grid-template-columns: 1fr 1fr; } }

.lead-field { display: flex; flex-direction: column; gap: .25rem; margin: 0 0 .6rem; }
.lead-field label { font-size: .85rem; font-weight: 600; color: var(--graphite-soft); }
.lead-optional { font-weight: 400; color: var(--muted); }
.lead-field input,
.lead-field textarea {
  font: inherit; font-size: .95rem; color: var(--ink);
  background: var(--white);
  border: 1px solid var(--rule);
  padding: .55rem .65rem;
  width: 100%;
  /* Without this a padded 100% input overflows its card on narrow screens. */
  box-sizing: border-box;
}
.lead-field textarea { resize: vertical; min-height: 3.1rem; }
.lead-field input:focus-visible,
.lead-field textarea:focus-visible {
  outline: 2px solid var(--hazard-ink);
  outline-offset: 1px;
  border-color: var(--hazard-ink);
}

.lead-actions { margin: .7rem 0 0; }
.lead-actions .btn { width: 100%; text-align: center; }
.lead-actions .btn[disabled] { opacity: .65; cursor: default; }

/* Empty until something is said, so it takes no vertical space on load. */
.lead-status { margin: .6rem 0 0; font-size: .9rem; }
.lead-status:empty { display: none; }
.lead-status[data-state="ok"] { color: #1c6b3a; font-weight: 600; }
.lead-status[data-state="error"] { color: #a3271c; font-weight: 600; }
.lead-status[data-state="busy"] { color: var(--steel); }

.lead-consent { margin: .6rem 0 0; font-size: .8rem; color: var(--muted); }

/* Off-screen rather than display:none — some bots skip hidden inputs, and this
   one is meant to be filled. aria-hidden and tabindex -1 on the markup keep it
   out of the accessibility tree and the tab order. */
.lead-hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.hero-form { margin-top: 0; max-width: 30rem; }
.call-panel-form { margin-top: 1.5rem; }

@media (min-width: 60rem) {
  /* The two columns. The copy gets the wider half so the H1 keeps a sensible measure, and the
     form is capped rather than allowed to grow with the viewport, which is what stops it reading
     as the subject of the page on a wide monitor. */
  .hero-grid { grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); gap: 2.75rem; }
  .hero .container { padding-top: 44px; padding-bottom: 44px; }
  .hero-form { max-width: 27rem; justify-self: end; width: 100%; }
  .hero-copy h1 { margin-bottom: 14px; }
  /* The contact panel is a two-column grid; the form spans it rather than
     squeezing into one column beside the ready-list. */
  .call-panel-form { grid-column: 1 / -1; }
}
