/* ============================================================================
   Emmendorfer Exteriors — design polish overlay (v2, post-Valor)
   Loaded AFTER public/assets/theme.css.

   v1 of this file existed to drag the OLD Emmendorfer theme toward Valor:
   rounded corners, soft shadows, hover lifts, a two-tier header, eyebrow chips
   and the whole blog/post system. theme.css IS the Valor design system now, so
   all of that has been deleted — keeping it would have overridden the real
   thing (its --radius-lg: 14px was quietly beating Valor's sharp 4px, and its
   blog rules were winning over Valor's).

   What is left is only what Valor does not provide:
     1. adapting Valor's hero to Emmendorfer's much longer headline
     2. fitting Emmendorfer's busier header onto small screens
     3. accessibility fixes that neither theme ships
   ============================================================================ */

/* ============================================================================
   1. Valor-parity hero (homepage)
   ============================================================================ */
/* Valor's H1 is 27 characters and sets two lines at the top clamp. Emmendorfer's
   is 90 and blew out to six. Tighten the clamp so it lands at three lines and
   the hero keeps Valor's height. */
.hero--split h1 { font-size: clamp(1.9rem, .95rem + 2.5vw, 2.9rem); }

/* Match the scrim Valor ships inline on its own hero: a 106deg ramp rather than
   the theme's 90deg, so the right column's card sits on a darker ground and the
   headline stops competing with the roof photo. */
.hero--split .hero__scrim {
  background: linear-gradient(106deg,
    rgba(var(--ink-rgb), .95) 0%,
    rgba(var(--ink-rgb), .89) 46%,
    rgba(var(--ink-rgb), .70) 78%,
    rgba(var(--ink-rgb), .55) 100%);
}

/* ---- Interior pages ported to Valor's two-column layout -------------------
   tools/port-valor-pages.mjs flattens the old stack of full-width <section>s
   into one .page-main column, wrapping each former section in .page-block.
   The sections used to supply their own vertical rhythm via section padding,
   so the blocks need it back. */
.page-block + .page-block { margin-top: clamp(34px, 4.5vw, 60px); }
.page-block > :first-child { margin-top: 0; }

/* The main column is ~62% of the container now, so card grids that were laid
   out for full width need to reflow rather than crush. */
.page-main .steps,
.page-main .svc-grid,
.page-main .tcards { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

/* ---- Dropdown menus on short viewports ------------------------------------
   The Guides menu is 14 rows. The panel is anchored to the sticky header, so
   on a 720px-tall laptop the last rows fell below the fold and scrolling the
   page moved the panel with it: "All roofing guides" was unreachable. Cap the
   panel to the space below the header and give it its own scrollbar, kept
   visible so it is obvious there is more below the fold. */
.nav__panel {
  max-height: calc(100dvh - 140px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
/* Deliberately NOT setting scrollbar-width / scrollbar-color: when either is
   present Chrome ignores ::-webkit-scrollbar and falls back to the macOS
   overlay scrollbar, which only appears mid-scroll. Styling the WebKit parts
   alone keeps the bar permanently visible, so it is obvious there is more
   below. Firefox falls through to its own always-visible scrollbar. */
.nav__panel::-webkit-scrollbar { width: 10px; display: block; }
.nav__panel::-webkit-scrollbar-track { background: transparent; margin: 6px 0; }
.nav__panel::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--color-accent) 45%, transparent);
  border: 3px solid var(--color-paper-card);
  border-radius: 999px;
}
.nav__panel::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--color-accent) 75%, transparent);
}


/* ---- Lead-magnet card (.mp) -----------------------------------------------
   Recovered from the pre-Valor overlay, where it was dead CSS because no page
   used the markup. The homepage magnet section now does.

   The source design scopes an orange (#E8792B) and royal blue (#1E56A0)
   LOCALLY to this card. On a magenta site that orange CTA fights the brand, so
   the two accents are remapped to --color-accent and --color-accent-ink here.
   Swap the two lines below back to the literal hex to get the original look. */
.mp {
  --mp-accent: var(--color-accent); --mp-accent-dk: var(--color-accent-ink);
  --mp-deep: var(--color-accent-ink); --mp-ink: var(--color-ink);
  position: relative; max-width: 960px; margin-inline: auto; isolation: isolate;
  background: #fff; border-radius: 22px; overflow: hidden;
  box-shadow: 0 30px 64px -34px rgba(0,0,0,.55);
}

