/*
Theme Name: NorthCo Classic v2
Theme URI: https://northcoservices.com/
Author: NorthCo Services
Description: A classic (non-FSE) WordPress theme for NorthCo Services — clean PHP templates, native Customizer for site-wide settings, hardcoded homepage content. Zero plugin dependencies.
Version: 2.0.0
Requires at least: 6.0
Tested up to: 6.9
Requires PHP: 8.1
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: northco-classic
Tags: business, custom-menu, featured-images, threaded-comments
*/




*{box-sizing:border-box;margin:0;padding:0;}
/* ── Display face: Poppins SemiBold ────────────────────────────────────────────
   LICENSING IS WHY THIS FONT. The brand's display face is Mont (Fontfabric, commercial) and
   NorthCo does not hold a licence for it, so it cannot be served. Mont was briefly wired in here
   and has been removed — the file is gone from the theme.

   Poppins is the closest free geometric sans: SIL Open Font License, so commercial use, webfont
   use and self-hosting are all permitted outright. Compared side by side at 46px it set to the
   SAME width as Mont Bold to the pixel, so nothing reflows; the tells are in the a, g, y, R and J.

   SemiBold 600, not Bold 700, because 600 is what matches Mont Bold's weight on screen.

   font-weight:600 900 is the same trick the Mont face used, and for the same reason: this theme
   asks for 700 in 77 places, 800 in 19 and 900 in 6, but there is ONE file. Declaring the range
   makes every one of those use the real SemiBold instead of the browser faking a heavier weight.
   It also means headings render at a consistent weight, which is what they already did under
   Mont — so this is a like-for-like swap, not a restyle.

   Self-hosted rather than Google-served: 8KB, one fewer third-party connection, and no
   render-blocking dependency on fonts.gstatic.com.

   ⚠ Poppins' latin subset has no ← or → glyph. Not used anywhere in the careers pages, but
   .nav-viewall and the footer links DO use → and are set in var(--heading) — so on the V2 site
   those arrows will fall back to a system font. Replace them with the inline SVG the role cards
   already use, or accept the fallback. Flagged, not silently ignored. */
@font-face{
  font-family:'Poppins';
  font-weight:600 900;
  font-style:normal;
  font-display:swap;
  src:url('assets/fonts/Poppins-SemiBold.woff2') format('woff2');
}

:root{
  /* Brand */
  --navy:#161044;
  --navy-dark:#0D0A26;       /* footer / deepest */
  --navy-700:#241C5A;        /* raised / hover */
  --brown:#a77845;           /* gold-600 */
  --brown-dark:#8C6236;      /* gold-700, for text on cream */
  --brown-light:#BB8C57;     /* gold-500 hover */
  --brown-pale:#F3E8D6;      /* gold-100 wash */
  --white:#ffffff;

  /* Conversion CTA — carried over from the production site, where Postmedia
     measured this button as a significant driver of quote requests. Loud on
     purpose; reserved for the primary "Free Estimate" action only. */
  --cta:#ff6b00;
  --cta-hover:#E85F00;

  /* Surfaces — cream-first */
  --cream:#FAF7F2;           /* page */
  --cream-raised:#F2EDE4;    /* raised block */
  --off:#F7F3EC;             /* warmer off-cream */
  --grey:#F1EFF3;            /* inset */
  --border:#E3E0E6;          /* hairline */
  --border-warm:#E8E0D2;     /* warm hairline on cream */

  /* Ink (warm neutrals) */
  --text:#0B082A;            /* navy-900 — default body text */
  --text-2:#3A3744;          /* secondary body */
  --muted:#6B6676;           /* caption */
  --light:#8F8B99;            /* tertiary */

  /* Type */
  --heading:'Poppins','Montserrat',sans-serif;
  --sans:'DM Sans',system-ui,sans-serif;
  --ease:cubic-bezier(.2,.8,.2,1);
  /* ── Page widths ──────────────────────────────────────────────────────────
     --max      the reading-content container. Every .inner-style wrapper uses it
                (10 rules), so this single value sets the page's text column.
     --hero-max bounds the full-bleed hero on very wide screens.

     WHY TWO VALUES. The nav and the hero are full-bleed by design — .nav is
     position:fixed with padding:0 48px, and .hero is a viewport-height panel grid
     with padding:12px. Neither reads --max. So on a wide monitor the page had two
     competing widths: chrome/imagery running edge to edge, and content stopping at
     1200px. At 1900px that was a 338px-per-side discrepancy, most visible at the
     trust strip where content snaps inward directly under the hero.

     1200 → 1440 closes it from the content side; --hero-max stops the hero running
     away past ~1720px. Previous values were --max:1200px and no hero bound at all;
     a copy of the stylesheet from before this change is in
     dev/snapshots/style.css.pre-width-experiment.bak.

     Tune both live in the preview with ?widths — see nc_width_switcher() in
     dev/render.php. Nothing else in the CSS hardcodes a page width, so these two
     numbers are the whole control surface. */
  --max:1440px;
  --hero-max:1720px;
}

body{
  font-family:var(--sans);
  color:var(--text);
  background:var(--cream);
  font-size:16px;
  line-height:1.6;
  overflow-x:hidden;
}
img{max-width:100%;display:block;}
a{color:var(--brown);text-decoration:none;}

/* ── FADE UP ANIMATION ── */
@keyframes fadeUp{
  from{opacity:0;transform:translateY(20px);}
  to{opacity:1;transform:translateY(0);}
}
.fade-up{
  opacity:0;
  animation:fadeUp 0.7s var(--ease) forwards;
}
.delay-1{animation-delay:0.1s;}
.delay-2{animation-delay:0.2s;}
.delay-3{animation-delay:0.3s;}
.delay-4{animation-delay:0.4s;}

/* Brown line draw animation */
@keyframes drawLine{
  from{transform:scaleX(0);}
  to{transform:scaleX(1);}
}

/* ════════════════════
   NAV
════════════════════ */
/* ── Anchor offset ─────────────────────────────────────────
   The nav below is position:fixed at 68px, so without this every
   in-page anchor (#quote, #estimator, #services) scrolls its
   target's top underneath the header — the section heading ends
   up hidden. scroll-padding-top on the scroll container fixes
   every anchor site-wide in one place, including the primary
   Free Estimate CTA which all four buttons now point at.
   68px nav + 16px breathing room.
─────────────────────────────────────────────────────────── */
html{
  scroll-padding-top:84px;
  /* An EXPLICIT canvas background, not just body's.
     A background on <body> only reaches the canvas by propagation, and that stops being
     dependable the moment body gets overflow:hidden — which it does while the nav drawer
     is open. With html transparent the browser then paints its default WHITE beyond the
     body box, which showed as a white band at the bottom of the screen on iOS whenever
     Safari's toolbar shifted the visual viewport. Setting it here means the canvas is
     cream no matter what body is doing. */
  background:var(--cream);
}

/* ── Responsive line breaks ────────────────────────────────
   Several headings use a hardcoded <br> to control where they
   wrap on desktop. On a phone that forced break usually just
   costs a line. Add class="br-md" to any such <br> to collapse
   it below 768px — and put the space AFTER the tag, e.g.
     Get Started<br class="br-md"> Today!
   Leading whitespace is ignored at the start of a wrapped line,
   so desktop is unaffected, and the gap appears once it's hidden.

   Candidates not yet converted (each is a judgement call about
   whether one long line beats two on mobile):
     feature-kitchen, feature-bathroom, feature-exterior,
     estimator, intro h1, hero-panels headings
─────────────────────────────────────────────────────────── */
@media(max-width:768px){
  br.br-md{display:none;}
}

.nav{
  position:fixed;top:0;left:0;right:0;z-index:500;
  background:rgba(250,247,242,0.88);
  backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid var(--border-warm);
  height:68px;
  display:flex;align-items:center;
  padding:0 48px;
  justify-content:space-between;
}
.nav-logo{
  font-family:var(--heading);
  font-size:17px;font-weight:800;
  color:var(--navy);letter-spacing:0.02em;
  display:flex;align-items:center;gap:3px;
}
.nav-logo-co{color:var(--brown);}
.nav-logo-tag{
  font-size:9px;font-weight:500;
  color:var(--light);letter-spacing:0.14em;
  text-transform:uppercase;
  margin-left:8px;padding-left:8px;
  border-left:1px solid var(--border);
  line-height:1;
}
.nav-links{
  display:flex;align-items:center;gap:2px;
}
.nav-link{
  font-family:var(--heading);font-size:11px;font-weight:600;
  letter-spacing:0.07em;text-transform:uppercase;
  color:var(--muted);padding:8px 13px;border-radius:6px;
  transition:color 0.2s,background 0.2s;
}
.nav-link:hover{color:var(--navy);background:var(--grey);}

/* ══════════════════════════════════════════════════════════════════════════════
   MEGA MENU

   ONE DOM, TWO PRESENTATIONS. The same markup is a hover dropdown in the desktop
   bar and a slide-out child panel in the mobile drawer. That is deliberate: these
   submenu links exist to restore the site-wide internal links that ten service
   pages lost, and duplicating the markup per breakpoint would either double every
   link or hide half of them from crawlers.

   ⚠ Panels are hidden with visibility/opacity/transform — NEVER display:none on a
   container whose links matter, and never rendered on demand. See the warning in
   northco_nav_item().
   ══════════════════════════════════════════════════════════════════════════════ */

.nav-has-panel{display:contents;}   /* overridden per breakpoint below */
.nav-trigger{display:inline-flex;align-items:center;gap:5px;}
.nav-caret{flex:0 0 auto;transition:transform .22s var(--ease);}

/* ── Desktop: full-bleed dropdown under the bar ───────────────────────────────── */
@media(min-width:901px){
  /* ⚠ DO NOT set position:relative on .nav here.
     It was, briefly, "to make it the containing block for the panels" — but .nav is
     already position:fixed (line ~122), and fixed ALSO establishes a containing block
     for absolutely-positioned descendants, so relative bought nothing and cost a lot:
     it un-fixed the nav, which stopped it sticking on scroll AND left .hero's
     compensating margin-top:68px double-counting, opening a 68px dead band between the
     nav and the hero. */
  .nav-has-panel{display:block;position:static;}

  /* The shared surface. Owns the background, border and shadow that the panels used to
     carry individually, and animates its HEIGHT between panels so switching nav items
     morphs one container instead of closing a box and opening another.
     height is animated from an explicit px value set by nav.js — auto is not animatable,
     and this is the one case where measuring in JS is the correct answer rather than a
     workaround. */
  .nav-mega-bg{
    /* DETACHED: 10px below the bar, all four corners rounded, so it reads as a card
       belonging to the nav rather than an extension of it.
       The 10px gap is only on the BACKDROP — .nav-panel still starts at top:100% and is
       transparent, so its invisible top 10px bridges the gap and the pointer never
       crosses dead space on the way down. Without that bridge, hovering from the
       trigger to the card would leave .nav-has-panel and close the panel. */
    position:absolute;top:calc(100% + 10px);height:0;

    /* left and width are set by nav.js from the .nav-links box, so the card spans
       exactly Kitchens → Contact. It cannot be done in CSS: .nav-links is a flex item
       whose offset depends on the logo width, and there is no static value to hardcode.
       These are the fallbacks for the no-JS case. */
    left:0;right:0;max-width:var(--max);margin:0 auto;

    /* Frosted, matching the nav's own treatment (rgba cream + blur) so the two read as
       one material.
       0.94 — only just translucent. 0.82 was tried and read as see-through rather than
       frosted: the hero photography came through strongly enough to compete with the
       12.5px --muted sub-labels sitting on top of it. At 0.94 you sense there is
       something behind without being able to read it, which is the effect wanted.
       Blur raised to 24px at the same time: with less light coming through, a heavier
       blur is what keeps the little that does read as frost rather than a faint ghost
       of the image. */
    background:rgba(250,247,242,0.94);
    backdrop-filter:blur(24px) saturate(140%);
    -webkit-backdrop-filter:blur(24px) saturate(140%);

    /* Full border and all four corners rounded now that it floats clear of the bar.
       The border-top is back precisely because it is no longer sitting on the nav's own
       border-bottom, so there is no doubled seam to avoid. */
    border:1px solid var(--border-warm);
    border-radius:18px;
    box-shadow:0 20px 44px rgba(11,8,42,0.16);

    opacity:0;visibility:hidden;pointer-events:none;
    transition:height .34s var(--ease),opacity .22s var(--ease),visibility .34s var(--ease);
    z-index:59;                                  /* directly beneath the panels */
  }
  body.nav-mega-open .nav-mega-bg{opacity:1;visibility:visible;}

  /* Height animation OFF for this state — note `height` is absent from the list below.
     The .34s height transition exists to MORPH the card when moving between nav items.
     On the FIRST open there is nothing to morph from: the card grows 0 → full height over
     .34s while .nav-panel cross-fades its content in over only .2s, so for ~140ms the
     featured card at the bottom of the panel is painted BELOW the card's bottom edge,
     hanging in mid-air. Delaying the content instead would just trade the bug for a
     sluggish menu, and would slow every switch too.
     So: first open snaps to full height and fades in as one surface (nothing can spill),
     and switching between items still morphs. nav.js adds this class, commits the height
     with a forced reflow, then removes it — see setOpen(). Applied on close for the same
     reason: a collapsing card can outrun its own fading content on the way down. */
  body.nav-mega-instant .nav-mega-bg{
    transition:opacity .22s var(--ease),visibility .22s var(--ease);
  }

  .nav-panel{
    position:absolute;top:100%;left:0;right:0;
    /* No chrome of its own — .nav-mega-bg provides it. This is what makes the switch
       between nav items read as one surface changing rather than two boxes blinking. */
    background:none;border:0;box-shadow:none;
    /* Hidden without display:none so the links stay in the accessibility tree. */
    opacity:0;visibility:hidden;pointer-events:none;
    /* Opacity only. The translateY(-8px) slide that used to be here made every switch
       look like a fresh drop-in; a straight cross-fade lets the outgoing and incoming
       content pass through each other while the backdrop holds still. */
    transition:opacity .2s var(--ease),visibility .2s var(--ease);
    z-index:60;
  }
  .nav-has-panel.is-open > .nav-panel{
    opacity:1;visibility:visible;pointer-events:auto;
  }
  .nav-has-panel.is-open > .nav-trigger .nav-caret{transform:rotate(180deg);}
  .nav-has-panel.is-open > .nav-trigger{color:var(--navy);}

  .nav-panel-inner{
    /* Aligned to .nav-mega-bg by nav.js, same as the card — see the note there.
       padding-top carries the card's 10px offset (26 + 10 = 36) so the content sits an
       even distance inside the card even though the panel itself starts higher, at the
       nav's bottom edge, to bridge the hover gap. */
    max-width:var(--max);margin:0 auto;
    padding:36px 28px 30px;
    /* 26px row gap, was 40px. The "View all" row plus a 40px gap plus its own 22px
       margin stacked to ~62px of empty band across the panel, which read as a broken
       layout rather than breathing room. */
    /* SINGLE column: view-all, then the link columns, then the featured card as a full
       width band beneath them.
       It was `1fr 300px` with the featured pinned right, which worked at 1200px wide but
       not once the card was narrowed to the nav-links span (~698px). At that width
       642px of content minus a 300px card left 342px for two 190px columns, so they
       wrapped and the panel grew to 567px with a large void beside the featured card.
       Stacking gives the columns the full width and turns the featured card into a
       short horizontal banner — shorter overall, and no dead space at any width. */
    display:grid;grid-template-columns:1fr;gap:22px;align-items:start;
  }
  .nav-panel-head{display:none;}    /* drawer-only */

  /* justify-self:start is load-bearing.
     grid-column:1/-1 makes this a full-width grid item, and grid items default to
     justify-self:stretch — so the inline-flex box expanded to the whole panel and its
     border-bottom drew a rule right across it, reading as a divider instead of an
     underlined link. start makes it hug its own text again.
     margin-bottom drops to 0 because the grid row gap now does that job. */
  /* .nav-panel-inner .nav-viewall — two classes, because the base .nav-viewall rule
     with margin-bottom:22px is declared LATER in this file and would otherwise win.
     dev/nc test caught exactly this and failed the build, which is the check earning
     its keep for the third time in this stylesheet. */
  .nav-panel-inner .nav-viewall{grid-column:1 / -1;justify-self:start;margin-bottom:0;}

  .nav-panel-cols{display:flex;gap:48px;flex-wrap:wrap;}
  .nav-col{min-width:190px;}

  /* Featured card as a horizontal banner across the foot of the panel. Image left at a
     fixed 150px, copy right — keeps it to ~110px tall instead of the ~260px the vertical
     version cost, which matters because this now sits BELOW the links rather than beside
     them and adds to the panel's height rather than filling space next to it.

     Every selector here is prefixed with .nav-panel-inner or .nav-featured to raise
     specificity: the base .nav-featured / .nav-feat-* rules are declared LATER in this
     file, so single-class selectors here lost silently. dev/nc test failed the build with
     6 dead declarations and caught it — fourth time in this stylesheet. */
  .nav-panel-inner .nav-featured{
    display:grid;grid-template-columns:150px 1fr;align-items:center;
    gap:0 18px;padding:0 18px 0 0;overflow:hidden;
  }
  .nav-featured .nav-feat-img{
    grid-row:1 / span 3;
    height:100%;min-height:104px;margin:0;
  }
  .nav-featured .nav-feat-eyebrow{padding:14px 0 0;}
  .nav-featured .nav-feat-title{padding:3px 0 0;}
  .nav-featured .nav-feat-body{padding:4px 0 14px;}
}