/* ---- content panel (single card, photo/book panel removed) ---- */
.mp__panel { position: relative; z-index: 1; background: #fff; display: flex; flex-direction: column;
  padding: clamp(32px, 4vw, 56px) clamp(28px, 4vw, 60px); }
.mp__spark { position: absolute; top: clamp(20px, 3vw, 34px); right: clamp(28px, 5vw, 66px); width: 26px; height: 26px;
  color: var(--mp-deep); opacity: .85; }
.mp__spark svg { width: 100%; height: 100%; }
/* .mp .mp__h beats `.section--alt h3 {color:#fff}` so the heading isn't white-on-white. */
.mp .mp__h { margin: 0; font-family: var(--font-display); font-weight: 900; letter-spacing: -.02em;
  font-size: clamp(1.55rem, 3.1vw, 2.5rem); line-height: 1.06; color: var(--mp-ink); }
.mp__hl { position: relative; color: var(--mp-accent); white-space: nowrap; }
.mp__hl::after { content: ""; position: absolute; left: 0; right: 0; bottom: -.05em; height: .1em;
  background: var(--mp-accent); border-radius: 2px; }
.mp__lead { margin: 14px 0 0; max-width: 56ch; color: #59636e; font-size: clamp(1rem, 1.3vw, 1.1rem); line-height: 1.5; }

/* ---- feature trio ---- */
.mp__feats { list-style: none; margin: clamp(20px, 2.6vw, 30px) 0 0; padding: 0; display: grid;
  grid-template-columns: repeat(3, 1fr); gap: clamp(12px, 1.4vw, 20px); }
.mp__feats li { display: flex; gap: 11px; align-items: flex-start; }
.mp__feats li + li { border-left: 1px solid #e6eaef; padding-left: clamp(12px, 1.4vw, 20px); }
.mp__ic { width: 42px; height: 42px; min-width: 42px; border-radius: 50%; background: var(--mp-deep); color: #fff;
  display: grid; place-items: center; }
.mp__ic svg { width: 19px; height: 19px; }
.mp__feat-body b { display: block; font-family: var(--font-display); font-weight: 800; color: var(--mp-ink);
  font-size: .98rem; line-height: 1.15; }
.mp__feat-body span { display: block; margin-top: 4px; color: #6a7580; font-size: .84rem; line-height: 1.4; }

/* ---- email form ---- */
.mp__form { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(22px, 3vw, 32px); }
.mp__field { position: relative; flex: 1 1 260px; display: flex; align-items: center; }
.mp__field > svg { position: absolute; left: 15px; width: 20px; height: 20px; color: #9aa4ae; pointer-events: none; }
.mp__field input { width: 100%; min-height: 60px; padding: 12px 16px 12px 46px; border: 1.5px solid #d9dee4;
  border-radius: 10px; font-size: 1.02rem; font-family: inherit; color: var(--mp-ink); background: #fff; }
.mp__field input::placeholder { color: #9aa4ae; }
.mp__field input:focus { outline: none; border-color: var(--mp-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 18%, transparent); }
.mp__btn { display: inline-flex; align-items: center; gap: 10px; min-height: 60px; padding: 0 26px; border: 0;
  border-radius: 10px; background: var(--mp-accent); color: #fff; font-family: var(--font-display); font-weight: 800;
  font-size: 1.05rem; cursor: pointer; box-shadow: 0 14px 26px -12px color-mix(in srgb, var(--color-accent) 55%, transparent); transition: background .15s; }
.mp__btn:hover { background: var(--mp-accent-dk); }
.mp__btn svg { width: 20px; height: 20px; }
.mp__btn-arrow { width: 18px; height: 18px; }

/* ---- privacy line + hand-drawn arrow ---- */
.mp__privacy { position: relative; display: flex; align-items: center; gap: 8px; margin: 15px 0 0;
  color: #8592a0; font-size: .85rem; }
.mp__privacy > svg { width: 16px; height: 16px; }
.mp__handarrow { position: absolute; right: 7%; top: -18px; width: 66px; height: 48px; color: var(--mp-deep);
  pointer-events: none; }

@media (max-width: 860px) {
  .mp__panel { padding: 34px 26px 36px; }
  .mp__feats { grid-template-columns: 1fr; gap: 14px; }
  .mp__feats li + li { border-left: 0; padding-left: 0; }
  .mp__spark, .mp__handarrow { display: none; }
}
@media (max-width: 520px) {
  .mp__form { flex-direction: column; }
  .mp__field { flex: none; width: 100%; }
  .mp__btn { width: 100%; justify-content: center; }
}

/* ============================================================================
   2. Header fit — Emmendorfer carries a phone number AND a Free Estimate button
   AND a hamburger where Valor carries fewer items, so the bar needs two more
   breakpoints below Valor's own 992 nav collapse.
   ============================================================================ */
@media (max-width: 992px) {
  /* Nav is hidden by the theme at this width; push the actions to the far right
     (logo left, actions right) or they bunch up next to the logo. */
  .header--topbar .header__actions { margin-left: auto; }
}
@media (max-width: 768px) {
  .header--topbar .header__actions .btn--header { display: none; }
}
@media (max-width: 479px) {
  .header--topbar .header__bar .tel { display: none; }
  .header__toplic { display: none; }
}

/* ---- Review cards (carried over; Valor has no .rev) -----------------------
   Google reviews range from one line to fifteen, so the grid row was sized by
   the longest one and left craters of white space beside the short ones.
   Clamp every review to the same number of lines and let the reader expand the
   long ones, so the cards start equal-height and stay tidy. */
.rev__text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 7;
  line-clamp: 7;
  overflow: hidden;
}
.rev.is-expanded .rev__text { -webkit-line-clamp: unset; line-clamp: unset; overflow: visible; }

/* Injected by site.js, and only on the cards that actually overflow. */
.rev__more {
  align-self: flex-start;
  margin-top: -2px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--color-accent);
  cursor: pointer;
}
.rev__more:hover { text-decoration: underline; }

/* ---- Definition list (carried over; Valor has no .deflist) -----------------
   The component never reset the UA <ul> indent, so 40px of page background sat
   INSIDE its own border as an empty strip down the left edge. */
.deflist { margin: 0; padding: 0; }
.deflist li { gap: 18px; padding: 22px 24px; align-items: start; }
/* The tick sat in a near-black tile with a hardcoded pink glyph: a heavy dark
   square on a light card, and off-token. Tint it with the accent instead and
   match the card's corner radius rather than the old 2px. */
.deflist .ic {
  width: 34px; height: 34px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-accent) 11%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-accent) 24%, transparent);
  color: var(--color-accent);
}
.deflist .ic svg { width: 18px; height: 18px; }
.deflist b { font-size: 1.02rem; letter-spacing: -0.01em; margin-bottom: 5px; }
.deflist p { line-height: 1.6; max-width: 72ch; }

/* ---- Before / after slider -----------------------------------------------
   Another carried-over component Valor has no version of, so it is the one
   place still wearing the old theme's styling. Brought up to the Valor
   language: mono uppercase labels, sharp 2px corners, accent grip. */

/* Labels read as chips, not floating text, and sit clear of the grip. */
.ba__lbl {
  top: 10px;
  padding: 6px 10px;
  font-size: var(--fs-label);
  letter-spacing: .1em;
  line-height: 1;
  backdrop-filter: blur(2px);
}
.ba__lbl--b { background: color-mix(in srgb, var(--color-ink) 88%, transparent); }
.ba__lbl--a { background: color-mix(in srgb, var(--color-success) 90%, transparent); }

/* Grip: a round accent control instead of a white square, so it reads as
   draggable. The ::after extends the hit area to 44px without growing the dot. */
.ba__handle { width: 3px; background: rgba(255,255,255,.9); }
.ba__grip {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--color-accent);
  box-shadow: 0 2px 10px rgba(var(--ink-rgb), .45), 0 0 0 3px rgba(255,255,255,.92);
}
.ba__grip svg { width: 18px; height: 18px; color: #fff; }
.ba__grip::after { content: ""; position: absolute; inset: -3px; border-radius: 50%; }
.ba__range:focus-visible + .ba__handle .ba__grip,
.ba__range:focus-visible ~ .ba__handle .ba__grip {
  box-shadow: 0 2px 10px rgba(var(--ink-rgb), .45), 0 0 0 4px var(--color-on-dark);
}

/* Caption: the location is the headline, the work description is the caption.
   The old rule ran the description at body size in caps, which shouted over the
   place name and wrapped to two lines. */
.gproject__meta { padding: 16px 18px 18px; }
.gproject__meta .loc { font-size: 1.05rem; color: var(--color-ink); }
.gproject__meta .loc svg { width: 15px; height: 15px; flex: none; }
.gproject__meta .sub {
  margin-top: 7px;
  font-size: var(--fs-label);
  line-height: 1.5;
  letter-spacing: .07em;
  color: var(--color-text-muted);
}

/* ============================================================================
   3. Accessibility — fixes neither theme ships
   ============================================================================ */

/* Top bar: more room around the utility row, and smaller social circles. The
   circles stay a 44px touch target via a transparent ::after, so shrinking the
   visible dot to 28px does not shrink the thing a thumb has to hit. */
.header__top-inner { min-height: 52px; padding-block: 9px; }
.header__social { gap: 8px; }
.header__social a { position: relative; width: 28px; height: 28px; }
.header__social a::after { content: ""; position: absolute; inset: -8px; }
.header__social svg { width: 14px; height: 14px; }

/* The phone link is ~27px tall; extend it to the 44px minimum. */
.tel { min-height: 44px; padding-block: 6px; }

/* Form fields. Both themes set `.field input:focus { outline: none }` with only
   a 1px inset shadow, and those selectors out-specify a bare `input:focus`, so
   a keyboard user gets almost no focus signal on the lead forms. Match their
   specificity and give it a real ring. Valor's 3px accent outline on everything
   else is left alone. */
input:focus, select:focus, textarea:focus,
.field input:focus, .field select:focus, .field textarea:focus,
.iq input:focus, .cform select:focus, .cform textarea:focus,
.magnet__form input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}

/* Sticky header is 96px tall (the theme's own sticky rail uses top:96px), so
   every in-page anchor (#services, #faq, #contact, the guide TOCs) was landing
   underneath it. Offset the scroll target instead of the header. */
h1[id], h2[id], h3[id], h4[id], section[id], article[id], main[id],
.faq__item[id], .prose [id] { scroll-margin-top: 96px; }

/* Touch: kill the 300ms double-tap zoom delay on controls and replace the
   default grey flash with a brand-tinted one. */
a, button, .btn, .nav__link, summary, [role="button"], input[type="submit"] {
  touch-action: manipulation;
  -webkit-tap-highlight-color: color-mix(in srgb, var(--color-accent) 18%, transparent);
}

/* Drawer: stop the scroll chain leaking to the page behind it, and let the
   panel scroll on short viewports instead of clipping its own links. */
.drawer__panel {
  overscroll-behavior: contain;
  overflow-y: auto;
  max-height: 100dvh;
  padding-bottom: max(20px, env(safe-area-inset-bottom));
}
.drawer__scrim { overscroll-behavior: contain; }

/* Reduced motion: Valor animates card lifts and image zooms without guarding
   them. One kill switch. */
@media (prefers-reduced-motion: reduce) {
  .svc, .tcard, .step, .magnet, .btn, .stat, .badge,
  .svc__media img, .svc__photo img, .blog-card__media img, .bcard, .gcard {
    transition: none !important;
    animation: none !important;
  }
  .svc:hover, .tcard:hover, .step:hover, .magnet:hover,
  .btn:hover, .stat:hover, .badge:hover, .bcard:hover, .gcard:hover { transform: none; }
  .svc:hover .svc__media img, .svc:hover .svc__photo img,
  .blog-card:hover .blog-card__media img { transform: none; }
}

/* Typography: no widowed last words in headings, and figures that line up in
   the price/stat columns. */
h1, h2, h3, .hero__copy h1, .section-head h2 { text-wrap: balance; }
.stat, .stat__num, .iq__price, .iq__sq, .rev__aggnum, .ctable td, .post-table td,
.priceanchor, .badge { font-variant-numeric: tabular-nums; }

/* Long unbroken strings (addresses, URLs in user-facing copy) must not blow out
   a card or force a horizontal scrollbar. */
.svc, .tcard, .step, .magnet, .leadcard, .rev, .blog-card__body { min-width: 0; }
.rev__text, .tcard p, .svc p, .blog-card__excerpt { overflow-wrap: anywhere; }
body { overflow-x: hidden; }

/* Tell the browser this is a light UI so form controls and scrollbars are
   rendered light even for users defaulting to dark. */
:root { color-scheme: light; }

/* ---------------------------------------------------------------------------
   Service archive cards with imagery.

   The base .index-card in theme.css is a padded text block. These add a photo
   above the text, so the padding moves off the <a> and onto an inner body
   element, otherwise the image inherits the 24px inset and cannot go
   edge-to-edge. Both variants coexist: /service-areas/ and /guides/ keep the
   plain text card.

   The archive runs full width now that the lead-form sidebar is gone, so the
   grid holds three columns further down the viewport before it steps to two.
--------------------------------------------------------------------------- */
.index-card--media { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
/* flex:none matters. The card is a flex column, so without it the media box is a
   shrinkable item: any card whose text overflows squeezes the photo's height
   while its width stays fixed, and object-fit:cover then crops harder — the
   photo reads as zoomed on some cards and not others. Fixing the box removes
   that whole class of behaviour. */
.index-card__media { display: block; flex: none; aspect-ratio: 16 / 10; overflow: hidden; background: var(--color-paper); }
.index-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s var(--ease); }
/* Only lift the image on devices that actually hover; on touch it just costs paint. */
@media (hover: hover) { .index-card--media:hover .index-card__media img { transform: scale(1.04); } }
.index-card__body { display: block; padding: 22px 24px 24px; }
/* .go sits at the foot of every card so the grid's rows line up regardless of
   how long a service title or blurb runs. */
.index-card--media .index-card__body { display: flex; flex-direction: column; flex: 1; }
.index-card--media .go { margin-top: auto; padding-top: 14px; }

@media (prefers-reduced-motion: reduce) {
  .index-card__media img { transition: none; }
  .index-card--media:hover .index-card__media img { transform: none; }
}

/* Service-area intro. Full width, but set in two columns so no single line runs
   the whole 1200px container — long measures are the reason full-bleed body
   copy usually reads badly. Two paragraphs, two columns; stacks on narrow. */
.area__intro { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 3vw, 44px); max-width: none; }
.area__intro p { margin: 0; color: var(--color-ink-soft); }
@media (max-width: 900px) { .area__intro { grid-template-columns: 1fr; gap: 14px; } }

/* The guides grid sits in its own full-width section directly beneath the
   two-column block above it, so it must not add a second full section's worth
   of top padding on top of that block's bottom padding. */
.section--grid { padding-top: 0; }

/* .answer in the sidebar: it is styled for the wide main column, so cap the
   type and tighten the padding to sit alongside the lead form rather than
   dwarf it. */
.page-aside .answer { margin-bottom: 18px; font-size: var(--fs-sm); }

/* Curated group headings on /guides/. These were inline styles, and two of them
   carried a second style attribute that browsers silently dropped — so the size
   and bottom gap never applied and those headings rendered oversized and jammed
   against their lists. As classes the rules cannot be lost that way.
   --sep marks the groups that follow another group and need the gap above. */
.guides-group__h { font-size: 1.3rem; margin-bottom: 20px; }
.guides-group__h--sep { margin-top: 40px; }

/* ---------------------------------------------------------------------------
   Utilities replacing inline style attributes.

   !important is deliberate. An inline style outranks every stylesheet rule,
   and this codebase sets margin-top in 164 rules, font-size in 280 and color
   in 426 — so a plain class would lose wherever one of those targets the same
   element, changing the render on an unknown subset of 100 pages. !important
   reproduces the previous priority exactly, making the sweep visually neutral.

   Anything containing `display` stayed inline: site.js and the estimator
   toggle .wizard__done and #estimator-lead-form through element.style.
   background-image:url(...) stayed inline too — that is per-item data.
--------------------------------------------------------------------------- */
.u-abs { position:absolute !important; }
.u-center { margin-inline:auto !important;text-align:center !important; }
.u-fff { color:#fff !important; }
.u-fff-bold { color:#fff !important;font-weight:700 !important; }
.u-flex-full { flex:1 1 100% !important; }
.u-fs-105 { font-size:1.05rem !important; }
.u-fs-130 { font-size:1.3rem !important; }
.u-ico-16 { width:16px !important;height:16px !important; }
.u-ico-20 { width:20px !important;height:20px !important; }
.u-ico-22 { width:22px !important;height:22px !important; }
.u-ico-34 { width:34px !important;height:34px !important; }
.u-jc-center { justify-content:center !important; }
.u-m-b14 { margin:0 0 14px !important; }
.u-m-b18 { margin:0 0 18px !important; }
.u-m-b8 { margin:0 0 8px !important; }
.u-m-t4 { margin:4px 0 0 !important; }
.u-m-t8-b20 { margin:8px 0 20px !important; }
.u-meta-6 { color:var(--color-text-muted) !important;font-size:var(--fs-sm) !important;margin-top:6px !important; }
.u-meta-8 { color:var(--color-text-muted) !important;font-size:var(--fs-sm) !important;margin-top:8px !important; }
.u-mi-auto { margin-inline:auto !important; }
.u-mt-10 { margin-top:10px !important; }
.u-mt-12 { margin-top:12px !important; }
.u-mt-14 { margin-top:14px !important; }
.u-mt-16 { margin-top:16px !important; }
.u-mt-18 { margin-top:18px !important; }
.u-mt-22 { margin-top:22px !important; }
.u-mt-8 { margin-top:8px !important; }
.u-mw-380 { max-width:380px !important; }
.u-mw-74ch { max-width:74ch !important; }
.u-mw-760 { max-width:760px !important; }
.u-mx-auto { margin:0 auto !important; }
.u-offscreen { position:absolute !important;left:-9999px !important; }
.u-tac { text-align:center !important; }
.u-tal { text-align:left !important; }
.u-warn-sm { color:#E7B7B7 !important;font-size:.92rem !important; }

/* ---------------------------------------------------------------------------
   Current-page nav indicator.

   theme.css styled .nav a[aria-current="page"] all along, but nothing ever set
   the attribute — the header is one shared partial, so it cannot know which
   page it is on. site.js now marks it at runtime.

   The shipped treatment reused hover exactly (ink text + a 2px underline),
   which makes the current page indistinguishable from wherever the pointer
   happens to be. An active state has to differ from hover to mean anything, so
   this uses accent colour, heavier weight and a thicker bar.

   .is-section marks a hub whose child page is open (/service/roof-repair/
   lights Services) and gets the same treatment.
--------------------------------------------------------------------------- */
.nav a[aria-current="page"],
.nav a.is-section { color: var(--color-accent); font-weight: 700; }
.nav a[aria-current="page"]::after,
.nav a.is-section::after { transform: scaleX(1); height: 3px; bottom: 2px; }

/* Dropdown panel: theme.css targets .nav__menu, a class this header does not
   use, so the panel highlight would never have applied either. */
.nav__panel a[aria-current="page"] { background: var(--color-paper-alt); color: var(--color-accent); font-weight: 700; }
.nav__panel a[aria-current="page"] .sub { color: var(--color-accent); }

/* Mobile drawer */
.drawer a[aria-current="page"],
.drawer__sub a[aria-current="page"] { color: var(--color-accent); font-weight: 700; }

/* ---------------------------------------------------------------------------
   Lead form layout.

   Six full-width fields stacked in a single column made a very long form, and
   the same markup renders at four different widths: a ~332px sidebar on
   desktop, ~480px inside the contact page's split CTA, 380px in the
   free-estimate modal, and the full container width when the sidebar collapses
   under 980px. A media query keyed to the viewport is the wrong instrument for
   that — the viewport does not tell you how wide THIS form is. So the columns
   come from auto-fit and the form decides for itself: it pairs name/phone and
   email/address wherever two 210px columns fit, and stacks them where they do
   not. No breakpoint is involved and no context is special-cased.

   max-width is what stops auto-fit going too far. Left uncapped, the collapsed
   sidebar hands the form ~900px and it would lay out four columns, putting
   name, phone, email and address on one line. 520px allows exactly two.

   The 235px floor is measured, not guessed. At 210px the form paired columns it
   had no room for: a ~536px viewport produced two columns of exactly 210px and a
   1200px one produced 223px, both too narrow for "Property address" and its
   placeholder. 235px is also what keeps large desktops consistent — the contact
   page's split CTA leaves the form 495px wide at 1440 and 490px at 1920, so a
   240px floor would pair at one and not the other. At 235px both pair, and no
   paired column is ever narrower than 238px.
--------------------------------------------------------------------------- */
.contact-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(235px, 1fr));
  column-gap: 14px;
  align-items: start;
  max-width: 520px;
  margin-inline: auto;
}

/* Anything that is not a paired field spans the row: the step label, heading,
   intro copy, honeypot, Turnstile widget, submit button and trust line. */
.contact-form > :not(.field) { grid-column: 1 / -1; }

/* A select and a textarea both read badly at half width beside a short input. */
.contact-form .field--wide { grid-column: 1 / -1; }

.contact-form .field { margin-bottom: 14px; }
.contact-form textarea { min-height: 104px; resize: vertical; line-height: 1.6; }

/* ---------------------------------------------------------------------------
   Free-estimate modal, fallback form.

   The dialog inherited `text-align:center` and a 42ch cap on its paragraph from
   when this was four short centred fields. With a left-aligned two-column form
   under it, the intro was both centred AND a different width from the fields —
   two ragged edges where the eye wants one. It now shares the form's block and
   alignment, so the whole panel reads off a single left margin.

   The panel also has to fit inside a viewport, above the page it is covering.
   The generous 40px top padding was affordable at four fields and is not at
   six, so the vertical rhythm tightens here without touching the page forms.
--------------------------------------------------------------------------- */
.estimator-modal__fallback { padding: 26px 28px 30px; text-align: left; }

/* Same 520px block as .contact-form, so intro and fields share a left edge. */
.estimator-modal__fallback .estimator-intro {
  max-width: 520px;
  margin: 0 auto 20px;
  text-align: left;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--color-text-muted);
}