/* ── Column headings + links (shared) ────────────────────────────────────────── */
.nav-col-label{
  font-family:var(--sans);font-size:10px;font-weight:700;
  letter-spacing:0.12em;text-transform:uppercase;
  color:var(--muted);margin-bottom:12px;
}
.nav-sub{
  display:block;padding:7px 0;text-decoration:none;
}
.nav-sub-label{
  display:block;
  font-family:var(--heading);font-size:14px;font-weight:700;
  color:var(--navy);line-height:1.3;
}
.nav-sub-desc{
  display:block;margin-top:2px;
  font-family:var(--sans);font-size:12.5px;color:var(--muted);line-height:1.35;
}
.nav-sub:hover .nav-sub-label{color:var(--brown-dark);}

.nav-viewall{
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.05em;text-transform:uppercase;
  color:var(--brown-dark);text-decoration:none;
  padding-bottom:3px;border-bottom:1px solid var(--border-warm);
  margin-bottom:22px;align-self:start;
}
.nav-viewall:hover{color:var(--navy);border-color:var(--navy);}
.nav-viewall-arr{transition:transform .2s var(--ease);}
.nav-viewall:hover .nav-viewall-arr{transform:translateX(3px);}

/* ── Featured card ───────────────────────────────────────────────────────────── */
.nav-featured{
  display:block;text-decoration:none;
  background:var(--white);border:1px solid var(--border-warm);
  border-radius:14px;overflow:hidden;padding-bottom:16px;
}
/* height is mandatory alongside the width/height attributes — see .v2-photo-img. */
.nav-feat-img{
  display:block;width:100%;height:130px;object-fit:cover;margin-bottom:14px;
}
.nav-feat-eyebrow{
  display:block;padding:0 16px;
  font-family:var(--sans);font-size:10px;font-weight:700;
  letter-spacing:0.1em;text-transform:uppercase;color:var(--brown);
}
.nav-feat-title{
  display:block;padding:4px 16px 0;
  font-family:var(--heading);font-size:15px;font-weight:800;
  color:var(--navy);line-height:1.25;
}
.nav-feat-body{
  display:block;padding:5px 16px 0;
  font-family:var(--sans);font-size:12.5px;color:var(--muted);line-height:1.4;
}
.nav-featured:hover .nav-feat-title{color:var(--brown-dark);}

/* ── Mobile drawer: chevron rows + slide-out child panels ────────────────────── */
@media(max-width:900px){
  .nav-has-panel{display:block;}

  /* The trigger becomes a full-width row matching .nav-drawer .nav-link, with the
     chevron on the right. The whole row opens the panel (JS preventDefaults the
     href) — one tap target, no label-vs-chevron ambiguity. */
  /* .nav-link.nav-trigger — THREE classes, and the third one is load-bearing.
     The trigger carries both classes, and `.nav-drawer .nav-link{padding:11px 0}` is
     declared LATER in this file at equal specificity, so a two-class selector here
     silently lost and the rows stayed 64px. Adding .nav-link raises specificity so
     order stops mattering.
     Note dev/nc test does NOT catch this: its cascade check compares identical
     selectors, and these two differ. Different-selector collisions on the same element
     still need to be reasoned about by hand.

     7px rather than 11px because the 34px chevron disc already supplies the row height.
     At 11px these rows were 64px against 48px for the flat links, which both looked
     inconsistent and pushed the top-level list 6px past the 714px drawer. */
  .nav-drawer .nav-link.nav-trigger{
    display:flex;align-items:center;justify-content:space-between;width:100%;
    /* 6px keeps the chevron rows level with the 9px flat rows above: the 34px disc plus
       12px equals the 16px text plus 18px+lineheight. Revert to 7px with the rollback. */
    padding:6px 0;
  }
  /* Chevron in a bordered gold disc, not bare.
     A bare chevron is too quiet to read as "tap to expand" for a 40–65 audience —
     especially when the rest of the row looks exactly like the flat links above it,
     which navigate. The disc gives it an unmistakable button affordance, and gold
     separates "opens something" from navy "goes somewhere". 34px, and the whole row
     is the tap target regardless, so the disc is a signal rather than a small target. */
  .nav-drawer .nav-caret{
    transform:rotate(-90deg);                                /* chevron points right */
    width:34px;height:34px;flex:0 0 34px;padding:10px;
    box-sizing:border-box;
    border:1px solid rgba(167,120,69,0.45);border-radius:11px;
    color:var(--brown-dark);
    transition:background .18s var(--ease),border-color .18s var(--ease);
  }
  .nav-drawer .nav-trigger:hover .nav-caret,
  .nav-drawer .nav-trigger:active .nav-caret{
    background:var(--brown-pale);border-color:var(--brown);
  }

  /* Child panel covers the whole drawer. .nav-drawer is position:fixed, so that is
     the containing block — inset:0 fills it, including over the buttons and socials. */
  .nav-drawer .nav-panel{
    position:absolute;inset:0;z-index:5;
    background:var(--cream);
    transform:translateX(100%);
    transition:transform .3s var(--ease);
    overflow-y:auto;-webkit-overflow-scrolling:touch;
    visibility:hidden;
  }
  .nav-drawer .nav-has-panel.is-open > .nav-panel{
    transform:translateX(0);visibility:visible;
  }
  .nav-drawer .nav-panel-inner{padding:14px 22px 30px;}

  .nav-panel-head{
    display:flex;align-items:center;gap:6px;
    margin:0 0 14px;padding-bottom:12px;
    border-bottom:1px solid var(--border-warm);
  }
  /* Back button: 44px TAP TARGET wrapping a 34px gold disc that matches the forward
     chevrons exactly. Two sizes on purpose — the disc is the visual affordance, the
     button is the hit area, and the hit area must stay 44px because this is the only
     way out of a child panel.
     A bare arrow was too quiet to read as a control at all, which is the same reason
     the forward chevrons got discs. Same border, radius and colour so "goes deeper" and
     "comes back" are visibly the same kind of thing. */
  .nav-back{
    width:44px;height:44px;flex:0 0 44px;margin-left:-10px;
    display:inline-flex;align-items:center;justify-content:center;
    background:none;border:0;padding:0;cursor:pointer;
  }
  .nav-back svg{
    width:34px;height:34px;padding:8px;box-sizing:border-box;
    border:1px solid rgba(167,120,69,0.45);border-radius:11px;
    color:var(--brown-dark);
    /* 2.6, not the tag's 2. This icon uses a 24-unit viewBox against the chevron's 12,
       so an identical stroke-width attribute renders visibly thinner once both are
       scaled into a 34px box. Matched by eye against .nav-caret. */
    stroke-width:2.6;
    transition:background .18s var(--ease),border-color .18s var(--ease);
  }
  .nav-back:hover svg,
  .nav-back:active svg{background:var(--brown-pale);border-color:var(--brown);}
  .nav-panel-title{
    font-family:var(--heading);font-size:17px;font-weight:800;color:var(--navy);
  }

  .nav-panel-cols{display:flex;flex-direction:column;gap:20px;}
  .nav-col-label{margin-bottom:8px;}
  /* 44px rows — these are the only small tap targets in the panel. */
  .nav-sub{padding:10px 0;min-height:44px;}
  .nav-viewall{margin-bottom:18px;}

  .nav-featured{margin-top:22px;}
  .nav-feat-img{height:110px;}
}
.nav-actions{display:flex;align-items:center;gap:10px;}
/* ── Desktop header actions ──
   All three solid, matching the live site: phone and Free Estimate in CTA orange, Join
   the Team in brand brown with white text. V1's own values are #FF6B00 and #B0763C —
   --cta is already an exact match for the orange, and --brown is within a few points
   of the gold, so both come from the design system rather than one-off hex.
   An earlier version tinted these as outlines to keep the orange CTA visually dominant.
   Jesse's call is parity with the live site, so they are filled. Don't revert to
   outlines on hierarchy grounds without asking.
   radius is 8px on all three — .nav-call used to be 6px for no reason. */
.nav-call{
  font-family:var(--heading);font-size:11px;font-weight:700;
  letter-spacing:0.06em;
  background:var(--cta);color:#fff;
  padding:8px 16px;border-radius:8px;
  border:0;
  transition:all 0.2s;
}
.nav-call:hover{background:var(--cta-hover);}
.nav-join{
  font-family:var(--heading);font-size:11px;font-weight:600;
  letter-spacing:0.06em;text-transform:uppercase;
  background:var(--brown);color:#fff;
  padding:9px 16px;border-radius:8px;
  border:0;
  transition:all 0.2s;
  /* Without this the label wraps to "JOIN THE / TEAM" and the button becomes 56px
     tall against its 36px neighbours, which knocks the whole bar out of alignment. */
  white-space:nowrap;
}
.nav-join:hover{background:var(--brown-dark);}
.nav-quote{
  font-family:var(--heading);font-size:11px;font-weight:700;
  letter-spacing:0.06em;text-transform:uppercase;
  background:var(--cta);color:white;
  padding:9px 20px;border-radius:8px;
  transition:all 0.2s;
  white-space:nowrap;
}
.nav-quote:hover{background:var(--cta-hover);}

/* Handset icon — hidden on desktop where the number itself is shown. */
.nav-call-ico{display:none;}
.nav-call{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;}

/* One explicit height for all three. Their paddings differ (8/9/9px vertical, and
   16/16/20px horizontal to suit three different label lengths), so without this they
   render as three slightly different boxes. Flex centring keeps the labels optically
   centred inside the fixed box. Must stay AFTER the rules above so it wins on the
   display property. */
.nav-join,.nav-call,.nav-quote{
  display:inline-flex;align-items:center;justify-content:center;
  height:38px;box-sizing:border-box;
}

/* Orange button variant for use anywhere .btn is used (mobile drawer, etc.) */
.btn-cta{background:var(--cta);color:#fff;border-color:var(--cta);}
.btn-cta:hover{background:var(--cta-hover);border-color:var(--cta-hover);}
/* ══════════════════════════════════════════════
   MOBILE NAV — burger + right-hand drawer
   Below 900px the desktop .nav-links bar is hidden.
   Before the drawer existed there was NO way to reach
   any page on a phone — and 85% of traffic is mobile.
════════════════════════════════════════════════ */
.nav-burger{display:none;}

@media(max-width:900px){
  .nav{padding:0 14px;}
  .nav-links{display:none;}

  /* Mobile header = logo + orange click-to-call + hamburger. Three items, which
     is exactly the production layout.
     The orange pill is the CALL button, not Free Estimate — that is the element
     Postmedia measured as a conversion driver, so it is reproduced as-is rather
     than repurposed. Free Estimate is carried by the sticky bar instead, so the
     two intents (call now / enquire later) both have a one-tap path. */
  .nav-join{display:none;}
  .nav-quote{display:none;}

  .nav-call{
    background:var(--cta);color:#fff;
    font-family:var(--heading);font-size:13px;font-weight:700;
    padding:10px 18px;border-radius:24px;
    letter-spacing:0.01em;
  }
  .nav-call:hover,.nav-call:active{background:var(--cta-hover);color:#fff;}
  .nav-call-ico{display:none;}   /* number is legible at this size — no icon needed */
  .nav-call-num{display:inline;}

  .nav-actions{gap:8px;}
  .nav-logo img{height:38px !important;}

  /* The box already matched the orange pill at 44px, but three bare 2px lines
     read far lighter than a solid 127×44 block beside them — the mismatch is
     optical, not dimensional. A circular container gives it comparable mass and
     makes it look like a button, which matters for an audience that skews older.
     Fully rounded to echo the pill's stadium shape.
     gap stays 5px: the animated X below is built on translateY(7px), which only
     lands correctly at that spacing. */
  /* Two buttons, one visual. #navBurger sits in the header; #navDrawerClose sits
     inside the drawer that slides over it, on exactly the same 44x44 box. Styling
     and timing are shared so the element on top always shows the same frame as the
     one beneath — the morph looks continuous instead of a snap when the drawer lands. */
  .nav-burger,
  .nav-drawer-close{
    display:inline-flex;flex-direction:column;justify-content:center;align-items:center;
    gap:5px;width:44px;height:44px;padding:0 10px;   /* 44px = min touch target */
    background:var(--white);
    border:1px solid var(--border-warm);
    border-radius:50%;
    cursor:pointer;margin-left:2px;
    transition:background .28s var(--ease),border-color .28s var(--ease);
  }
  .nav-burger[aria-expanded="true"],
  .nav-drawer.is-open .nav-drawer-close{
    background:var(--navy);
    border-color:var(--navy);
  }
  .nav-burger[aria-expanded="true"] span,
  .nav-drawer.is-open .nav-drawer-close span{background:#fff;}

  .nav-burger span,
  .nav-drawer-close span{
    display:block;width:100%;height:2px;border-radius:2px;
    background:var(--navy);
    /* .28s matches the drawer's .32s slide closely enough that the bars finish
       crossing as the drawer settles. No transition-delay on purpose — a delay on
       one button and not the other would desync them mid-slide, which is exactly
       the snap this is meant to remove.
       gap stays 5px above: translateY(7px) only lands a clean X at that spacing. */
    transition:transform .28s var(--ease),opacity .18s linear,background .28s var(--ease);
  }
  .nav-burger[aria-expanded="true"] span:nth-child(1),
  .nav-drawer.is-open .nav-drawer-close span:nth-child(1){transform:translateY(7px) rotate(45deg);}
  .nav-burger[aria-expanded="true"] span:nth-child(2),
  .nav-drawer.is-open .nav-drawer-close span:nth-child(2){opacity:0;}
  .nav-burger[aria-expanded="true"] span:nth-child(3),
  .nav-drawer.is-open .nav-drawer-close span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}

  /* Respect a reduced-motion preference: land on the X, don't animate to it. */
  @media(prefers-reduced-motion:reduce){
    .nav-burger,.nav-drawer-close,
    .nav-burger span,.nav-drawer-close span{transition:none;}
  }
}

/* Scrim behind the drawer. visibility (not display) so it can transition. */
.nav-scrim{
  position:fixed;inset:0;z-index:998;
  background:rgba(11,8,42,.45);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .3s var(--ease),visibility .3s var(--ease);
}
.nav-scrim.is-open{opacity:1;visibility:visible;pointer-events:auto;}

/* ── EXPERIMENT 2026-08-11: fully detached floating drawer ──
   Matches the desktop mega card — inset from every edge, all corners rounded, reading as
   an object over the page rather than a panel welded to its edge.

   THE 20px OF INSET IS PAID FOR, not free. This drawer ran at exactly 714px of content in
   a 714px viewport, so the vertical inset had to come from somewhere: the nav rows drop
   from ~49px to ~46px (see .nav-drawer .nav-link below), which frees ~21px across seven
   rows and keeps them above the 44px tap-target floor.

   TO ROLL BACK: restore `top:0;right:0;height:100%` and `border-radius:20px 0 0 20px`
   here, and put .nav-drawer .nav-link padding back to 11px / .nav-trigger to 7px. */
.nav-drawer{
  position:fixed;z-index:999;
  top:10px;right:10px;
  /* max() so the card clears the iOS home indicator when Safari's toolbar is hidden and
     env() becomes non-zero — a flat 10px would tuck under it. */
  bottom:max(10px, env(safe-area-inset-bottom));
  width:min(86vw,360px);height:auto;
  background:var(--cream);
  border-radius:20px;
  box-shadow:0 18px 48px rgba(11,8,42,.22);
  transform:translateX(100%);visibility:hidden;
  transition:transform .32s var(--ease),visibility .32s var(--ease);

  /* overflow:HIDDEN, and this element must NOT be the scroll container.
     Scrolling moved to .nav-drawer-inner — see the note there.

     Why: the closed mega panels are position:absolute to this element at
     transform:translateX(100%), i.e. 346px off to the right, and a transformed child
     still contributes to scrollable overflow. While this element scrolled, that made
     the drawer 691px wide internally against a 346px viewport, so you could swipe
     sideways and find the hidden panels parked out there.
     overflow-x:hidden alone did NOT fix it — it suppresses the scrollbar but the
     overflow area remains and momentum scrolling still reaches it. Separating the
     clipping container (this) from the scrolling container (.nav-drawer-inner) removes
     the scrollable area entirely. */
  overflow:hidden;
}
.nav-drawer.is-open{transform:translateX(0);visibility:visible;}

.nav-drawer-inner{
  display:flex;flex-direction:column;min-height:100%;
  /* THIS is the scroll container, not .nav-drawer. The parent clips (overflow:hidden)
     so the off-canvas mega panels cannot create a sideways scroll area; this element
     scrolls the top-level content on phones too short to fit it.
     height + box-sizing are needed together: without them the padding would push the
     scroll height past 100% and reintroduce a scrollbar on a drawer that already fits. */
  height:100%;box-sizing:border-box;
  overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;
  /* Every value in here is part of a height budget, not taste. 7 menu links +
     3 buttons + the social row have to fit inside ~714px (an iPhone 15 Pro page
     viewport with Safari's toolbars showing) or the icons land under the toolbar.
     Measured total is ~672px, leaving ~40px of slack.
     56px top clears the 44px close button (top:12px ends at 56px).
     Bottom uses max() so the icons clear the home indicator when Safari's bottom
     toolbar is hidden and env() becomes non-zero.
     Deliberately NOT justify-content:center — if the menu ever does outgrow the
     drawer, a centred flex column pushes the overflow off BOTH ends and the first
     link becomes unreachable by scrolling. Top-aligned stays recoverable. */
  /* 70px top, not 56px. At 56 the close X (12–56px) sat 7px above the first row's
     chevron disc (63–97px) AND overlapped it horizontally — two tappable circles
     almost touching in the same column, which reads as one broken control. 70px opens
     that to 21px.
     The 14px is RECLAIMED below, not added: this drawer runs at exactly 714px with zero
     slack, so the actions and social margins were tightened by the same 14px. Adding
     top padding without taking it back pushes the social icons under the fold. */
  padding:70px 26px max(24px, env(safe-area-inset-bottom));
}

/* Position only. The circle, the bars and the ≡→✕ animation are defined with
   .nav-burger inside the max-width:900px block ABOVE, because the two buttons share
   one visual. Do not re-declare background / border / width / height here: this rule
   comes later in the file, so anything set here silently beats the shared rules and
   the morph breaks. (The drawer is hidden above 900px anyway, so there is no desktop
   case that needs its own styling.) */
.nav-drawer-close{
  position:absolute;top:12px;right:14px;
}

/* Reuse of .nav-link markup — override the compact bar styling for the drawer.
   11px padding gives a 48px row: still over the 44px minimum tap target, but 8px
   per row shorter than the old 15px. Across 7 menu items that's 56px reclaimed,
   which is the difference between the social icons sitting below the fold and the
   whole stack fitting on a 714px iPhone viewport. */
.nav-drawer .nav-link{
  /* 9px, was 11px. Funds the floating drawer's 20px vertical inset: ~46px rows instead of
     ~49px, which is ~21px reclaimed across seven rows and still clear of the 44px
     tap-target floor. Revert to 11px if the detached drawer is rolled back. */
  display:block;padding:9px 0;
  border-bottom:1px solid var(--border-warm);
  font-family:var(--heading);font-size:16px;font-weight:600;
  letter-spacing:0;text-transform:none;
  color:var(--navy);text-decoration:none;
}
.nav-drawer .nav-link:last-child{border-bottom:0;}
.nav-drawer .nav-link[aria-current="page"]{color:var(--brown-dark);}

.nav-drawer-actions{margin-top:12px;display:flex;flex-direction:column;gap:9px;}   /* 12px, was 20px — 8 of the 14px reclaimed for the drawer's top padding. */
.nav-drawer-actions .btn{width:100%;text-align:center;justify-content:center;}

/* ── Drawer social row ──
   Four icons under the buttons: Instagram, Facebook, Google reviews, email.
   Fixed margin-top, NOT auto. Auto absorbed all the leftover height into one gap
   above this row, which pinned the icons to the very bottom and left a hole in the
   middle of the drawer. A fixed gap keeps the whole stack together near the top.
   44px targets: this is the one place in the drawer with small tappable things. */
.nav-drawer-social{
  margin-top:14px;padding-top:16px;   /* was 18/18 — the other 6px of the reclaim. */
  /* space-between, not gap: the row is the same 294px as the buttons above it, so
     spreading the four 44px circles edge to edge lines the first and last up with
     the button edges and reads as part of the same stack. A fixed gap would leave
     them bunched at the left. */
  display:flex;align-items:center;justify-content:space-between;
  border-top:1px solid var(--border-warm);
}
.nav-drawer-social .nds-link{
  display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;
  color:var(--muted);
  background:var(--white);
  border:1px solid var(--border-warm);
  text-decoration:none;
  transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease);
}
.nav-drawer-social .nds-link:hover{
  color:var(--navy);border-color:var(--navy);background:var(--off);
}
/* BBB has no glyph mark, so it's set as type. 11px/800 with tightened tracking is
   the largest "BBB" that clears the 44px circle's inner width without crowding it. */
.nav-drawer-social .nds-word{
  font-family:var(--heading);font-size:11px;font-weight:800;
  letter-spacing:-.02em;line-height:1;color:inherit;
}
/* No per-icon colour overrides here on purpose — all four glyphs are single-path
   fill:currentColor, so they take their colour from .nds-link and change together
   on hover. An earlier rule tried to force the four-colour Google mark to inherit
   currentColor and just rendered it flat black; the mark itself is monochrome now. */

/* Never render either on desktop, regardless of leftover state classes. */
@media(min-width:901px){
  .nav-drawer,.nav-scrim,.nav-burger{display:none !important;}
}

/* Freeze the page behind an open drawer. */
/* Scroll lock while the drawer is open.
   position:fixed, NOT overflow:hidden alone. On iOS Safari overflow:hidden on <body>
   does not reliably stop touch scrolling, and it leaves the body clipped to the viewport
   while the document stays full height — which is what exposed the bare canvas as a
   white band at the bottom of the screen.
   `top` is set inline by nav.js to the negative of the current scroll offset, so the page
   stays visually put instead of snapping to the top; nav.js restores the offset on close.
   width:100% is required — a fixed body collapses to its content width otherwise. */
body.nav-open{
  position:fixed;
  width:100%;
  overflow:hidden;
}


/* ══════════════════════════════════════════════
   ESTIMATOR SCROLL HINT
   Sits just above the sticky pill while the current step has options below the
   fold. Light on purpose — the pill below it is the primary action (navy, or gold
   in step mode), so this has to read as secondary or it competes.
   Shown by nav.js; mobile only.
════════════════════════════════════════════════ */
.est-scroll-hint{display:none;}

@media(max-width:900px){
  .est-scroll-hint{
    position:fixed;z-index:991;
    left:50%;bottom:calc(88px + env(safe-area-inset-bottom, 0px));
    display:inline-flex;align-items:center;gap:7px;
    padding:8px 14px;
    background:rgba(255,255,255,.95);
    -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
    color:var(--navy);
    border:1px solid var(--border-warm);border-radius:999px;
    font-family:var(--heading);font-size:10.5px;font-weight:700;
    letter-spacing:0.08em;text-transform:uppercase;
    box-shadow:0 6px 18px rgba(11,8,42,.16);
    cursor:pointer;

    /* Hidden by default; nav.js adds .is-on. visibility so it can transition. */
    opacity:0;visibility:hidden;pointer-events:none;
    transform:translateX(-50%) translateY(6px);
    transition:opacity .25s var(--ease),transform .25s var(--ease),visibility .25s;
  }

  .est-scroll-hint.is-on{
    opacity:1;visibility:visible;pointer-events:auto;
    transform:translateX(-50%) translateY(0);
    animation:est-nudge 2.1s ease-in-out 1s infinite;
  }
}

/* Gentle vertical nudge — draws the eye without becoming a distraction. */
@keyframes est-nudge{
  0%,100%{transform:translateX(-50%) translateY(0);}
  50%    {transform:translateX(-50%) translateY(5px);}
}

@media(prefers-reduced-motion:reduce){
  .est-scroll-hint.is-on{animation:none;}
}


/* ══════════════════════════════════════════════
   STICKY MOBILE CTA BAR
   Keeps Call and Free Estimate one tap away with the header
   still clean. Better than a header button on a tall phone:
   always visible while scrolling, and in the thumb zone rather
   than 800px up the screen. Opt out with body.no-sticky-cta.
════════════════════════════════════════════════ */
.sticky-cta{display:none;}

@media(max-width:900px){
  /* Floating frosted pill rather than an edge-to-edge bar — it reads as hovering
     over the page instead of chopping the bottom off it, and the translucency
     keeps the content underneath visible so it feels less like lost screen. */
  .sticky-cta{
    display:flex; gap:8px; align-items:center;
    position:fixed; z-index:990;
    left:14px; right:14px;
    bottom:calc(12px + env(safe-area-inset-bottom, 0px));
    padding:8px;
    border-radius:999px;
    background:rgba(250,247,242,.72);
    backdrop-filter:saturate(180%) blur(18px);
    -webkit-backdrop-filter:saturate(180%) blur(18px);
    border:1px solid rgba(255,255,255,.55);
    box-shadow:0 10px 30px rgba(11,8,42,.16), 0 2px 6px rgba(11,8,42,.06);
    transition:opacity .28s var(--ease),transform .28s var(--ease);
  }

  /* Slides away while #quote is on screen — the pill points AT that form, so
     keeping it visible is both redundant and liable to sit over the form's own
     submit button. Toggled by nav.js via IntersectionObserver. */
  .sticky-cta.is-hidden{
    opacity:0;transform:translateY(140%);pointer-events:none;
  }
  .sticky-cta a{
    flex:1 1 0; display:inline-flex; align-items:center; justify-content:center;
    gap:8px; height:46px; border-radius:999px; text-decoration:none;
    font-family:var(--heading); font-size:12px; font-weight:700;
    letter-spacing:0.06em; text-transform:uppercase;
  }
  /* Call already has an orange pill in the header, so it is not repeated here —
     and a second orange element would split attention. The sticky bar carries
     the other intent only, in navy so it supports rather than competes. */
  .sticky-cta .sc-call{display:none;}
  .sticky-cta .sc-quote{background:var(--navy);color:#fff;flex:1 1 100%;}
  .sticky-cta .sc-quote:active{background:var(--navy-700);}

  /* Inside the estimator the pill proxies the step's Next button (see nav.js).
     Gold marks it as "advance the builder" rather than "leave and enquire", and
     the disabled state mirrors the real button so it can't be tapped before the
     step's requirement is met. */
  .sticky-cta.is-step .sc-quote{background:var(--brown);}
  .sticky-cta.is-step .sc-quote:active{background:var(--brown-dark);}
  .sticky-cta.is-step-disabled .sc-quote{
    background:var(--border);color:var(--light);
    cursor:not-allowed;pointer-events:auto;   /* keep it tappable so the tap is a no-op, not a dead zone */
  }

  /* Stop the floating pill covering the last of the page content or the footer.
     Pill occupies 8 + 46 + 8 = 62px, sitting 12px off the bottom. */
  body{padding-bottom:calc(86px + env(safe-area-inset-bottom, 0px));}

  /* Hide it while the drawer is open — the drawer has its own CTAs. */
  body.nav-open .sticky-cta{display:none;}

  /* Opt-out */
  body.no-sticky-cta .sticky-cta{display:none;}
  body.no-sticky-cta{padding-bottom:0;}
}

/* Tablet — full-bleed makes sense on a phone, but stretching one button across
   716px of iPad reads as a banner rather than a floating action. Cap the width
   and centre it: left+right+max-width with auto margins splits the leftover
   space evenly, which works on a fixed-position element. */
@media(min-width:701px) and (max-width:900px){
  .sticky-cta{
    max-width:360px;
    margin-left:auto;
    margin-right:auto;
    bottom:calc(18px + env(safe-area-inset-bottom, 0px));
  }
}

/* ════════════════════
   HERO — 3 panels
   White bg shows between panels.
   Images are the panels themselves.
   Text overlays on bottom of each.
════════════════════ */
.hero{
  /* margin, not margin-top: the `auto` sides centre the hero once --hero-max starts
     biting. Written longhand rather than as margin-inline so it works on the older
     Safari versions this audience is genuinely on. 68px clears the fixed nav. */
  margin:68px auto 0;
  max-width:var(--hero-max);
  display:grid;
  grid-template-columns:1fr 1fr 1fr;
  height:calc(100vh - 68px);
  min-height:580px;
  max-height:900px;
  gap:12px;
  /* --cream, not --white. The hero is a 3-column grid with a 12px gap, so its own
     background shows through between the panels and anywhere the panels do not reach —
     and at pure white that read as a pale box against the cream page, most visibly at
     the bottom of the screen with the drawer open: body{overflow:hidden} locks the
     scroll, iOS shifts the visual viewport as the Safari toolbar changes, and the strip
     of exposed hero background appeared as a white rectangle under the navy scrim.
     Matching the page background makes any exposed area invisible whatever causes it. */
  background:var(--cream);
  padding:12px;
}
@media(max-width:768px){
  .hero{
    grid-template-columns:1fr;
    height:auto;
    /* min-height:580px was NOT being reset here — three stacked panels were
       sharing 580px (~193px each), far too short for label + heading + sub +
       pills + CTA, so .panel-content (absolute, bottom:0) overflowed upward
       and collided with .panel-label. */
    min-height:0;
    max-height:none;
    gap:10px;padding:10px;
  }

}

.panel{
  position:relative;overflow:hidden;
  cursor:pointer;
  border-radius:16px;
}
.panel-img{
  position:absolute;inset:0;
  background-size:cover;
  background-position:center;
  transition:transform 0.7s var(--ease);
}
.panel:hover .panel-img{transform:scale(1.04);}

/* Gradient — lighter at top, heavier at bottom */
.panel-grad{
  position:absolute;inset:0;
  background:linear-gradient(
    to bottom,
    rgba(10,8,30,0.0) 20%,
    rgba(10,8,30,0.32) 55%,
    rgba(10,8,30,0.72) 100%
  );
  transition:background 0.4s;
}
.panel:hover .panel-grad{
  background:linear-gradient(
    to bottom,
    rgba(10,8,30,0.0) 20%,
    rgba(10,8,30,0.42) 55%,
    rgba(10,8,30,0.78) 100%
  );
}

/* Service label top-left */
.panel-label{
  position:absolute;top:28px;left:28px;
  font-family:var(--heading);font-size:9px;font-weight:700;
  letter-spacing:0.22em;text-transform:uppercase;
  color:rgba(255,255,255,0.92);
  display:flex;align-items:center;gap:8px;
  text-shadow:0 1px 8px rgba(0,0,0,0.4);
}
.panel-label::before{
  content:'';width:20px;height:1.5px;
  background:var(--brown-light);flex-shrink:0;
}

/* NEW badge */
.panel-new{
  position:absolute;top:26px;right:26px;
  font-family:var(--heading);font-size:8px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:white;
  border:1px solid rgba(167,120,69,0.7);
  padding:4px 10px;border-radius:20px;
  background:rgba(167,120,69,0.3);
  backdrop-filter:blur(4px);
}

/* Bottom content */
.panel-content{
  position:absolute;bottom:0;left:0;right:0;
  padding:32px 32px 36px;
  z-index:1;
}

/* Brown underline — animates in on hover */
.panel-content::before{
  content:'';
  display:block;
  width:32px;height:2px;
  background:var(--brown);
  margin-bottom:14px;
  transform:scaleX(0);transform-origin:left;
  transition:transform 0.4s var(--ease);
}
.panel:hover .panel-content::before{transform:scaleX(1);}

.panel-heading{
  font-family:var(--heading);
  font-size:clamp(20px,2.2vw,28px);
  font-weight:800;
  color:white;
  line-height:1.15;
  letter-spacing:-0.02em;
  margin-bottom:8px;
}
.panel-sub{
  font-size:13px;
  color:rgba(255,255,255,0.6);
  line-height:1.5;
  margin-bottom:18px;
  font-weight:300;
  max-width:260px;
}
.panel-pills{
  display:flex;flex-wrap:wrap;gap:5px;
  margin-bottom:18px;
}
.panel-pill{
  font-family:var(--heading);font-size:9px;font-weight:600;
  letter-spacing:0.07em;text-transform:uppercase;
  color:rgba(255,255,255,0.6);
  border:1px solid rgba(255,255,255,0.2);
  padding:3px 9px;border-radius:20px;
  transition:all 0.3s;
}
.panel:hover .panel-pill{
  color:rgba(255,255,255,0.85);
  border-color:rgba(255,255,255,0.35);
}
.panel-cta{
  font-family:var(--heading);font-size:11px;font-weight:700;
  letter-spacing:0.1em;text-transform:uppercase;
  color:white;
  display:inline-flex;align-items:center;gap:7px;
  opacity:0;transform:translateY(6px);
  transition:opacity 0.3s,transform 0.3s;
  text-decoration:none;
}
.panel:hover .panel-cta{opacity:1;transform:translateY(0);}
.panel-cta .arr{transition:transform 0.2s;}
.panel:hover .panel-cta .arr{transform:translateX(4px);}

/* ── Hero panels · mobile ──────────────────────────────────
   MUST live here, AFTER the base .panel-* rules above. A media
   query adds no specificity, so an override placed before the
   base rule loses the cascade on equal specificity — which is
   exactly what happened on the first attempt at this fix.
─────────────────────────────────────────────────────────── */
/* COMPACT is the mobile default.
   All three panels must fit inside a 393×852 viewport *above the sticky CTA bar*
   — at 248px the bar covered the Exterior panel's Explore link, which defeated
   the whole point of the compact layout:
     852 − 68 nav − 74 floating pill − 40 padding/gaps = 670 ÷ 3 ≈ 220
   This matters because siding is 25% of all site conversions and it sits third
   in the stack — at the original 430px it was entirely below the fold on the
   device carrying 85% of traffic. Sub-copy is dropped (it repeats in
   services-trio and services-detail); label, heading, pills and CTA stay. */
@media(max-width:768px){
  /* .panel has no intrinsic height — every child is position:absolute — so a
     stacked layout needs an explicit one or the content overflows the box. */
  .panel{min-height:220px;}

  .panel-sub{display:none;}
  .panel-content{padding:18px 20px 20px;}
  .panel-label{top:16px;left:20px;}
  .panel-new{top:14px;right:16px;}
  .panel-heading{font-size:clamp(22px,5.6vw,28px);margin-bottom:10px;}
  .panel-pills{margin-bottom:12px;}

  /* Weight the gradient higher so short panels keep their text legible. */
  .panel-grad{
    background:linear-gradient(to bottom,
      rgba(10,8,30,0.05) 0%, rgba(10,8,30,0.42) 45%, rgba(10,8,30,0.80) 100%);
  }

  /* Touch devices have no :hover, so this hover-revealed CTA was permanently
     invisible to the 85% of traffic that is mobile. */
  .panel-cta{opacity:1;transform:none;}
}

/* ── Hero variant · TALL (body.hero-tall) ──────────────────
   The original 430px treatment, kept as an opt-out so the two
   can be compared from the preview switcher. Not the default:
   at 430px the Exterior panel falls below the fold on a phone.
─────────────────────────────────────────────────────────── */
@media(max-width:768px){
  body.hero-tall .panel{min-height:430px;}
  body.hero-tall .panel-sub{display:block;font-size:14px;margin-bottom:14px;}
  body.hero-tall .panel-content{padding:24px 22px 26px;}
  body.hero-tall .panel-label{top:22px;left:22px;}
  body.hero-tall .panel-new{top:20px;right:20px;}
  body.hero-tall .panel-heading{font-size:clamp(26px,6.6vw,34px);margin-bottom:14px;}
  body.hero-tall .panel-grad{
    background:linear-gradient(to bottom,
      rgba(10,8,30,0.0) 20%, rgba(10,8,30,0.32) 55%, rgba(10,8,30,0.72) 100%);
  }
}

/* ════════════════════
   TRUST BAR
════════════════════ */
/* Four supporting facts as plain text with dot separators.
   Previously five pills, one per row on a phone, 257px total — 36% of the viewport.
   Two things caused that and BOTH had to go: the pill chrome (34px tall, 201–258px
   wide each) and 96px of horizontal padding, which left only 306px of a 402px screen,
   narrower than the widest pill. flex-wrap was already set and could not help. */
.trust{
  border-bottom:1px solid var(--border-warm);
  padding:16px 48px;
  background:var(--cream);
}
.trust-inner{
  max-width:var(--max);margin:0 auto;
  display:flex;align-items:center;justify-content:center;
  gap:6px 12px;flex-wrap:wrap;
}
.trust-item{
  font-family:var(--sans);font-size:11px;font-weight:500;
  color:var(--text-2);letter-spacing:0.06em;text-transform:uppercase;
  white-space:nowrap;   /* a phrase breaking mid-way reads as two separate facts */
}
.trust-sep{color:var(--brown);font-size:13px;line-height:1;}

/* Legacy: .trust-dot / .trust-divider are no longer emitted by trust-bar.php, kept
   only so any other template still using the old markup does not lose its styling. */
.trust-dot{
  width:5px;height:5px;border-radius:50%;
  background:var(--brown);flex-shrink:0;
}
.trust-divider{width:1px;height:20px;background:var(--border);}

@media(max-width:700px){
  /* 20px, not 48px. The wide desktop gutter was costing 56px of usable width on a
     phone, which is most of the reason nothing could share a row. */
  .trust{padding:14px 20px;}
  .trust-divider{display:none;}

  /* 2x2 grid, and the separators GO.
     A dot only reads as a divider when the items sit on one line. Once they wrap to
     their own rows the dots strand at the end of each line and read as stray
     punctuation — which is the "crunched" look. Desktop keeps them because everything
     fits on one row there. */
  .trust-inner{
    display:grid;grid-template-columns:1fr 1fr;
    gap:9px 14px;
    justify-content:initial;align-items:start;
  }
  .trust-sep{display:none;}
  .trust-item{
    /* nowrap has to be released: the longest fact is ~202px and a grid cell here is
       ~174px, so it must be allowed to break inside its own cell instead of
       overflowing the row. */
    white-space:normal;
    text-align:center;
    font-size:10px;letter-spacing:0.04em;line-height:1.35;
  }
}

/* ════════════════════
   SECTION WRAPPER
════════════════════ */
.section{padding:96px 48px;}
.section.tight{padding:64px 48px;}
.inner{max-width:var(--max);margin:0 auto;}
@media(max-width:768px){
  .section{padding:56px 24px;}
  .section.tight{padding:44px 24px;}
}

/* ════════════════════
   TYPE
════════════════════ */
/* Eyebrow — 12px DM Sans Bold, 0.14em track, gold, with leading 32×2 gold dash */
.eyebrow{
  font-family:var(--sans);font-size:12px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--brown);margin-bottom:18px;
  display:inline-flex;align-items:center;gap:12px;
  line-height:1.4;
}
.eyebrow::before{
  content:'';flex:0 0 32px;height:2px;background:var(--brown);
}
/* Opt-in for short eyebrows that should never break across two lines. Not the
   default — longer ones still need to wrap on narrow screens.

   The ::before override is what makes nowrap SAFE. Without it, nowrap on a container
   too narrow for the text pushes the overflow out of the page and gives you sideways
   scroll — a worse bug than the wrap it fixed. Letting the decorative dash shrink
   (flex:0 1 32px, floor of 14px) means the RULE gives way under pressure instead of
   the words. Worst case the dash is stubby; the text still reads on one line.
   Budget at the floor: 14px dash + 12px gap + ~236px text ≈ 262px, which still fits
   inside a 320px viewport's content column. */
.eyebrow.nowrap{white-space:nowrap;display:flex;}
.eyebrow.nowrap::before{flex:0 1 32px;min-width:14px;}
.eyebrow.center{justify-content:center;}
.eyebrow.center::after{content:'';flex:0 0 32px;height:2px;background:var(--brown);}

.section-heading{
  font-family:var(--heading);
  font-size:clamp(30px,3.8vw,48px);
  font-weight:900;color:var(--navy);
  letter-spacing:-0.01em;line-height:1.08;
  margin-bottom:18px;
  text-wrap:pretty;
}
.section-heading em{
  color:var(--brown);
  font-style:italic;font-weight:500;
  font-family:var(--sans);
}
.section-sub{
  font-size:18px;color:var(--text-2);
  line-height:1.6;max-width:560px;
  text-wrap:pretty;
}

/* ════════════════════
   INTRO — 2 col
════════════════════ */
.intro-grid{
  display:grid;grid-template-columns:1fr 1fr;
  gap:72px;align-items:center;
}
@media(max-width:768px){.intro-grid{grid-template-columns:1fr;gap:40px;}}

/* Animated navy left border on service links */
.service-link{
  display:flex;align-items:center;justify-content:space-between;
  padding:18px 16px;
  border:1.5px solid var(--border);
  border-radius:12px;
  text-decoration:none;
  position:relative;
  transition:all 0.25s var(--ease);
  margin-bottom:10px;
  background:var(--white);
}
.service-link::before{
  content:'';
  position:absolute;left:0;top:0;bottom:0;
  width:2px;background:var(--navy);
  transform:scaleY(0);transform-origin:bottom;
  transition:transform 0.3s var(--ease);
}
.service-link:hover::before{transform:scaleY(1);}
.service-link:hover{border-color:var(--brown);background:var(--brown-pale);transform:translateX(4px);}
.service-link:first-child{}

.sl-left{display:flex;flex-direction:column;gap:3px;}
.sl-name{
  font-family:var(--heading);font-size:15px;font-weight:700;
  color:var(--navy);letter-spacing:-0.01em;
  display:flex;align-items:center;gap:10px;
}
.sl-new{
  font-family:var(--heading);font-size:8px;font-weight:700;
  letter-spacing:0.12em;text-transform:uppercase;
  background:var(--brown);color:white;
  padding:2px 7px;border-radius:20px;
}
.sl-sub{font-size:13px;color:var(--muted);}
.sl-arr{
  font-size:16px;color:var(--light);
  transition:transform 0.2s,color 0.2s;
}
.service-link:hover .sl-arr{transform:translateX(5px);color:var(--navy);}

/* CTA buttons */
.btn{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.07em;text-transform:uppercase;
  padding:13px 26px;border-radius:10px;
  transition:all 0.22s var(--ease);
  text-decoration:none;border:none;cursor:pointer;
}
.btn:hover{transform:translateY(-1px);}
.btn-dark{background:var(--navy);color:white;}
.btn-dark:hover{background:#1e1660;box-shadow:0 6px 20px rgba(22,16,68,0.18);}
.btn-brown{background:var(--brown);color:white;}
.btn-brown:hover{background:var(--brown-light);box-shadow:0 6px 20px rgba(167,120,69,0.28);}
.btn-outline{
  background:transparent;
  border:1.5px solid var(--border);
  color:var(--navy);
}
.btn-outline:hover{border-color:var(--navy);}
.btn-sm{padding:10px 18px;font-size:11px;}

/* ════════════════════
   SERVICE FEATURE BLOCKS
   Ultra-clean. Full width image,
   no coloured backgrounds.
   Text uses navy lines as accent only.
════════════════════ */
.feature{padding:80px 48px;}
.feature.alt{background:var(--off);}
@media(max-width:768px){.feature{padding:52px 24px;}}

.feature-grid{
  display:grid;grid-template-columns:1fr 1fr;
  gap:72px;align-items:center;
  max-width:var(--max);margin:0 auto;
}
.feature-grid.rev .feature-text{order:-1;}
@media(max-width:768px){
  .feature-grid{grid-template-columns:1fr;gap:36px;}
  .feature-grid.rev .feature-text{order:0;}
}

.feature-image{
  position:relative;
  border-radius:16px;
  overflow:hidden;
  aspect-ratio:4/3;
}
.feature-image img{
  width:100%;height:100%;
  object-fit:cover;display:block;
  transition:transform 0.6s var(--ease);
}
.feature-image:hover img{transform:scale(1.03);}

/* Thin brown line accent on image corner */
.feature-image::after{
  content:'';
  position:absolute;
  bottom:-12px;right:-12px;
  width:60px;height:60px;
  border-bottom:2px solid var(--brown);
  border-right:2px solid var(--brown);
  pointer-events:none;
}
.feature-grid.rev .feature-image::after{
  bottom:-12px;left:-12px;right:auto;
  border-right:none;
  border-left:2px solid var(--brown);
}

/* Also used as an <h2> in feature-exterior — margin:0 keeps it identical either way. */
.feature-eyebrow{margin:0;
  font-family:var(--heading);font-size:9px;font-weight:700;
  letter-spacing:0.2em;text-transform:uppercase;
  color:var(--brown);margin-bottom:14px;
}
.feature-heading{
  font-family:var(--heading);
  font-size:clamp(22px,2.8vw,34px);
  font-weight:800;color:var(--navy);
  letter-spacing:-0.02em;line-height:1.15;
  margin-bottom:10px;
  position:relative;
  padding-bottom:20px;
}
/* Thin navy underline on heading */
.feature-heading::after{
  content:'';
  position:absolute;bottom:0;left:0;
  width:40px;height:1.5px;
  background:var(--navy);
}
.feature-hook{
  font-size:15px;font-weight:500;
  color:var(--brown);margin-bottom:16px;
  font-style:italic;
}
.feature-body{
  font-size:15px;color:var(--muted);
  line-height:1.75;margin-bottom:12px;
}
.feature-tagline{
  font-family:var(--heading);font-size:13px;font-weight:700;
  color:var(--navy);margin-bottom:26px;
  letter-spacing:0.02em;
}
.feature-pills{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:28px;}
.feature-pill{
  font-family:var(--heading);font-size:9px;font-weight:600;
  letter-spacing:0.08em;text-transform:uppercase;
  color:var(--muted);
  border:1px solid var(--border);
  padding:5px 11px;border-radius:20px;
}
.feature-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}

/* ════════════════════
   ESTIMATOR SECTION
   White bg. Navy border accent.
   Doesn't break the white aesthetic.
════════════════════ */
.estimator-section{
  padding:80px 48px;
  border-top:1px solid var(--border);
  border-bottom:1px solid var(--border);
  background:var(--off);
}
.estimator-inner{max-width:860px;margin:0 auto;}
.estimator-header{text-align:center;margin-bottom:44px;}
.estimator-heading{
  font-family:var(--heading);
  font-size:clamp(24px,3.5vw,38px);
  font-weight:800;color:var(--navy);
  letter-spacing:-0.025em;line-height:1.15;
  margin-bottom:12px;
}
.estimator-heading em{color:var(--brown);font-style:normal;}
.estimator-sub{
  font-size:16px;color:var(--muted);
  max-width:440px;margin:0 auto;line-height:1.65;
}
.estimator-wrap{
  border:1.5px solid var(--border);
  border-radius:20px;
  overflow:hidden;
  background:var(--white);
  box-shadow:0 2px 40px rgba(22,16,68,0.06);
}

/* ════════════════════
   REVIEWS

   Own markup, not a plugin's widget. Base rules FIRST, mobile overrides in the
   media query at the END of this block — media queries add no specificity, so a
   base rule placed after one silently wins. dev/nc test now checks for exactly
   that, because this stylesheet has been bitten by it repeatedly.
════════════════════ */
/* Left-aligned, matching every other section header on the page. This block was the
   only centred one, which is what made the section read as a different site. */
.reviews-header{text-align:left;margin-bottom:28px;}

/* Legacy pair, still used by the old markup elsewhere. Kept so nothing regresses. */
.stars{font-size:20px;letter-spacing:3px;color:var(--brown);display:block;margin-bottom:6px;}
.rating-text{
  font-family:var(--heading);font-size:13px;font-weight:700;
  color:var(--navy);letter-spacing:0.04em;
}

/* Smaller than .section-heading's clamp(30px,3.8vw,48px). At 48px "Ottawa homeowners'
   experiences" ran to two lines and dominated a section that is supporting evidence,
   not a headline. */
.rv-heading{
  text-align:left;margin-bottom:16px;
  font-size:clamp(24px,2.6vw,34px);
}

/* ── Aggregate: the score is the point of the section, so it gets real size ── */
.rv-aggregate{
  display:inline-flex;align-items:center;gap:14px;
  padding:12px 22px;border-radius:999px;
  background:var(--white);border:1px solid var(--border-warm);
}
.rv-agg-score{
  font-family:var(--heading);font-size:38px;font-weight:800;line-height:1;
  color:var(--navy);
}
.rv-agg-meta{display:flex;flex-direction:column;align-items:flex-start;gap:3px;}
.rv-agg-count{
  font-family:var(--sans);font-size:12px;color:var(--muted);letter-spacing:0.02em;
}

/* ── Stars ── */
.rv-stars{display:inline-flex;gap:2px;line-height:1;}
.rv-star{color:var(--brown);font-size:14px;}
.rv-star.is-empty{color:var(--border);}

/* ── Cards ── */
.rv-grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:18px;
  align-items:start;
}
/* Ported from the uiverse card Jesse picked. Tailwind → tokens:
     bg-white p-6 space-y-4      → var(--white), 22px padding, 14px flex gap
     border border-gray-300      → 1px solid var(--border-warm)
     rounded-lg / shadow-lg      → 14px radius + the soft navy-tinted shadow below
     bg-red-500 (avatar)         → var(--brown); red is not in the palette and reads
                                   as an error state next to navy and cream
     text-red-500 (stars, link)  → var(--brown) / var(--brown-dark)
   Structure is his — identity, stars, text, Read more. */