/* Mobile is one column, stated rather than inferred.
   auto-fit already collapses the form on a phone — the arithmetic works out at
   303px on a 390px screen — but leaving it implicit means the mobile layout is
   a consequence of gutter, split-CTA and card-padding values that any of a
   dozen unrelated edits could shift. On a tablet at 768px the same rule pairs
   two 253px columns, which is comfortable on a desk and cramped in two thumbs.
   767px is the site's existing mobile breakpoint, used in theme.css for the
   header and the proof grid. max-width is released with it so the single
   column uses the full card rather than sitting in a 520px box. */
@media (max-width: 767px) {
  .contact-form { grid-template-columns: 1fr; max-width: none; }
}

/* Required-field marker.

   Driven off the `required` attribute with :has() rather than an asterisk typed
   into each label, so the mark cannot drift from the validation. Make a field
   optional and its star disappears in the same edit — no second place to
   remember, which matters here because these labels live in two partials and
   the required set has already changed once.

   The star is decorative: `required` is what actually tells a screen reader the
   field is mandatory, and it is announced whether or not this renders. Some
   screen readers do read generated content, so the worst case is hearing
   "star" alongside "required" — noise, not a loss of meaning. Browsers without
   :has() simply show no star and still enforce the field.

   Message carries "(optional)" in its own label, so the form says which fields
   are which in both directions without needing a legend. */
.contact-form .field:has(:is(input, select, textarea)[required]) label::after {
  content: "*";
  margin-left: 3px;
  color: var(--color-accent);
  font-weight: 700;
}

/* Footer column headings were <h4> directly under the page's <h2>s, which
   skipped a level on 97 of 101 pages — the single cause of every heading-order
   failure on the site. They are <h3> now.

   font-size is pinned so the change is semantic only: h3 and h4 share the
   display-font rule in theme.css but not a size, so an unqualified h3 would
   have rendered these visibly larger. theme.css also carries a purpose-built
   .footer__h (mono, uppercase, letter-spaced) that the footer partial has
   never used; adopting it is a design decision, not an SEO fix, so it stays
   unused here. */
.footer h3 { font-size: 1rem; }