.rv-card{
  /* blockquote carries UA margin — reset it or the gap doubles up. */
  margin:0;
  display:flex;flex-direction:column;gap:14px;
  padding:22px 20px;
  background:var(--white);
  border:1px solid var(--border-warm);border-radius:14px;
  /* shadow-lg equivalent, tinted navy rather than neutral grey so it sits in the
     warm palette instead of looking like a grey box on cream. */
  box-shadow:0 4px 16px rgba(11,8,42,0.06);
}
.rv-text{
  font-family:var(--sans);font-size:15px;line-height:1.6;color:var(--navy);
}
.rv-text p{margin:0 0 10px;}
.rv-text p:last-child{margin-bottom:0;}

/* ── Clamp for long reviews ──
   line-clamp needs -webkit-box even in 2026: the standardised `line-clamp` is still
   not universally supported, and -webkit-line-clamp is. The full text stays in the
   DOM either way — this only limits what is painted.

   .no-js safety: the toggle is display:none until nav.js adds body.rv-js, and the
   clamp is gated on that same class. If the script never runs you get the full review
   and no dead button, rather than truncated text with no way to expand it. */
.rv-card.is-long .rv-text{
  display:block;
}
body.rv-js .rv-card.is-long:not(.is-open) .rv-text{
  display:-webkit-box;
  /* 5 lines on desktop, 4 on mobile (see the media query at the end of this block).
     Every card clamps, so every card is the same height and the grid reads as a set. */
  -webkit-line-clamp:5;
  -webkit-box-orient:vertical;
  overflow:hidden;
  /* FIXED height, not min-height — min-height was tried and does not work. The clamp
     counts rendered lines but says nothing about the 10px gap BETWEEN paragraphs, so a
     two-paragraph review came out 5 lines + 10px = taller than a one-paragraph review
     at 5 clean lines, and no floor can pull it back down. A fixed box clips that gap
     and every clamped card measures identically.
     1.6em is one line at this line-height, so this tracks the type scale — a hard px
     value would break the moment font-size changed. Paragraph margins are left intact
     rather than zeroed, because running two paragraphs together mid-sentence reads as
     a missing space. */
  height:calc(5 * 1.6em);
}
/* align-items:START, and it must stay that way.
   This was briefly `stretch` to equalise card heights, which backfired badly: grid
   stretches every item to the ROW height, so expanding one card dragged its two
   neighbours to the same height and left them with a block of dead space. Expanding one
   review must not touch the others.
   Equal heights come for free anyway while clamped — same clamp, same header, same
   toggle means identical natural heights — so nothing was gained by forcing it. */
body.rv-js .rv-grid{align-items:start;}

.rv-toggle{
  display:none;                       /* shown only once nav.js is confirmed running */
  align-self:flex-start;
  background:none;border:0;padding:4px 0;margin:0;cursor:pointer;
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.05em;text-transform:uppercase;
  color:var(--brown-dark);
  border-bottom:1px solid var(--border-warm);
  min-height:32px;                    /* not a 44px target: it is secondary to the card */
}
body.rv-js .rv-card.is-long .rv-toggle{display:inline-flex;align-items:center;}
.rv-toggle:hover{color:var(--navy);border-color:var(--navy);}
.rv-toggle .rv-toggle-less{display:none;}
.rv-card.is-open .rv-toggle .rv-toggle-more{display:none;}
.rv-card.is-open .rv-toggle .rv-toggle-less{display:inline;}

/* Now a <header> at the TOP of the card, not a footer. No margin-top:auto — that was
   pushing it to the bottom, which is exactly the layout this replaced. */
.rv-by{display:flex;align-items:center;gap:11px;}
.rv-role{
  font-family:var(--sans);font-size:12px;color:var(--muted);
  letter-spacing:0.01em;line-height:1.3;
}

/* 48px displayed while the tag carries width="96" height="96" for a 2x screen.
   The explicit height here is load-bearing: an <img> height ATTRIBUTE beats
   aspect-ratio, so without a CSS height these would render 48x96 and stretch every
   card in the row — the same bug that inflated the estimator upgrade cards. */
.rv-avatar{
  width:48px;height:48px;flex:0 0 48px;
  border-radius:50%;object-fit:cover;display:block;
  background:var(--grey);
}
/* Solid disc with white initials, per the uiverse card — bg-red-500 mapped to
   var(--brown). The earlier pale-gold-on-cream version was too quiet to anchor the
   card now that identity leads it. */
.rv-avatar-ph{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--heading);font-size:16px;font-weight:800;
  letter-spacing:0.02em;
  color:#fff;background:var(--brown);
}
.rv-who{display:flex;flex-direction:column;gap:2px;min-width:0;}
.rv-name{
  font-family:var(--heading);font-size:14px;font-weight:700;
  color:var(--navy);font-style:normal;   /* cite italicises by default */
}
/* Unnamed reviewer. Deliberately quieter than a real name — it should not look like
   equally strong attribution, because it isn't. */
.rv-name-anon{font-weight:600;color:var(--muted);font-size:13px;}
.rv-date{font-family:var(--sans);font-size:12px;color:var(--muted);}

/* ── The single outbound link, kept quiet and at the foot ── */
.rv-more{text-align:left;margin:20px 0 0;}
.rv-more a{
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.05em;text-transform:uppercase;
  color:var(--muted);text-decoration:none;
  border-bottom:1px solid var(--border-warm);padding-bottom:2px;
}
.rv-more a:hover{color:var(--navy);border-color:var(--navy);}

@media(max-width:900px){
  .rv-grid{grid-template-columns:1fr;gap:10px;}
  /* Tighter on a phone: three stacked cards are the tallest thing in this section,
     and the trust strip above already taught us how fast vertical space goes. */
  .rv-card{padding:14px 15px;gap:9px;border-radius:12px;}
  .rv-text{font-size:14px;line-height:1.55;}
  /* 4 lines here, and the fixed height must track it — 1.55em is one line at the
     mobile line-height set just above. */
  body.rv-js .rv-card.is-long:not(.is-open) .rv-text{
    -webkit-line-clamp:4;
    height:calc(4 * 1.55em);
  }
  .rv-aggregate{gap:12px;padding:9px 16px;}
  .rv-agg-score{font-size:30px;}
  .reviews-header{margin-bottom:18px;}
  .rv-heading{margin-bottom:12px;}
  /* The avatar is the only thing forcing the footer taller than the text needs. */
  .rv-avatar{width:40px;height:40px;flex-basis:40px;}
  .rv-avatar-ph{font-size:16px;}
}

/* ════════════════════
   WHY CHOOSE — minimal
   No background fill. Just grid of
   items with a brown dot accent.
════════════════════ */
.why-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:0;
}
@media(max-width:900px){.why-grid{grid-template-columns:1fr 1fr;}}
@media(max-width:500px){.why-grid{grid-template-columns:1fr;}}
.why-item{
  padding:32px;
  border-right:1px solid var(--border);
  border-bottom:1px solid var(--border);
  transition:background 0.2s;
}
.why-item:hover{background:var(--off);}
.why-item:nth-child(3n){border-right:none;}
.why-item:nth-last-child(-n+3){border-bottom:none;}
@media(max-width:900px){
  .why-item:nth-child(2n){border-right:none;}
  .why-item:nth-child(3n){border-right:1px solid var(--border);}
  .why-item:nth-last-child(-n+3){border-bottom:1px solid var(--border);}
  .why-item:nth-last-child(-n+2){border-bottom:none;}
}
.why-dot{
  width:7px;height:7px;border-radius:50%;
  background:var(--brown);margin-bottom:16px;
}
.why-name{
  font-family:var(--heading);font-size:13px;font-weight:700;
  /* margin-top:0 is required: these are <h3> so they carry the site's six "Why Choose"
     headings in the document outline (V1 had them as H3 and V2 had demoted them to
     divs). An h3 brings a UA margin-block-start of ~1em, which would push every item
     down inside its grid cell. */
  color:var(--navy);margin:0 0 7px;letter-spacing:-0.01em;
}
.why-body{font-size:13px;color:var(--muted);line-height:1.6;}

/* ════════════════════
   ABOUT — condensed
════════════════════ */
.about-grid{
  display:grid;grid-template-columns:1fr 1fr;
  gap:72px;align-items:start;
}
@media(max-width:768px){.about-grid{grid-template-columns:1fr;gap:40px;}}
.about-image{border-radius:16px;overflow:hidden;aspect-ratio:4/3;}
.about-image img{width:100%;height:100%;object-fit:cover;}
.about-image-ph{
  width:100%;aspect-ratio:4/3;
  background:linear-gradient(135deg,#e8e3db,#d4cfc6);
  display:flex;align-items:center;justify-content:center;
  font-size:48px;
}
.about-mission{
  font-family:var(--heading);
  font-size:16px;font-weight:600;
  color:var(--navy);line-height:1.6;
  border-left:2px solid var(--brown);
  padding-left:18px;margin-bottom:32px;
  font-style:italic;
}
.about-values{display:grid;grid-template-columns:1fr 1fr;gap:24px 32px;}
@media(max-width:480px){.about-values{grid-template-columns:1fr;}}
.about-val-name{
  font-family:var(--heading);font-size:12px;font-weight:700;
  /* Same as .why-name — these are <h3> now (the four company values, H3 in V1). */
  color:var(--navy);margin:0 0 5px;letter-spacing:0.02em;
  display:flex;align-items:center;gap:8px;
}
.about-val-name::before{
  content:'';width:5px;height:5px;border-radius:50%;
  background:var(--brown);flex-shrink:0;
}
.about-val-body{font-size:12px;color:var(--muted);line-height:1.6;}

/* ════════════════════
   CONTACT FORM
   Dark section — the only non-white bg.
   Justified since it's functional.
════════════════════ */
.contact-section{background:var(--navy);padding:80px 48px;}
@media(max-width:768px){.contact-section{padding:52px 24px;}}
.contact-grid{
  display:grid;grid-template-columns:1fr 2fr;
  gap:72px;align-items:center;
  max-width:var(--max);margin:0 auto;
}
@media(max-width:768px){.contact-grid{grid-template-columns:1fr;gap:36px;}}
.contact-heading{
  font-family:var(--heading);
  font-size:clamp(30px,4vw,48px);
  font-weight:800;color:var(--white);
  letter-spacing:-0.025em;line-height:1.1;
  margin-bottom:12px;
}
.contact-sub{font-size:14px;color:rgba(255,255,255,0.45);line-height:1.65;margin-bottom:24px;}
.contact-line{
  width:32px;height:2px;background:var(--brown);margin-bottom:0;
}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px;}
@media(max-width:500px){.form-row{grid-template-columns:1fr;}}

/* Cross-link between the short form and the estimator, so whichever one a
   visitor lands on, the other is one tap away rather than a dead end. */
.form-alt{
  margin:18px 0 0;
  font-size:14px;line-height:1.55;color:var(--muted);
}
.form-alt.center{text-align:center;margin-top:14px;}

/* ── Estimator offer, inset above the first field ──────────
   Gold wash so it registers before someone starts typing, but
   stays clearly secondary to the navy submit button below —
   the goal is a visible fork in the road, not a second CTA
   competing with the primary one.
─────────────────────────────────────────────────────────── */
.form-alt-box{
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  background:var(--brown-pale);
  border:1px solid rgba(167,120,69,0.28);
  border-radius:14px;
  padding:14px 16px;
  margin:0 0 22px;
}
.fab-text{flex:1 1 200px;min-width:0;}
.fab-text strong{
  display:block;
  font-family:var(--heading);font-weight:700;font-size:14.5px;
  line-height:1.3;letter-spacing:-0.005em;color:var(--navy);
}
.fab-text span{
  display:block;margin-top:3px;
  font-size:12.5px;line-height:1.45;color:var(--brown-dark);
}
.fab-link{
  flex:0 0 auto;
  display:inline-flex;align-items:center;gap:7px;
  background:var(--white);
  border:1px solid rgba(167,120,69,0.40);
  color:var(--brown-dark);
  font-family:var(--heading);font-size:11.5px;font-weight:700;
  letter-spacing:0.05em;text-transform:uppercase;
  padding:10px 16px;border-radius:999px;
  text-decoration:none;white-space:nowrap;
  transition:background 0.2s,color 0.2s,border-color 0.2s;
}
.fab-link:hover{background:var(--navy);border-color:var(--navy);color:#fff;}
.fab-link .arr{transition:transform 0.2s var(--ease);}
.fab-link:hover .arr{transform:translateX(3px);}

/* ── Quiet variant ──
   Postmedia: the boxed version pulled attention out of the form it lives inside. This
   strips the panel and the button down to a single line of text with an inline link,
   so the fork stays discoverable without competing with the fields below it.

   Placed AFTER the rules it overrides — .form-alt-box and .fab-link are both defined
   above, so anything here wins on equal specificity. Reversing that order silently
   breaks it, which is what dev/nc test's first check exists to catch. */
.form-alt-box.form-alt-quiet{
  display:block;
  background:none;border:0;border-radius:0;
  padding:0;margin:0 0 14px;
}
.form-alt-quiet .fab-text{flex:none;}
.form-alt-quiet .fab-text strong{
  display:inline;
  font-family:var(--sans);font-weight:500;font-size:13px;
  letter-spacing:0;color:var(--muted);
}
.form-alt-quiet .fab-link{
  display:inline;
  background:none;border:0;padding:0;margin-left:5px;
  font-family:var(--sans);font-size:13px;font-weight:600;
  letter-spacing:0;text-transform:none;
  color:var(--brown-dark);
  border-bottom:1px solid rgba(167,120,69,0.45);
}
.form-alt-quiet .fab-link:hover{
  background:none;color:var(--navy);border-bottom-color:var(--navy);
}

/* Narrow columns: the pill goes full-width under the text rather than squeezing. */
@media(max-width:560px){
  .form-alt-box{gap:12px;padding:14px;}
  .fab-link{width:100%;justify-content:center;}
}
.form-alt a{
  color:var(--brown-dark);text-decoration:none;font-weight:600;
  white-space:nowrap;
}
.form-alt a:hover{color:var(--navy);text-decoration:underline;}
.form-alt a .arr{display:inline-block;transition:transform 0.2s var(--ease);}
.form-alt a:hover .arr{transform:translateX(3px);}
.form-alt-note{
  display:block;margin-top:3px;
  font-size:12.5px;color:var(--light);
}
.fi{
  width:100%;
  background:rgba(255,255,255,0.07);
  border:1px solid rgba(255,255,255,0.12);
  color:white;padding:12px 14px;border-radius:8px;
  font-size:14px;font-family:var(--sans);
  transition:border-color 0.2s;outline:none;
  margin-bottom:0;
}
.fi:focus-visible{border-color:rgba(167,120,69,0.5);box-shadow:0 0 0 3px rgba(167,120,69,0.28);}
/* Ring, not just a border tint. outline:none is set above, and on a dark field a 1px
   border-colour change is not a discoverable focus indicator for anyone tabbing through. */
.fi:focus{border-color:rgba(167,120,69,0.5);}
.fi::placeholder{color:rgba(255,255,255,0.28);}
.fi-select{
  width:100%;
  background:rgba(255,255,255,0.07);
  border:1px solid rgba(255,255,255,0.12);
  color:rgba(255,255,255,0.6);padding:12px 14px;border-radius:8px;
  font-size:14px;font-family:var(--sans);
  transition:border-color 0.2s;outline:none;
  margin-bottom:10px;cursor:pointer;
  -webkit-appearance:none;
}
.fi-select option{background:var(--navy);color:white;}
.fi-select:focus-visible{border-color:rgba(167,120,69,0.5);box-shadow:0 0 0 3px rgba(167,120,69,0.28);}
.fi-select:focus{border-color:rgba(167,120,69,0.5);}
.fi-textarea{
  width:100%;
  background:rgba(255,255,255,0.07);
  border:1px solid rgba(255,255,255,0.12);
  color:white;padding:12px 14px;border-radius:8px;
  font-size:14px;font-family:var(--sans);
  resize:vertical;min-height:90px;
  transition:border-color 0.2s;outline:none;
  margin-bottom:10px;
}
.fi-textarea:focus-visible{border-color:rgba(167,120,69,0.5);box-shadow:0 0 0 3px rgba(167,120,69,0.28);}
.fi-textarea:focus{border-color:rgba(167,120,69,0.5);}
.fi-textarea::placeholder{color:rgba(255,255,255,0.28);}
.fi-submit{
  width:100%;background:var(--brown);color:white;
  border:none;padding:14px;border-radius:8px;
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.1em;text-transform:uppercase;
  cursor:pointer;transition:background 0.2s;
}
.fi-submit:hover{background:var(--brown-light);}

/* ════════════════════
   FOOTER
════════════════════ */
.footer{background:#0d0a26;padding:56px 48px 28px;}
@media(max-width:768px){.footer{padding:40px 24px 24px;}}
.footer-grid{
  display:grid;grid-template-columns:1.4fr 1fr 1.2fr 1fr;
  gap:40px;max-width:var(--max);margin:0 auto 44px;
}
@media(max-width:900px){.footer-grid{grid-template-columns:1fr 1fr;}}
@media(max-width:500px){.footer-grid{grid-template-columns:1fr;}}
.footer-brand{
  font-family:var(--heading);font-size:16px;font-weight:800;
  color:white;margin-bottom:12px;
}
.footer-brand span{color:var(--brown);}
.footer-desc{font-size:13px;color:rgba(255,255,255,0.3);line-height:1.65;margin-bottom:20px;max-width:220px;}
.footer-socials{display:flex;gap:8px;}
.fsoc{
  width:32px;height:32px;border-radius:50%;
  background:rgba(255,255,255,0.06);
  border:1px solid rgba(255,255,255,0.1);
  display:flex;align-items:center;justify-content:center;
  font-size:12px;color:rgba(255,255,255,0.4);
  transition:all 0.2s;text-decoration:none;
}
.fsoc:hover{background:var(--brown);border-color:var(--brown);color:white;}
.footer-col-title{
  font-family:var(--heading);font-size:9px;font-weight:700;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--brown-light);margin-bottom:16px;
}
.footer-links{display:flex;flex-direction:column;gap:8px;}
.flink{font-size:13px;color:rgba(255,255,255,0.38);text-decoration:none;transition:color 0.2s;}
.flink:hover{color:rgba(255,255,255,0.75);}
.flink.new{color:rgba(167,120,69,0.8);}
.flink.section-head{color:rgba(255,255,255,0.55);font-weight:600;margin-top:4px;}
.fcontact{display:flex;align-items:flex-start;gap:8px;font-size:13px;color:rgba(255,255,255,0.38);margin-bottom:10px;}
.footer-bottom{
  max-width:var(--max);margin:0 auto;
  padding-top:24px;border-top:1px solid rgba(255,255,255,0.06);
  display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;
}
.fcopy{font-size:12px;color:rgba(255,255,255,0.2);}
.flegal a{font-size:12px;color:rgba(255,255,255,0.2);text-decoration:none;}
.flegal a:hover{color:rgba(255,255,255,0.45);}


/* ── WIDGET ── */
.widget{
  width:100%;max-width:820px;
  background:var(--white);
  border-radius:22px;
  box-shadow:var(--shadow-lg);
  overflow:hidden;
}

/* ════════════════════════════════════════════════════
   V2 MOTIFS — 2026 refresh
   Services trio · Founder quote · Recent work grid
   Testimonial · FAQ · Navy contact band
════════════════════════════════════════════════════ */

/* Shared section rhythm on cream */
.v2-section{padding:128px 48px;}
.v2-section.tight{padding:96px 48px;}
.v2-section.cream-raised{background:var(--cream-raised);}
.v2-section.navy{background:var(--navy);color:#fff;}
.v2-inner{max-width:var(--max);margin:0 auto;}
@media(max-width:768px){
  .v2-section{padding:80px 24px;}
  .v2-section.tight{padding:64px 24px;}
}

/* Shared: photo placeholder block (cream-raised with hairline label) */
.v2-photo{
  position:relative;width:100%;aspect-ratio:4/3;
  background:var(--cream-raised);
  border-radius:10px;overflow:hidden;
  display:flex;align-items:flex-end;
  border:1px solid var(--border-warm);
}
.v2-photo--tall{aspect-ratio:3/4;}
.v2-photo--wide{aspect-ratio:16/10;}
.v2-photo--square{aspect-ratio:1/1;}

/* Real photo inside a .v2-photo slot.
   height:100% is LOAD-BEARING. The tag carries width="760" height="570" so the browser
   reserves space and the page does not jump on load — but a height attribute outranks
   the parent's aspect-ratio, so without this the image renders 373x570 and stretches
   the card. object-fit:cover then crops rather than distorts. Same failure the
   estimator upgrade photos hit; dev/nc test now guards it. */
.v2-photo-img{
  display:block;width:100%;height:100%;
  object-fit:cover;object-position:center;
  /* Above .v2-photo::before, which is absolutely positioned and would otherwise paint
     its placeholder diagonal across the photo. See the note on that rule. */
  position:relative;z-index:1;
}
.v2-photo-label{
  font-family:var(--sans);font-size:11px;font-weight:500;
  color:var(--muted);letter-spacing:0.14em;text-transform:uppercase;
  padding:14px 18px;
  border-top:1px solid var(--border-warm);
  background:rgba(250,247,242,0.6);
  width:100%;
}
/* Diagonal "empty slot" marker. It is position:absolute, so it paints ABOVE the
   non-positioned children — including a real <img> — which drew a faint line straight
   across the service photos once they landed. Two guards, deliberately both:
     :has(img)  skips painting it entirely when a photo is present
     z-index    on .v2-photo-img keeps the photo on top regardless, so this is still
                right if :has() is ever unavailable or the markup changes shape */
.v2-photo::before{
  content:"";
  position:absolute;inset:0;
  background-image:
    linear-gradient(135deg, transparent 49.5%, var(--border-warm) 49.5%, var(--border-warm) 50.5%, transparent 50.5%);
  opacity:0.5;
}
.v2-photo:has(img)::before{display:none;}

/* ── Service card photo hover — matched to the hero panels ──
   Same numbers as .panel deliberately, so the two sections feel like one system:
   scale(1.04) over 0.7s, plus a navy rgba(10,8,30,…) tint. ::after is the tint layer
   because ::before is already the placeholder diagonal above.

   Gated on @media(hover:hover) as well as the desktop width. Width alone is not enough:
   on a touch device :hover STICKS after a tap, so the tint would stay stuck over the
   photo until you tapped elsewhere — which looks like a rendering bug. hover:hover
   excludes touch entirely, and 85% of this site's traffic is touch. */
@media (hover:hover) and (min-width:901px){
  .v2-photo-img{transition:transform 0.7s var(--ease);}
  .svc-card:hover .v2-photo-img{transform:scale(1.04);}

  .v2-photo:has(img)::after{
    content:'';position:absolute;inset:0;
    /* Above .v2-photo-img, which sits at z-index 1. */
    z-index:2;pointer-events:none;
    background:rgba(10,8,30,0);
    transition:background 0.4s var(--ease);
  }
  .svc-card:hover .v2-photo:has(img)::after{background:rgba(10,8,30,0.28);}
}

/* ── Eyebrow + heading block (reusable header) ── */
.v2-header{max-width:640px;margin:0 0 56px;}
.v2-header.center{margin-left:auto;margin-right:auto;text-align:center;}
.v2-h2{
  font-family:var(--heading);font-weight:900;
  font-size:clamp(34px,4.2vw,56px);
  line-height:1.04;letter-spacing:-0.01em;
  color:var(--navy);
  text-wrap:pretty;
  margin:0;
}
.v2-h2 em{
  color:var(--brown);font-style:italic;font-weight:500;
  font-family:var(--sans);
}
/* 620px, not 560px. Measured at 19px: 560 gives 57 characters a line and 620 gives
   ~63 — both inside the 45–75 range that reads comfortably, so this is not a
   readability change. It exists so the paragraph fills its 640px column instead of
   stopping 80px short of the heading above it, which was the actual visual complaint.
   Do NOT remove the cap entirely: at the full 1184px inner width this would run to
   ~120 characters a line, which is genuinely hard to read. */
.v2-lede{
  font-size:19px;line-height:1.55;color:var(--text-2);
  margin:20px 0 0;max-width:620px;
  text-wrap:pretty;
}
.v2-header.center .v2-lede{margin-left:auto;margin-right:auto;}

/* ════════════════════
   Services trio (3-up)
════════════════════ */
.services-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:32px;
}
@media(max-width:900px){.services-grid{grid-template-columns:1fr;gap:40px;}}


/* ══════════════════════════════════════════════
   SERVICES DETAIL — one card per service, real H2s
   Plain cream so it reads as a service index rather
   than another feature band. Cards are divided by a
   2px grid gap with the warm hairline showing through.
════════════════════════════════════════════════ */
.svcd-section{padding:112px 48px;background:var(--cream);}
@media(max-width:768px){.svcd-section{padding:72px 24px;}}

.svcd-grid{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:2px;
  margin-top:52px;
  background:var(--border-warm);
  border:1px solid var(--border-warm);
  border-radius:14px;
  overflow:hidden;
}
/* Keep the two-up layout on iPad portrait (744px mini / 834px Pro 11) — it reads
   best two-up, and only phones genuinely need a single column. */
@media(max-width:700px){.svcd-grid{grid-template-columns:1fr;}}

.svcd-card{
  background:var(--cream);
  padding:34px 32px 30px;
  display:flex;flex-direction:column;
  transition:background 0.25s var(--ease);
}
.svcd-card:hover{background:var(--off);}
@media(max-width:768px){.svcd-card{padding:28px 24px 26px;}}

.svcd-eyebrow{
  font-family:var(--sans);font-size:10px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--brown);margin-bottom:12px;
}

.svcd-h2{
  font-family:var(--heading);font-weight:800;
  font-size:clamp(19px,1.9vw,25px);
  line-height:1.2;letter-spacing:-0.005em;
  color:var(--navy);margin:0 0 12px;
  text-wrap:pretty;
}

.svcd-blurb{
  font-size:15.5px;line-height:1.6;color:var(--text-2);
  margin:0 0 22px;text-wrap:pretty;
}

.svcd-link{
  margin-top:auto;
  font-family:var(--heading);font-size:12px;font-weight:700;
  letter-spacing:0.05em;text-transform:uppercase;
  color:var(--brown-dark);text-decoration:none;
  display:inline-flex;align-items:center;gap:8px;
}
.svcd-link:hover{color:var(--navy);}
.svcd-link .arr{transition:transform 0.25s var(--ease);}
.svcd-link:hover .arr{transform:translateX(4px);}

/* iPad portrait — two columns at ~346px each, so tighten padding and type so the
   long service headings don't run to four lines.
   Placed AFTER the base rules above: a media query adds no specificity. */
@media(min-width:701px) and (max-width:900px){
  .svcd-section{padding:88px 28px;}
  .svcd-card{padding:26px 22px 24px;}
  .svcd-h2{font-size:20px;line-height:1.18;}
  .svcd-blurb{font-size:14.5px;margin-bottom:18px;}
}
.svc-card{
  display:flex;flex-direction:column;
  background:transparent;
  border-radius:16px;
  transition:transform var(--dur-base,200ms) var(--ease);
}
.svc-card .v2-photo{
  border-radius:10px;
  margin-bottom:24px;
  aspect-ratio:4/3;
}
.svc-eyebrow{
  font-family:var(--sans);font-size:11px;font-weight:700;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--brown);margin-bottom:12px;
  display:flex;align-items:center;gap:10px;
}
.svc-eyebrow::before{
  content:"";width:24px;height:2px;background:var(--brown);flex:0 0 24px;
}
.svc-name{
  font-family:var(--heading);font-weight:900;
  font-size:28px;line-height:1.1;letter-spacing:-0.01em;
  color:var(--navy);margin:0 0 10px;
}
.svc-blurb{
  font-size:15px;line-height:1.6;color:var(--text-2);
  margin:0 0 20px;
}
.svc-meta{
  display:flex;gap:16px;align-items:center;
  margin-bottom:20px;
}
.svc-meta-item{
  font-family:var(--sans);font-size:12px;font-weight:500;
  color:var(--muted);
}
.svc-meta-dot{
  display:inline-block;width:3px;height:3px;border-radius:50%;
  background:var(--brown);vertical-align:middle;margin:0 4px 3px;
}
.svc-link{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--sans);font-size:14px;font-weight:700;
  color:var(--navy);text-decoration:none;
  border-bottom:1.5px solid var(--brown);padding-bottom:2px;
  align-self:flex-start;
  transition:color var(--dur-base,200ms) var(--ease);
}
.svc-link:hover{color:var(--brown-dark);}
.svc-link .arr{transition:transform var(--dur-base,200ms) var(--ease);}
.svc-card:hover .svc-link .arr{transform:translateX(4px);}

/* New badge */
.svc-new{
  display:inline-block;font-family:var(--sans);font-size:10px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--white);background:var(--brown);
  padding:4px 10px;border-radius:999px;
  margin-left:10px;vertical-align:middle;
  position:relative;top:-2px;
}

/* ════════════════════
   Founder quote band
════════════════════ */
.founder{
  background:var(--navy);
  padding:128px 48px;
  color:#fff;
  position:relative;overflow:hidden;
}
@media(max-width:768px){.founder{padding:80px 24px;}}
.founder-inner{max-width:960px;margin:0 auto;}
.founder-eyebrow{
  font-family:var(--sans);font-size:12px;font-weight:700;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--brown-light);margin-bottom:32px;
  display:inline-flex;align-items:center;gap:12px;
}
.founder-eyebrow::before{
  content:"";width:32px;height:2px;background:var(--brown);
}
.founder-quote{
  font-family:var(--heading);font-weight:900;
  font-size:clamp(32px,3.8vw,52px);
  line-height:1.12;letter-spacing:-0.01em;
  color:#fff;margin:0 0 36px;
  text-wrap:pretty;
  max-width:900px;
}
.founder-attr{
  display:flex;align-items:center;gap:14px;
  font-family:var(--sans);font-size:15px;
  color:rgba(255,255,255,0.72);
}
.founder-attr::before{
  content:"";width:28px;height:1.5px;background:var(--brown);flex:0 0 28px;
}
.founder-attr em{color:#fff;font-style:italic;font-weight:500;}

/* subtle compass watermark (optional) */
.founder-mark{
  position:absolute;
  right:-60px;bottom:-60px;
  width:360px;height:360px;
  opacity:0.06;
  pointer-events:none;
}

/* ════════════════════
   Recent work grid (2-up with meta)
════════════════════ */
.work-head{
  display:flex;justify-content:space-between;align-items:flex-end;
  gap:32px;margin-bottom:48px;flex-wrap:wrap;
}
.work-head .v2-header{margin-bottom:0;}
.work-viewall{
  font-family:var(--sans);font-size:14px;font-weight:700;
  color:var(--navy);text-decoration:none;
  display:inline-flex;align-items:center;gap:6px;
  border-bottom:1.5px solid var(--brown);padding-bottom:2px;
  transition:color var(--dur-base,200ms) var(--ease);
}
.work-viewall:hover{color:var(--brown-dark);}
.work-viewall .arr{transition:transform var(--dur-base,200ms) var(--ease);}
.work-viewall:hover .arr{transform:translateX(4px);}

.work-grid{
  display:grid;grid-template-columns:1fr 1fr;
  gap:32px;
}
@media(max-width:800px){.work-grid{grid-template-columns:1fr;}}
.work-card{display:flex;flex-direction:column;cursor:pointer;text-decoration:none;color:inherit;}
.work-card .v2-photo{
  aspect-ratio:4/3;
  border-radius:10px;
  margin-bottom:20px;
  transition:transform var(--dur-base,200ms) var(--ease);
}
.work-card:hover .v2-photo{transform:translateY(-4px);}
.work-meta{
  font-family:var(--sans);font-size:11px;font-weight:700;
  letter-spacing:0.14em;text-transform:uppercase;
  color:var(--muted);margin-bottom:8px;
}
.work-meta .dot{
  display:inline-block;width:3px;height:3px;border-radius:50%;
  background:var(--brown);vertical-align:middle;margin:0 8px 3px;
}
.work-meta .svc-type{color:var(--brown);}
.work-title{
  font-family:var(--sans);font-size:17px;font-weight:700;
  color:var(--navy);line-height:1.4;
  margin:0;text-wrap:pretty;
}

/* ════════════════════
   Testimonial (asymmetric image + pullquote)
════════════════════ */
.testi-grid{
  display:grid;grid-template-columns:5fr 7fr;
  gap:80px;align-items:center;
}
@media(max-width:900px){.testi-grid{grid-template-columns:1fr;gap:40px;}}
.testi-photo{aspect-ratio:4/5;}
.testi-body{max-width:640px;}
.testi-quote{
  font-family:var(--heading);font-weight:900;
  font-size:clamp(24px,2.6vw,34px);
  line-height:1.28;letter-spacing:-0.005em;
  color:var(--navy);margin:24px 0 24px;
  text-wrap:pretty;
}
.testi-attr{
  display:flex;align-items:center;gap:14px;
  font-family:var(--sans);font-size:14px;
  color:var(--muted);
}
.testi-attr::before{
  content:"";width:28px;height:1.5px;background:var(--brown);flex:0 0 28px;
}
.testi-attr em{color:var(--text);font-style:italic;font-weight:500;}

/* ════════════════════
   FAQ (2-col, accordion)
════════════════════ */
.faq-grid{
  display:grid;grid-template-columns:5fr 7fr;
  gap:80px;align-items:start;
}
@media(max-width:900px){.faq-grid{grid-template-columns:1fr;gap:40px;}}
.faq-list{display:flex;flex-direction:column;}
.faq-item{
  border-top:1px solid var(--border-warm);
  padding:24px 0;
}
.faq-item:last-child{border-bottom:1px solid var(--border-warm);}
.faq-q{
  all:unset;cursor:pointer;
  display:flex;justify-content:space-between;align-items:center;gap:24px;
  font-family:var(--sans);font-size:17px;font-weight:700;
  color:var(--navy);line-height:1.4;width:100%;
}
.faq-q .faq-icon{
  flex:0 0 20px;width:20px;height:20px;position:relative;
  transition:transform var(--dur-base,200ms) var(--ease);
}
.faq-q .faq-icon::before,
.faq-q .faq-icon::after{
  content:"";position:absolute;background:var(--brown);
  left:50%;top:50%;transform:translate(-50%,-50%);
}
.faq-q .faq-icon::before{width:14px;height:2px;}
.faq-q .faq-icon::after{width:2px;height:14px;transition:transform var(--dur-base,200ms) var(--ease);}
.faq-item.open .faq-q .faq-icon::after{transform:translate(-50%,-50%) scaleY(0);}
.faq-a{
  max-height:0;overflow:hidden;
  transition:max-height var(--dur-slow,320ms) var(--ease);
}
.faq-a-inner{
  padding:16px 0 0;
  font-size:15px;line-height:1.65;color:var(--text-2);
  max-width:560px;
}
.faq-item.open .faq-a{max-height:320px;}

/* ════════════════════
   Navy contact band (V2)
════════════════════ */
.v2-contact{
  background:var(--navy);
  padding:96px 48px;
  color:#fff;
}
@media(max-width:768px){.v2-contact{padding:72px 24px;}}
.v2-contact-grid{
  max-width:var(--max);margin:0 auto;
  display:grid;grid-template-columns:5fr 7fr;
  gap:80px;align-items:start;
}
@media(max-width:900px){.v2-contact-grid{grid-template-columns:1fr;gap:48px;}}
.v2-contact-heading{
  font-family:var(--heading);font-weight:900;
  font-size:clamp(36px,4.2vw,56px);
  line-height:1.02;letter-spacing:-0.01em;
  color:#fff;margin:18px 0 24px;
}
.v2-contact-heading em{
  color:var(--brown-light);font-style:italic;font-weight:500;
  font-family:var(--sans);
}
.v2-contact-eyebrow{
  font-family:var(--sans);font-size:12px;font-weight:700;
  letter-spacing:0.18em;text-transform:uppercase;
  color:var(--brown-light);
  display:inline-flex;align-items:center;gap:12px;
}
.v2-contact-eyebrow::before{
  content:"";width:32px;height:2px;background:var(--brown);
}
.v2-contact-copy{
  font-size:16px;line-height:1.65;color:rgba(255,255,255,0.72);
  margin:0 0 32px;max-width:420px;
}
.v2-contact-meta{
  font-size:14px;color:rgba(255,255,255,0.55);line-height:1.8;
}
.v2-contact-meta strong{color:#fff;font-weight:600;}
.v2-contact-meta a{color:rgba(255,255,255,0.85);text-decoration:none;border-bottom:1px solid rgba(167,120,69,0.5);padding-bottom:1px;}
.v2-contact-meta a:hover{color:var(--brown-light);}

/* Form */
.v2-form{
  background:rgba(255,255,255,0.04);
  border:1px solid rgba(255,255,255,0.1);
  border-radius:16px;
  padding:32px;
}
.v2-form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px;}
@media(max-width:600px){.v2-form-row{grid-template-columns:1fr;}}
.v2-field{display:flex;flex-direction:column;gap:6px;}
.v2-field label{
  font-family:var(--sans);font-size:12px;font-weight:600;
  color:rgba(255,255,255,0.72);letter-spacing:0.04em;
}
.v2-field input,
.v2-field select,
.v2-field textarea{
  width:100%;
  background:rgba(255,255,255,0.05);
  border:1px solid rgba(255,255,255,0.14);
  border-radius:6px;
  color:#fff;padding:12px 14px;
  font-family:var(--sans);font-size:14px;line-height:1.5;
  transition:border-color var(--dur-base,200ms) var(--ease),background var(--dur-base,200ms) var(--ease);
  outline:none;
}
.v2-field textarea{min-height:100px;resize:vertical;}
.v2-field input::placeholder,
.v2-field textarea::placeholder{color:rgba(255,255,255,0.35);}
.v2-field input:focus,
.v2-field select:focus,
.v2-field textarea:focus{
  border-color:var(--brown-light);
  background:rgba(255,255,255,0.08);
  box-shadow:0 0 0 3px rgba(167,120,69,0.28);
}
.v2-field select{appearance:none;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='none' stroke='%23BB8C57' stroke-width='1.75' d='M1 1l5 5 5-5'/></svg>");background-repeat:no-repeat;background-position:right 16px center;padding-right:40px;}
.v2-submit{
  display:inline-flex;align-items:center;gap:10px;
  background:var(--brown);color:#fff;
  border:none;border-radius:8px;
  padding:14px 28px;
  font-family:var(--sans);font-size:14px;font-weight:700;
  letter-spacing:0.02em;
  cursor:pointer;
  transition:background var(--dur-base,200ms) var(--ease);
  margin-top:8px;
}
.v2-submit:hover{background:var(--brown-light);}
.v2-submit .arr{transition:transform var(--dur-base,200ms) var(--ease);}
.v2-submit:hover .arr{transform:translateX(3px);}

/* Tweaks panel CSS removed alongside the edit-mode IIFE it styled — that block
   injected a floating panel, wrote --cream/--brown/--brown-dark inline onto
   :root (shadowing the tokens declared above), reached for the homepage .hero
   via a bare querySelector, and postMessage'd to window.parent with a wildcard
   origin. Prototype scaffolding from the tool that generated the widget; it had
   no business running on a production page. */


/* ── HERO — clean white gradient ── */

/* ══ ESTIMATOR — SCOPED UNDER .est-root ══ */
.est-root { display:block; width:100%; height:auto !important; min-height:0 !important; }
.est-root .widget {
  box-shadow:none;
  border-radius:20px;
  height:auto !important;
  min-height:0 !important;
  overflow:visible !important;
}






/* ── WIDGET SHELL ── */
.est-root .widget {
  width:100%;max-width:860px;
  background:var(--white);
  border-radius:22px;
  box-shadow:var(--shadow-lg);
  overflow:hidden;
}


/* The widget's internal hero lived here (~45 lines: .est-hero, .hero-left,
   .hero-eyebrow, .hero-title, .hero-sub, .hero-trust, .hero-cta-*) along with a
   trust-pill strip that briefly replaced it. All removed — the hero restated the
   section header above it almost verbatim, carried a second competing CTA, and
   cost 407px on a phone; the pills then restated "no commitment, no pressure"
   from the same header. The widget now opens on the progress bar. */


/* ── PROGRESS ── */
.est-root .progress-bar {height:3px;background:var(--border);}

.est-root .progress-fill {height:100%;background:linear-gradient(to right,var(--brown),var(--brown-light));transition:width 0.4s var(--ease);}


/* ── STEP NAV DOTS ── */
.est-root .step-nav {display:flex;align-items:center;padding:0 52px;border-bottom:1px solid var(--border);background:var(--white);overflow-x:auto;gap:0;}

@media(max-width:640px){.est-root .step-nav {padding:0 28px;}
}
.est-root .step-dot {display:flex;align-items:center;gap:7px;padding:14px 0;cursor:default;opacity:0.35;transition:opacity 0.3s;white-space:nowrap;flex-shrink:0;}

.est-root .step-dot:not(:last-child) {margin-right:24px;}

.est-root .step-dot.active {opacity:1;}

.est-root .step-dot.done {opacity:0.7;}

.est-root .dot-num {width:22px;height:22px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-family:var(--heading);font-size:10px;font-weight:700;color:var(--muted);transition:all 0.3s;flex-shrink:0;}

.est-root .step-dot.active .dot-num {background:var(--navy);color:white;}

.est-root .step-dot.done .dot-num {background:var(--brown);color:white;}

.est-root .dot-lbl {font-family:var(--heading);font-size:10px;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:var(--muted);}

.est-root .step-dot.active .dot-lbl {color:var(--navy);}


/* ── STEP BODY ── */
.est-root .w-body {padding:40px 52px 44px;}

@media(max-width:640px){.est-root .w-body {padding:28px 28px 36px;}
}
.est-root .step-panel {display:none;}

.est-root .step-panel.active {display:block;}


/* ── STEP TYPE/HEADINGS ── */
.est-root .step-heading {font-family:var(--heading);font-size:22px;font-weight:800;color:var(--navy);letter-spacing:-0.02em;margin-bottom:7px;}

.est-root .step-sub {font-size:14px;color:var(--muted);line-height:1.6;margin-bottom:28px;max-width:560px;}


/* ── CATEGORY GRID (Step 1 — 3 big tiles) ── */
.est-root .cat-grid {display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-bottom:28px;}

@media(max-width:640px){.est-root .cat-grid {grid-template-columns:1fr;}
}
.est-root .cat-card {
  border:2px solid var(--border);border-radius:16px;
  padding:28px 22px;cursor:pointer;
  transition:all 0.22s var(--ease);
  position:relative;overflow:hidden;
  display:flex;flex-direction:column;gap:10px;
  background:var(--white);
}

.est-root .cat-card:hover {border-color:var(--brown-border);box-shadow:0 4px 20px rgba(22,16,68,0.07);}

.est-root .cat-card.selected {border-color:var(--navy);background:linear-gradient(135deg,#f8f6ff 0%,#f0edfa 100%);}

.est-root .cat-card.selected::after {content:'✓';position:absolute;top:14px;right:14px;width:22px;height:22px;border-radius:50%;background:var(--navy);color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;font-family:var(--heading);}

.est-root .cat-icon {font-size:32px;}

/* Sits inline after the card name rather than on its own row above it — that row
   cost ~26px per card and pushed My Exterior further down the page. The ✦ went
   too: fussy at 8px and it widened the badge for no information. */
.est-root .cat-new {
  display:inline-block;
  background:var(--brown);color:#fff;
  font-family:var(--heading);font-size:8px;font-weight:700;
  letter-spacing:0.12em;text-transform:uppercase;
  padding:3px 7px;border-radius:20px;
  vertical-align:2px;      /* optically centres against the 17px cap height */
  margin-left:6px;
  white-space:nowrap;
}

.est-root .cat-name {font-family:var(--heading);font-size:17px;font-weight:800;color:var(--navy);letter-spacing:-0.02em;line-height:1.2;}

/* ── Category card photos ──────────────────────────────────
   Real photos rather than emoji: renovation is a visual purchase and the card is
   selling a finished room. Replaces the emoji discs, which were always a
   placeholder.

   DESKTOP: photo on top. Cards sit in a 3-up grid at ~270px each, so a side-by-
   side split would leave too little room for the copy.

   MOBILE: photo BESIDE the copy (see the media query further down). A photo above
   the text costs 100–300px of height and would push My Exterior back below the
   fold; beside it, the photo fits inside the height the name + sub + pills already
   occupy, so it costs nothing.

   Source files are 240×240 centre crops (12–20KB) derived from the existing
   hero-*.jpg, which were 1024×1536 at 108–145KB — 10–15× oversized for this.
─────────────────────────────────────────────────────────── */
.est-root .cat-photo{
  display:block;width:100%;height:120px;
  object-fit:cover;border-radius:10px;
  background:var(--grey);
}

/* Emoji disc — the MOBILE stand-in for the photo above. Hidden on desktop. */
.est-root .cat-icon{
  display:none;
  align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:50%;
  background:var(--brown-pale);
  font-size:14px;line-height:1;
  margin-right:9px;vertical-align:-8px;
  flex-shrink:0;
}

.est-root .cat-sub {font-size:13px;color:var(--muted);line-height:1.55;}

.est-root .cat-pills {display:flex;flex-wrap:wrap;gap:5px;margin-top:2px;}

.est-root .cat-pill {font-family:var(--heading);font-size:8px;font-weight:600;letter-spacing:0.07em;text-transform:uppercase;color:var(--muted);border:1px solid var(--border);padding:3px 9px;border-radius:20px;}

/* ── Category cards · mobile density ──────────────────────
   Stacked one-per-row on a phone, so every px of card chrome pushes My Exterior
   further below the fold. Tightening padding, internal gap and grid gap recovers
   roughly 70px across the three cards without touching type sizes.

   AFTER the base rules above — a media query adds no specificity.
─────────────────────────────────────────────────────────── */
@media(max-width:768px){
  .est-root .cat-grid{gap:10px;margin-bottom:20px;}
  .est-root .cat-sub{font-size:12.5px;line-height:1.5;}
  .est-root .cat-pills{gap:4px;}

  /* ── Photos off, emoji on ────────────────────────────────
     Photos were tried here and measured badly. At a 331px card there is no
     layout where an image is free:

       emoji, no photo   147px cards  → all three visible
       photo BESIDE copy 187px cards  → exterior partly cut  (96px photo leaves
                                        174px for copy, so the pills wrap to two
                                        rows and the text column outgrows the
                                        photo it was meant to hide behind)
       photo ABOVE copy  248px cards  → exterior off screen entirely

     So the emoji disc carries mobile and the photo is desktop-only, where cards
     sit 3-up with room to spare. Same markup, one shown per breakpoint. */
  .est-root .cat-photo{display:none;}
  .est-root .cat-icon{display:inline-flex;}
  .est-root .cat-card{padding:18px 16px;gap:7px;border-radius:14px;}
}


/* ── SERVICE GRID (Step 2 — within category) ── */
.est-root .service-grid {display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:28px;}

@media(max-width:580px){.est-root .service-grid {grid-template-columns:1fr;}
}
.est-root .service-card {
  border:2px solid var(--border);border-radius:var(--r);
  padding:22px;cursor:pointer;
  transition:all 0.22s var(--ease);
  position:relative;
  background:var(--white);
}

.est-root .service-card:hover {border-color:var(--brown-border);box-shadow:var(--shadow-md);}

.est-root .service-card.selected {border-color:var(--navy);background:linear-gradient(135deg,#f8f6ff 0%,#f0edfa 100%);}

.est-root .sc-tick {position:absolute;top:14px;right:14px;width:20px;height:20px;border-radius:50%;background:var(--border);font-size:9px;font-weight:700;color:transparent;display:flex;align-items:center;justify-content:center;transition:all 0.2s;font-family:var(--heading);}

.est-root .service-card.selected .sc-tick {background:var(--navy);color:white;}

.est-root .sc-eyebrow {font-family:var(--heading);font-size:9px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:var(--brown);margin-bottom:6px;display:flex;align-items:center;gap:6px;}

.est-root .sc-popular {background:var(--brown-pale);color:var(--brown);font-size:8px;padding:2px 7px;border-radius:20px;letter-spacing:0.08em;}

.est-root .sc-name {font-family:var(--heading);font-size:16px;font-weight:800;color:var(--navy);line-height:1.2;letter-spacing:-0.015em;margin-bottom:8px;}

.est-root .sc-tagline {font-size:13px;color:var(--muted);line-height:1.55;margin-bottom:10px;}

.est-root .sc-divider {height:1px;background:var(--border);margin-bottom:10px;}

.est-root .sc-pills {display:flex;flex-wrap:wrap;gap:5px;}

.est-root .sc-pill {font-family:var(--heading);font-size:8px;font-weight:600;letter-spacing:0.07em;text-transform:uppercase;color:var(--muted);border:1px solid var(--border);padding:3px 8px;border-radius:20px;}


/* ── MULTI-SELECT GRID (checkboxes for exterior) ── */
.est-root .multi-grid {display:grid;grid-template-columns:1fr;gap:10px;margin-bottom:28px;}

.est-root .multi-card {
  border:2px solid var(--border);border-radius:var(--r);
  padding:18px 20px;cursor:pointer;
  transition:all 0.22s var(--ease);
  display:flex;align-items:center;gap:16px;
  background:var(--white);
}

.est-root .multi-card:hover {border-color:var(--brown-border);}

.est-root .multi-card.selected {border-color:var(--navy);background:linear-gradient(135deg,#f8f6ff 0%,#f0edfa 100%);}

.est-root .mc-check {
  width:22px;height:22px;border-radius:6px;
  border:2px solid var(--border);
  display:flex;align-items:center;justify-content:center;
  font-size:12px;color:transparent;flex-shrink:0;
  transition:all 0.2s;font-family:var(--heading);font-weight:700;
}

.est-root .multi-card.selected .mc-check {background:var(--navy);border-color:var(--navy);color:white;}

.est-root .mc-body {flex:1;}

.est-root .mc-name {font-family:var(--heading);font-size:14px;font-weight:700;color:var(--navy);margin-bottom:3px;letter-spacing:-0.01em;}

.est-root .mc-desc {font-size:13px;color:var(--muted);line-height:1.5;}


/* ── MATERIAL / OPTION GRID ── */
.est-root .option-grid {display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:28px;}

@media(max-width:580px){.est-root .option-grid {grid-template-columns:1fr;}
}
.est-root .option-card {
  border:2px solid var(--border);border-radius:var(--r);
  padding:18px 16px;cursor:pointer;
  transition:all 0.22s var(--ease);
  position:relative;background:var(--white);
}

.est-root .option-card:hover {border-color:var(--brown-border);}

.est-root .option-card.selected {border-color:var(--navy);background:linear-gradient(135deg,#f8f6ff 0%,#f0edfa 100%);}

.est-root .option-card.selected::after {content:'✓';position:absolute;top:10px;right:10px;width:18px;height:18px;border-radius:50%;background:var(--navy);color:white;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;font-family:var(--heading);}

.est-root .oc-icon {font-size:22px;margin-bottom:8px;}

.est-root .oc-tier {font-family:var(--heading);font-size:8px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;margin-bottom:4px;}

.est-root .oc-tier.budget {color:#22a06b;}

.est-root .oc-tier.mid {color:var(--brown);}

.est-root .oc-tier.premium {color:var(--navy);}

.est-root .oc-name {font-family:var(--heading);font-size:14px;font-weight:700;color:var(--navy);margin-bottom:4px;letter-spacing:-0.01em;}

.est-root .oc-brand {font-size:11px;color:var(--light);margin-bottom:6px;font-style:italic;}

.est-root .oc-desc {font-size:12px;color:var(--muted);line-height:1.5;}


/* ── YES/NO BINARY CARDS ── */
.est-root .binary-grid {display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:28px;}

.est-root .binary-card {
  border:2px solid var(--border);border-radius:var(--r);
  padding:20px;cursor:pointer;text-align:center;
  transition:all 0.22s var(--ease);background:var(--white);
  display:flex;flex-direction:column;align-items:center;gap:8px;
}

.est-root .binary-card:hover {border-color:var(--brown-border);}

.est-root .binary-card.selected {border-color:var(--navy);background:linear-gradient(135deg,#f8f6ff 0%,#f0edfa 100%);}

.est-root .bc-icon {font-size:28px;}

.est-root .bc-label {font-family:var(--heading);font-size:15px;font-weight:700;color:var(--navy);}

.est-root .bc-sub {font-size:12px;color:var(--muted);line-height:1.5;}


/* ── UPGRADE CARDS ── */
/* ── Upgrade card photos ───────────────────────────────────
   Half these labels are trade jargon ("Headers & Crown", "LeMans Corner System",
   "Appliance Garage", "Built-In Niche") — a photo is the explanation, not
   decoration, so unlike the category cards it earns its height here.

   Rendered by northco_upgrade_photo() in functions.php: a real <img> once
   assets/images/upgrades/upg-<key>.jpg exists, otherwise the labelled placeholder
   below. Both use .uc-photo so the layout is identical either way and swapping in
   real photos can't shift anything.
─────────────────────────────────────────────────────────── */
.est-root .uc-photo{
  display:block;width:100%;
  /* height:auto is load-bearing. The <img> carries width="480" height="300" —
     which reserves space and prevents layout shift — but those attributes are
     presentational hints that map to CSS width/height, and an explicit height
     BEATS aspect-ratio. Without this the image rendered 208x300 instead of
     208x130, and since grid rows stretch to the tallest cell, one real photo
     inflated every card in its row from 248px to 427px. */
  height:auto;
  aspect-ratio:16 / 10;
  object-fit:cover;
  border-radius:9px;
  margin-bottom:11px;
  background:var(--grey);
}

/* Obvious-on-purpose placeholder — dashed, tinted, captioned, so an unshot
   upgrade is impossible to miss in review and can't quietly ship. */
.est-root .uc-photo-ph{
  display:flex;align-items:center;justify-content:center;
  padding:8px;
  background:repeating-linear-gradient(
    135deg, var(--brown-pale) 0 8px, #EFE4D2 8px 16px );
  border:1.5px dashed var(--brown-border, rgba(167,120,69,.45));
}
.est-root .uc-photo-ph span{
  font-family:var(--heading);font-size:9px;font-weight:700;
  letter-spacing:0.08em;text-transform:uppercase;
  color:var(--brown-dark);text-align:center;line-height:1.35;
}

.est-root .upgrade-grid {display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:28px;}

@media(max-width:640px){.est-root .upgrade-grid {grid-template-columns:1fr 1fr;}
}
@media(max-width:380px){.est-root .upgrade-grid {grid-template-columns:1fr;}
}
.est-root .upgrade-card {
  border:2px solid var(--border);border-radius:var(--r);
  padding:16px;cursor:pointer;
  transition:all 0.22s var(--ease);
  position:relative;background:var(--white);
  display:flex;flex-direction:column;gap:5px;
}

.est-root .upgrade-card:hover {border-color:var(--brown-border);}

.est-root .upgrade-card.selected {border-color:var(--brown);background:var(--brown-pale);}

.est-root .uc-badge {position:absolute;top:10px;right:10px;width:18px;height:18px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;color:transparent;transition:all 0.2s;font-family:var(--heading);}

.est-root .upgrade-card.selected .uc-badge {background:var(--brown);color:white;}

.est-root .uc-icon {font-size:20px;}

.est-root .uc-name {font-family:var(--heading);font-size:12px;font-weight:700;color:var(--navy);line-height:1.3;letter-spacing:-0.01em;}

.est-root .uc-desc {font-size:11px;color:var(--muted);line-height:1.5;}

.est-root .uc-add {font-family:var(--heading);font-size:9px;font-weight:700;color:var(--brown);letter-spacing:0.06em;text-transform:uppercase;margin-top:2px;}


/* ── CONTACT FORM ── */
.est-root .contact-form {display:flex;flex-direction:column;gap:12px;margin-bottom:28px;}

.est-root .form-row-2 {display:grid;grid-template-columns:1fr 1fr;gap:12px;}

@media(max-width:500px){.est-root .form-row-2 {grid-template-columns:1fr;}
}
.est-root .form-input, .est-root .form-select {
  width:100%;padding:13px 15px;
  border:1.5px solid var(--border);border-radius:10px;
  font-size:14px;font-family:var(--sans);
  color:var(--text);background:var(--white);
  transition:border-color 0.2s;outline:none;
}

.est-root .form-input:focus-visible, .est-root .form-select:focus-visible {
  border-color:var(--navy);box-shadow:0 0 0 3px rgba(167,120,69,0.28);
}
.est-root .form-input:focus, .est-root .form-select:focus {border-color:var(--navy);}

.est-root .form-input::placeholder {color:var(--light);}

.est-root .form-select {color:var(--muted);cursor:pointer;}

.est-root .form-select option {color:var(--text);}


/* ── UPSELL NUDGE ── */
.est-root .upsell-nudge {
  display:none;
  background:linear-gradient(135deg,var(--brown-pale),#f0edfa);
  border:1.5px solid var(--brown-border);
  border-radius:var(--r);padding:18px 20px;
  margin-bottom:24px;
  gap:14px;align-items:flex-start;
}

.est-root .upsell-nudge.show {display:flex;}

/* .nudge-icon was the styled class but NOTHING in the markup uses it — the nudge
   renders .nudge-indicator and .nudge-wrap, both of which had no rule at all. So
   the arrow was unstyled body text and the content column couldn't grow. */
.est-root .nudge-indicator{
  flex:0 0 26px;
  width:26px;height:26px;border-radius:50%;
  background:var(--brown);color:#fff;
  display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:700;line-height:1;
  margin-top:1px;
}
.est-root .nudge-wrap{flex:1 1 auto;min-width:0;}

.est-root .nudge-title {font-family:var(--heading);font-size:13px;font-weight:700;color:var(--navy);margin-bottom:5px;}

.est-root .nudge-body {font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:10px;}

.est-root .nudge-cta {background:none;border:none;font-family:var(--heading);font-size:12px;font-weight:700;color:var(--navy);cursor:pointer;letter-spacing:0.04em;text-decoration:underline;text-underline-offset:3px;padding:0;}

.est-root .nudge-cta:hover {color:var(--brown);}


/* ── SUMMARY / ESTIMATE RESULT ── */
.est-root .estimate-result {
  background:linear-gradient(135deg,var(--navy) 0%,#1e1660 100%);
  border-radius:var(--r);padding:36px;
  color:white;margin-bottom:28px;
  position:relative;overflow:hidden;
}

.est-root .estimate-result::before {content:'';position:absolute;top:-60px;right:-60px;width:200px;height:200px;border-radius:50%;border:1.5px solid rgba(255,255,255,0.06);}

.est-root .er-label {font-family:var(--heading);font-size:9px;font-weight:700;letter-spacing:0.2em;text-transform:uppercase;color:rgba(255,255,255,0.5);margin-bottom:8px;}

.est-root .er-range {font-family:var(--heading);font-size:clamp(28px,5vw,42px);font-weight:800;color:white;letter-spacing:-0.025em;line-height:1;margin-bottom:8px;}

.est-root .er-range span {color:var(--brown-light);}

.est-root .er-note {font-size:13px;color:rgba(255,255,255,0.5);margin-bottom:24px;line-height:1.55;}

.est-root .er-breakdown {display:flex;flex-direction:column;gap:8px;}

.est-root .er-line {display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:rgba(255,255,255,0.06);border-radius:8px;}

.est-root .er-line-label {font-size:13px;color:rgba(255,255,255,0.7);}

.est-root .er-line-val {font-family:var(--heading);font-size:13px;font-weight:700;color:var(--brown-light);}


/* ── NEXT STEPS ── */
.est-root .next-steps {display:flex;flex-direction:column;gap:10px;margin-bottom:28px;}

.est-root .ns-item {display:flex;align-items:flex-start;gap:14px;padding:14px 16px;background:var(--off);border-radius:10px;border:1px solid var(--border);}

.est-root .ns-num {width:24px;height:24px;border-radius:50%;background:var(--navy);color:white;display:flex;align-items:center;justify-content:center;font-family:var(--heading);font-size:10px;font-weight:700;flex-shrink:0;}

.est-root .ns-text {font-size:14px;color:var(--muted);line-height:1.6;padding-top:2px;}


/* ── SECTION DIVIDER ── */
.est-root .section-divider {display:flex;align-items:center;gap:12px;margin:20px 0 24px;}

.est-root .section-divider-line {flex:1;height:1px;background:var(--border);}

.est-root .section-divider-label {font-family:var(--heading);font-size:9px;font-weight:700;letter-spacing:0.16em;text-transform:uppercase;color:var(--light);}


/* ── BUTTONS ── */
.est-root .btn-row {display:flex;gap:12px;justify-content:space-between;align-items:center;}

/* Mobile: the sticky pill IS the Continue button, so the in-panel one is a
   duplicate sitting a few pixels above it. Back and Skip stay — the pill only
   replaces the forward action.

   Gated on body.est-pill-nav, which nav.js only sets once the pill has actually
   taken over. If that script fails the in-panel button remains, so there's never
   a state with no way forward. Desktop keeps it either way. */
@media(max-width:900px){
  body.est-pill-nav .est-root .btn-next{display:none;}
  /* With the forward button gone, Back sits left and Skip right. */
  body.est-pill-nav .est-root .btn-row{justify-content:space-between;}
  body.est-pill-nav .est-root .btn-row > div:empty{display:none;}
}

.est-root .btn-back {font-family:var(--sans);font-size:14px;color:var(--muted);background:none;border:none;cursor:pointer;display:flex;align-items:center;gap:6px;transition:color 0.2s;padding:4px 0;}

.est-root .btn-back:hover {color:var(--navy);}

.est-root .btn-next {background:var(--navy);color:var(--white);border:none;padding:15px 32px;border-radius:10px;font-family:var(--heading);font-size:14px;font-weight:700;cursor:pointer;transition:all 0.2s var(--ease);display:flex;align-items:center;gap:9px;letter-spacing:0.03em;}

.est-root .btn-next:hover {background:#1e1660;transform:translateY(-1px);box-shadow:0 6px 22px rgba(22,16,68,0.22);}

.est-root .btn-next:disabled {opacity:0.35;cursor:default;transform:none;box-shadow:none;}

.est-root .btn-next.gold {background:var(--brown);}

.est-root .btn-next.gold:hover {background:var(--brown-light);box-shadow:0 6px 22px rgba(167,120,69,0.3);}

.est-root .btn-arrow {transition:transform 0.2s;}

.est-root .btn-next:hover:not(:disabled) .btn-arrow {transform:translateX(3px);}

.est-root .btn-skip {font-family:var(--sans);font-size:13px;color:var(--light);background:none;border:none;cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color 0.2s;}

.est-root .btn-skip:hover {color:var(--muted);}


@media(max-width:600px){
  .est-root .step-nav {padding:0 28px;}

  .est-root .w-body {padding:28px 28px 36px;}

  .est-root .estimate-result {padding:24px;}

  .est-root .cat-grid, .est-root .option-grid {grid-template-columns:1fr;}

  .est-root .upgrade-grid {grid-template-columns:1fr 1fr;}

}


/* ══════════════════════════════════════════════
   ESTIMATOR · MOBILE
   The widget had never been reviewed below desktop width. Two
   things were badly wrong at 402px:

   1. .estimator-section kept its desktop 48px side padding while
      every other section drops to 24px — so the widget got only
      303px of a 402px screen, 99px lost to nesting alone.

   2. Six labelled step dots overflowed 2.3× (692px of content in
      a 303px box). overflow-x:auto meant it technically scrolled,
      but nothing signalled that and the active step was never
      scrolled into view — on step 4 you were still looking at
      steps 1–2.

   Placed at the END of the estimator CSS on purpose: a media
   query adds no specificity, so overrides must follow the base
   rules they replace.
════════════════════════════════════════════════ */
@media(max-width:768px){

  /* ── 1 · Reclaim the width ─────────────────────────────── */
  .estimator-section{padding:56px 14px;}
  .estimator-header{margin-bottom:28px;}

  /* ── 2 · Numbers-only step nav ─────────────────────────────
     6 × 22px dots + a single active label ≈ 194px, which fits the
     reclaimed width comfortably, so the nav no longer scrolls.
     .active / .done are already applied by updateDots(), so the
     label swap needs no JS. */
  .est-root .step-nav{
    padding:0 14px;
    justify-content:space-between;
    overflow-x:visible;
  }
  .est-root .step-dot,
  .est-root .step-dot:not(:last-child){
    margin-right:0;
    flex-shrink:1;
    min-width:0;
  }
  .est-root .dot-lbl{display:none;}
  .est-root .step-dot.active .dot-lbl{
    display:inline;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    color:var(--brown-dark);
  }

  /* The progress bar becomes the primary position cue once the
     other five labels are hidden, so give it more presence. */
  .est-root .progress-bar{height:4px;}
}




/* ══════════════════════════════════════════════
   iOS INPUT ZOOM FIX
   ══════════════════════════════════════════════

   iOS Safari automatically zooms the page when you focus an input whose computed
   font-size is under 16px — and the zoom PERSISTS after the field loses focus. In
   the estimator that meant filling in step 5 and arriving at your estimate on
   step 6 zoomed in, with no obvious way back out short of pinching.

   Every field in the theme was 14px, so this affected all three forms: the
   homepage lead form, the navy contact band, and the estimator.

   16px is the exact threshold — at 16px there is no zoom. Mobile only; desktop
   keeps 14px, where it reads better.

   The other "fix" for this is maximum-scale=1 on the viewport meta. Do NOT do
   that — it disables pinch zoom for everyone, which is an accessibility failure.
   Raising the font size is the correct fix.

   Placed at the very END of the file deliberately: a media query adds no
   specificity, so this has to follow every base rule it overrides.
════════════════════════════════════════════════ */
@media(max-width:900px){
  .fi,
  .fi-select,
  .fi-textarea,
  .v2-field input,
  .v2-field select,
  .v2-field textarea,
  .est-root .form-input,
  .est-root .form-select,
  .est-root .form-textarea{
    font-size:16px;
  }
}

/* ── Skip link ───────────────────────────────────────────────────────────────────
   Clipped off-screen, NOT display:none — a display:none element is removed from the tab order,
   which would make the skip link unreachable and therefore pointless. It becomes visible only
   on :focus-visible, so it never appears for mouse users. */
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:2000;
  font-family:var(--sans);font-size:14px;font-weight:600;
  background:var(--navy);color:#fff;
  padding:12px 18px;border-radius:0 0 6px 0;
  text-decoration:none;
}
.skip-link:focus-visible,
.skip-link:focus{
  left:0;
  outline:2px solid var(--brown-light);outline-offset:2px;
}
