/* ==========================================================================
   Tri-Star Seamless Gutters — site styles
   Mobile first. Direction B "Stage & Float".
   ========================================================================== */

*,*::before,*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}
@media (min-width: 720px) { body { font-size: 17.5px; } }

/* height:auto is load-bearing. The width/height attributes land as presentational
   hints, and a definite height makes the browser ignore aspect-ratio entirely. */
img, svg { display: block; max-width: 100%; height: auto; }

/* author display:block above beats the UA sheet's [hidden] rule, so restate it */
[hidden] { display: none !important; }
a { color: var(--blue); }

h1,h2,h3,h4 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: .005em;
  margin: 0;
  text-wrap: balance;
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--navy); color: #fff; padding: 12px 18px;
  font-weight: 700; text-decoration: none;
}
.skip-link:focus { left: 8px; top: 8px; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ---------- shared type ---------- */
.eyebrow {
  font-family: var(--font-display); font-weight: 600;
  font-size: 14px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--red-a11y); margin: 0;
}
@media (min-width: 720px) { .eyebrow { font-size: 15px; } }
.eyebrow--dark { color: var(--on-dark-eyebrow); }

.lede { font-size: 1.0625rem; line-height: 1.6; color: var(--slate); margin: 12px 0 0; }
@media (min-width: 720px) { .lede { font-size: 1.125rem; } }

.section { padding-block: var(--sec-y); }
.section--tint { background: var(--off-white); }

/* The 64ch cap used to sit on .section-head itself, which held the heading to
   683px of the 1152px available and pushed most H2s onto two lines. The cap
   belongs on the running text, not on the block: the heading is scanned, the
   paragraph is read. Headings now run to the container; ledes keep their measure. */
.section-head { margin-bottom: 28px; }
.section-head h2 { font-size: 1.95rem; margin-top: 10px; }
@media (min-width: 720px) { .section-head h2 { font-size: 2.6rem; } .section-head { margin-bottom: 36px; } }
.section-head p { margin-bottom: 0; max-width: var(--measure); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 13px 22px;
  font-family: var(--font-body); font-weight: 700; font-size: 1rem;
  border: 0; border-radius: var(--radius-btn); cursor: pointer;
  text-decoration: none; transition: background-color .18s ease;
}
.btn--primary { background: var(--red); color: #fff; }
.btn--primary:hover { background: var(--red-dark); }
.btn--navy { background: var(--navy); color: #fff; }
.btn--navy:hover { background: #142B4D; }
.btn--ghost-dark {
  background: transparent; color: #fff;
  border: 1px solid rgba(255,255,255,.45);
}
.btn--ghost-dark:hover { background: rgba(255,255,255,.1); }
.btn--block { width: 100%; font-size: 1.0625rem; padding: 16px; }

/* ==========================================================================
   HEADER
   ========================================================================== */
.site-header {
  background: var(--white);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 50;
}
.site-header .wrap {
  display: flex; align-items: center; gap: 14px;
  min-height: 68px;
}
@media (min-width: 1080px) { .site-header .wrap { min-height: 80px; gap: 28px; } }

.brand { display: flex; align-items: center; flex: 0 0 auto; }
.brand img { height: 44px; width: auto; }
@media (min-width: 1080px) { .brand img { height: 54px; } }

.primary-nav { display: none; }
@media (min-width: 1080px) {
  /* The nav absorbs the slack between the logo and the action group and centres
     itself in it. Left to its natural width it sat hard against the logo with a
     ~300px void to its right, which read as a mistake.
     flex-shrink is 0 deliberately: at the 1080 end of the range there is only
     about 60px to spare, and letting the nav shrink would squash the items
     rather than wrap them. */
  .primary-nav {
    display: flex; align-items: center; justify-content: center;
    flex: 1 0 auto; gap: 18px;
  }
  .primary-nav > a, .nav-trigger {
    font-family: var(--font-body); font-weight: 700; font-size: 1rem;
    color: var(--ink); text-decoration: none; background: none; border: 0;
    padding: 8px 0; cursor: pointer; display: inline-flex; align-items: center; gap: 5px;
  }
  .primary-nav > a:hover, .nav-trigger:hover { color: var(--blue); }

  /* Current page. Every page already ships aria-current="page"; this is what
     makes it visible. Inset box-shadow rather than a border so the rule does
     not change the item's height and shift the row. */
  .primary-nav > a[aria-current="page"] {
    color: var(--blue); box-shadow: inset 0 -2px 0 var(--red);
  }
}
@media (min-width: 1240px) { .primary-nav { gap: 26px; } }

/* services dropdown (desktop) */
.has-menu { position: relative; }
/* Single column, 6 Nov 2026. This was two columns at 560px, and the comment
   here justified it with "twelve services in a single stack would run off the
   bottom of a laptop viewport". That was true of twelve. It was never revisited
   after the 12 -> 9 consolidation on 3 Nov.

   Nine items at 40px plus padding is ~380px, which sits comfortably inside any
   laptop viewport under an 80px header. The two-column version also read in a
   Z pattern rather than straight down, and left the ninth item orphaned beside
   an empty cell. Labels were shortened at the same time, since the panel is now
   narrow and "Services" above it already supplies the context.

   Do not widen this back out without a reason. If the service list ever grows
   past about twelve, revisit grouping rather than columns. */
.nav-menu {
  position: absolute; top: calc(100% + 6px); left: -14px;
  width: 300px; background: #fff;
  border: 1px solid var(--border); border-top: 3px solid var(--red);
  border-radius: 0 0 var(--radius-card) var(--radius-card);
  box-shadow: 0 16px 36px rgba(11,31,58,.16);
  padding: 10px; margin: 0; list-style: none;
  display: none; grid-template-columns: 1fr; gap: 1px;
}
.has-menu[data-open="true"] .nav-menu { display: grid; }
.nav-menu a {
  display: block; padding: 10px 14px; border-radius: 4px;
  font-size: .95rem; font-weight: 600; color: var(--ink); text-decoration: none;
}
.nav-menu a:hover { background: var(--off-white); color: var(--blue); }
.nav-menu a[aria-current="page"] {
  background: var(--off-white); color: var(--blue);
  box-shadow: inset 2px 0 0 var(--red);
}
.nav-trigger svg { transition: transform .18s ease; }
.has-menu[data-open="true"] .nav-trigger svg { transform: rotate(180deg); }

.header-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.header-tel {
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem;
  color: var(--navy); text-decoration: none; white-space: nowrap;
  display: none;
}
@media (min-width: 1080px) { .header-tel { display: block; font-size: 1.45rem; } }
.header-actions .btn { display: none; }
@media (min-width: 1080px) { .header-actions .btn { display: inline-flex; } }

/* mobile controls */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: var(--radius-btn);
  border: 1px solid var(--border); background: #fff; color: var(--navy);
  cursor: pointer;
}
.icon-btn--call { background: var(--red); border-color: var(--red); color: #fff; }
@media (min-width: 1080px) { .icon-btn { display: none; } }

/* mobile drawer.
   Scoped to .site-header so it out-specifies `.site-header .wrap { display:flex }`.
   The drawer carries .wrap too, and without this it inherits that flex and is
   never hidden. */
.site-header .mobile-nav {
  display: none; border-top: 1px solid var(--border);
  background: #fff; padding-block: 8px 18px;
}
/* Twelve services plus the section links overflow a phone screen, and the drawer
   lives inside a sticky header. Cap it to the viewport and scroll it internally
   so the Free Estimate button at the bottom stays reachable. */
.site-header .mobile-nav[data-open="true"] {
  display: block;
  max-height: calc(100vh - 69px);
  max-height: calc(100dvh - 69px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
@media (min-width: 1080px) { .mobile-nav { display: none !important; } }
.mobile-nav ul { list-style: none; margin: 0; padding: 0; }
.mobile-nav a {
  display: block; padding: 13px 0; font-weight: 700; font-size: 1.0625rem;
  color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--border);
}
.mobile-nav a[aria-current="page"] { color: var(--blue); }
.mobile-nav .sub a { padding-left: 16px; font-weight: 600; font-size: 1rem; color: var(--slate); }
.mobile-nav .mobile-cta { margin-top: 16px; }

/* ==========================================================================
   HERO — Stage & Float
   ========================================================================== */
.hero { position: relative; background: var(--navy); }

.hero-stage { position: absolute; inset: 0; overflow: hidden; }
.hero-slide {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .5s ease;
}
.hero-slide[data-active="true"] { opacity: 1; }
/* --focus is set per slide in the markup. 0% anchors the crop to the top of
   the photo, 50% is the browser default. These photos all put the building in
   the upper two thirds and lawn or gravel in the lower third, so a centred
   crop throws away the roofline and keeps the dead ground. Top-weighting it
   is what keeps the gutter run in frame. */
.hero-slide img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% var(--focus, 50%);
}
@media (prefers-reduced-motion: reduce) { .hero-slide { transition: none; } }

/* Directional, not a blanket. Heavy enough for AA text contrast on the left,
   light enough on the right that the gutter work in the photo still reads. */
.hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(11,31,58,.84) 0%, rgba(11,31,58,.72) 55%, rgba(11,31,58,.9) 100%);
}
@media (min-width: 1080px) {
  .hero-scrim {
    background: linear-gradient(94deg,
      rgba(11,31,58,.92) 0%, rgba(11,31,58,.78) 42%,
      rgba(11,31,58,.40) 74%, rgba(11,31,58,.16) 100%);
  }
}

.hero .wrap { position: relative; z-index: 2; padding-block: 36px 40px; }
@media (min-width: 1080px) {
  /* Copy column is centred against the form card. The card is wider and shorter
     than it used to be (two-up fields), so the two columns are closer in height
     and the leftover photo splits evenly above and below the headline. */
  .hero .wrap {
    display: grid; grid-template-columns: minmax(0,1fr) 500px; gap: 54px;
    align-items: center; padding-block: 56px 56px;
  }
}

.hero-copy { color: var(--on-dark); }
.hero h1 {
  font-size: 2.4rem; margin: 10px 0 14px; color: #fff;
}
@media (min-width: 560px) { .hero h1 { font-size: 2.9rem; } }
/* The headline breaks after the comma, so "Made On Site in Chattanooga" has to
   fit the copy column on one line or the phrase splits again. That column is
   (wrap - 500px card - 54px gap), so it grows from ~458px at 1080 to a fixed
   718px once wrap maxes out around 1360. The type has to track it: flat 4.05rem
   only fits at the wide end. Ceiling is 4.05rem because at the old 4.25rem the
   line measures 720px against 718px and wraps by two pixels. */
@media (min-width: 1080px) {
  .hero h1 { font-size: clamp(2.45rem, 8.97vw - 3.6rem, 4.05rem); margin-top: 12px; }
}

/* The break is only correct in the two-column layout. Below 1080 the copy runs
   full width and the headline should wrap on its own. */
.hero h1 .h1-break { display: none; }
@media (min-width: 1080px) { .hero h1 .h1-break { display: inline; } }
.hero .lede { color: var(--on-dark-muted); max-width: 46ch; }

/* Truck-livery slogan, sitting where the lede used to. Red rule above it is the
   "small accent rule" the vibe sheet allows; red stays otherwise action-only. */
.hero-slogan {
  font-family: var(--font-display); font-weight: 700;
  font-size: 1.3rem; letter-spacing: .13em; text-transform: uppercase;
  color: #fff; margin: 20px 0 0; padding-top: 20px; position: relative;
}
@media (min-width: 720px) { .hero-slogan { font-size: 1.55rem; } }
.hero-slogan::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 54px; height: 3px; background: var(--red);
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
@media (min-width: 1080px) { .hero-actions { margin-top: 26px; } }

/* ---------- hero photo dots ----------
   Appended to .hero-copy by hero-gallery.js. They stay in that element rather
   than moving into .hero-stage even when pinned to a corner: the stage carries
   aria-hidden, and a focusable control inside an aria-hidden subtree is still
   in the tab order but invisible to assistive tech.

   The button is 28px so the tap target clears the 24px minimum (WCAG 2.5.8);
   the visible dot is the ::before, which is why the dots look smaller than the
   space they occupy. */
.hero-dots { display: flex; gap: 4px; margin-top: 24px; }

/* Pinned bottom-right once the hero is the two-column layout. The containing
   block is .hero .wrap (the nearest positioned ancestor), and offsets resolve
   against its PADDING box, so right:0 would push the dots out into the gutter.
   Feeding --gutter back lines their right edge up with the estimate card's. */
@media (min-width: 1080px) {
  .hero-dots {
    position: absolute; margin: 0;
    right: var(--gutter); bottom: 18px;
  }
}

.hero-dot {
  width: 28px; height: 28px; padding: 0;
  border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.hero-dot::before {
  content: ""; width: 10px; height: 10px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.9);
  background: transparent;
  transition: background .2s ease, transform .2s ease;
}
.hero-dot:hover::before { background: rgba(255,255,255,.55); }
.hero-dot[aria-current="true"]::before { background: #fff; transform: scale(1.2); }
@media (prefers-reduced-motion: reduce) { .hero-dot::before { transition: none; } }

/* ---------- estimate form card ---------- */
.estimate-card {
  background: #fff; border-radius: 9px;
  padding: 24px 20px 22px;
  box-shadow: var(--shadow-float);
  margin-top: 28px;
}
@media (min-width: 720px) { .estimate-card { padding: 28px 28px 26px; } }
@media (min-width: 1080px) {
  /* no offset and no translate: the grid centres it against the headline */
  .estimate-card { margin-top: 0; transform: none; padding: 26px 26px 24px; }
}
.estimate-card h2 { font-size: 1.85rem; margin-bottom: 5px; }
.estimate-card .reassure { font-size: .86rem; color: var(--slate); margin: 0 0 18px; line-height: 1.5; }

.field { margin-bottom: 12px; }
.field label { display: block; font-size: .8rem; font-weight: 700; color: var(--ink); margin-bottom: 5px; }
.field label .opt { color: var(--slate); font-weight: 500; }
.field input, .field select {
  width: 100%; min-height: 46px;
  border: 1px solid var(--border); border-radius: var(--radius-btn);
  padding: 0 12px; background: #fff;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.field input::placeholder { color: #9AA8B8; }
.field input:focus-visible, .field select:focus-visible {
  outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(0,61,165,.22);
}
/* one column on phones, two up once there is room — the vibe sheet is explicit
   about this, and Email at ~155px on a 390px screen is unusable */
.field-row { display: grid; grid-template-columns: 1fr; gap: 0 12px; }
@media (min-width: 560px) { .field-row { grid-template-columns: 1fr 1fr; } }
.field-row .field { margin-bottom: 12px; }

.hp { position: absolute; left: -9999px; opacity: 0; height: 0; width: 0; }

.form-fine { font-size: .76rem; color: var(--slate); text-align: center; margin: 11px 0 0; }
.form-fine a { color: var(--slate); }

.form-note {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 4px;
  background: #FFF4F6; border: 1px solid #F3C6CE;
  font-size: .82rem; color: #8A0C20; line-height: 1.5;
}
.form-note[hidden] { display: none; }

/* ==========================================================================
   TRUST STRIP
   ========================================================================== */
/* No top padding override. The card no longer overhangs, so the trust strip sits
   straight under the photo. */
.trust { background: var(--navy); color: #fff; }
.trust ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0;
}
@media (min-width: 560px) { .trust ul { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) { .trust ul { grid-template-columns: repeat(4,1fr); } }
.trust li {
  display: flex; flex-direction: column; gap: 3px;
  padding: 16px 0; font-size: .92rem; font-weight: 600;
  border-top: 1px solid rgba(255,255,255,.14);
}
@media (min-width: 560px) {
  .trust li { border-top: 0; border-left: 1px solid rgba(255,255,255,.14); padding: 20px 20px 20px 22px; }
  .trust li:nth-child(odd) { border-left: 0; padding-left: 0; }
}
@media (min-width: 1080px) {
  .trust li { border-left: 1px solid rgba(255,255,255,.14); padding: 22px 20px 22px 24px; }
  .trust li:nth-child(odd) { border-left: 1px solid rgba(255,255,255,.14); padding-left: 24px; }
  .trust li:first-child { border-left: 0; padding-left: 0; }
}
.trust b {
  font-family: var(--font-display); font-weight: 800; font-size: 1.6rem;
  line-height: 1.05; color: #fff;
}
.trust span { color: #B9C8DC; font-weight: 500; line-height: 1.45; font-size: .9rem; }

/* ==========================================================================
   SERVICES — staggered grid
   ========================================================================== */
.service-grid { display: grid; gap: 22px; }
@media (min-width: 640px) { .service-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) {
  .service-grid { grid-template-columns: repeat(3,1fr); gap: 26px; }
}

/* Every card identical: one shot ratio, one body height, link pinned to the
   bottom. No stagger offset and no per-column aspect ratios. */
.service-card {
  display: flex; flex-direction: column;
  text-decoration: none; color: inherit;
  background: #fff; border: 1px solid var(--border);
  border-radius: var(--radius-card); overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform .2s ease, box-shadow .2s ease;
}
.service-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 6px 14px rgba(11,31,58,.10), 0 18px 40px rgba(11,31,58,.14);
}
@media (prefers-reduced-motion: reduce) { .service-card:hover { transform: none; } }

.service-card .shot { aspect-ratio: 3 / 2; overflow: hidden; background: var(--off-white); }
.service-card .shot img { width: 100%; height: 100%; object-fit: cover; }
.service-card .body {
  padding: 20px var(--card-pad) 24px;
  display: flex; flex-direction: column; flex: 1;
}
.service-card .rule { width: 40px; height: 3px; background: var(--red); margin-bottom: 13px; }
.service-card h3 { font-size: 1.4rem; }
/* min-height holds two lines even when the copy is one, so every body block is
   the same height and the cards line up across rows, not just within a row */
.service-card p {
  font-size: .95rem; color: var(--slate); line-height: 1.55;
  margin: 8px 0 0; min-height: 3.1em;
}
.service-card .go {
  font-size: .85rem; font-weight: 700; color: var(--blue);
  margin: 12px 0 0; padding-top: 2px;
  display: inline-flex; align-items: center; gap: 6px;
  align-self: flex-start; margin-top: auto;
}

/* ==========================================================================
   OUR WORK
   ========================================================================== */
/* Plain photo grid. No captions, no overlays, no frames.
   5 columns at desktop so the initial 10 fill exactly two rows. */
.work-grid { display: grid; grid-template-columns: repeat(2,1fr); gap: 10px; }
@media (min-width: 560px)  { .work-grid { grid-template-columns: repeat(3,1fr); gap: 12px; } }
@media (min-width: 900px)  { .work-grid { grid-template-columns: repeat(4,1fr); } }
@media (min-width: 1200px) { .work-grid { grid-template-columns: repeat(5,1fr); } }
.work-grid img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: 5px; background: var(--off-white);
}
/* display:none also stops the lazy loader fetching them */
.work-grid img.is-hidden { display: none; }

.work-more { margin-top: 20px; text-align: center; }
.work-count { font-size: .9rem; color: var(--slate); margin: 12px 0 0; }

/* ==========================================================================
   ABOUT
   ========================================================================== */
.about-grid { display: grid; gap: 28px; align-items: center; }
@media (min-width: 900px) { .about-grid { grid-template-columns: 420px minmax(0,1fr); gap: 54px; } }
.about-grid .portrait { border-radius: var(--radius-card); overflow: hidden; background: var(--off-white); }
/* 1/1, not the source photo's native 4/5. The About copy lost a paragraph in the
   4 Nov repetition pass, and a 525px portrait against a 301px text column left the
   right half of the navy band empty. Square brings the two within ~120px.
   object-position keeps the installer and the ladder in frame through the crop. */
.about-grid .portrait img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; object-position: center 38%; }
.about-body h2 { font-size: 1.95rem; }
@media (min-width: 720px) { .about-body h2 { font-size: 2.6rem; } }
/* The copy column grew with the wider wrap, so the paragraphs need their own
   cap or they run to ~95ch. The h2 above is left uncapped on purpose. */
.about-body p { color: var(--slate); margin: 14px 0 0; max-width: var(--measure); }
.about-body p:first-of-type { font-size: 1.0625rem; }

/* ==========================================================================
   REVIEWS (placeholder until GBP access lands)
   ========================================================================== */
/* Real Google reviews, supplied by the operator 4 Nov 2026. The draft-placeholder
   block that lived here is gone. Every review is verbatim and attributed.

   Deliberately NOT a Google-branded widget: no Google logo or wordmark is used,
   because that is their trademark and we have no partnership. The layout borrows
   the familiar shape (avatar, name, reviewer stats, stars, body) which is what
   carries the recognition, and the label just says where they came from.

   No autoplay. A review carousel that advances on its own is a WCAG 2.2.2
   problem and an annoyance; these move only when the visitor moves them. */
.reviews__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 16px;
}
.reviews__source {
  margin: 0; color: var(--slate); font-size: .9rem; font-weight: 600;
}
.reviews__nav { display: flex; gap: 8px; }
.reviews__btn {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--white); color: var(--navy);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background .2s ease, border-color .2s ease;
}
.reviews__btn:hover { background: var(--off-white); border-color: var(--navy); }
.reviews__btn[disabled] { opacity: .35; cursor: default; }
.reviews__btn[disabled]:hover { background: var(--white); border-color: var(--border); }

/* Scroll-snap rail. Works with no JS at all: it is just a scrollable list, and
   the arrows are an enhancement on top of native scrolling and swipe. */
.reviews__track {
  list-style: none; margin: 0; padding: 4px 4px 14px;
  display: grid; grid-auto-flow: column; grid-auto-columns: 86%;
  gap: 16px; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scrollbar-width: none;
}
.reviews__track::-webkit-scrollbar { display: none; }
@media (min-width: 700px)  { .reviews__track { grid-auto-columns: 46%; gap: 20px; } }
@media (min-width: 1080px) { .reviews__track { grid-auto-columns: 31.5%; } }
.reviews__track:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

.rv {
  scroll-snap-align: start;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--card-pad);
  box-shadow: var(--shadow-card);
}
.rv__top { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.rv__avatar {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 700; font-size: 1.25rem;
  color: #fff; background: var(--navy);
}
.rv__avatar--a { background: var(--blue); }
.rv__avatar--b { background: var(--navy); }
.rv__avatar--c { background: var(--slate); }
.rv__avatar--d { background: var(--blue); }
.rv__avatar--e { background: var(--slate); }
.rv__who { display: flex; flex-direction: column; min-width: 0; }
.rv__name { color: var(--ink); font-size: .98rem; font-weight: 700; }
.rv__meta { color: var(--slate); font-size: .8rem; }

.rv__rating { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.rv__stars { width: 92px; height: 17px; fill: var(--star); flex: 0 0 auto; }
.rv__when { color: var(--slate); font-size: .8rem; }

.rv__body { color: var(--slate); margin: 0; font-size: .95rem; }
/* Clamp is applied by JS, never in the stylesheet. With JS off the full review
   text must still be readable rather than silently cut off at eight lines. */
/* 6 lines, not 8. Cards in a scroll rail all stretch to the tallest one, so a
   deep clamp on the longest review leaves the short reviews sitting in a tall
   half-empty card. Six is enough to judge whether to expand it. */
.rv__body.is-clamped {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6;
  overflow: hidden;
}
.rv__more {
  margin-top: 10px; padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--font-body); font-weight: 700; font-size: .88rem;
  color: var(--blue); text-decoration: underline;
}
.rv__more:hover { color: var(--navy); }

@media (prefers-reduced-motion: reduce) {
  .reviews__track { scroll-behavior: auto; }
  .reviews__btn { transition: none; }
}

/* ==========================================================================
   FAQ
   ========================================================================== */
/* Two columns from 980px, built as two independent stacks rather than one
   grid of details. A single grid would pair items into shared rows, so opening
   one would stretch its row and leave a hole beside it — the exact dead space
   this section was redesigned to remove. Separate columns only ever push their
   own items down. 4 + 3 split, so the left column runs one card longer. */
.faq { display: grid; gap: 14px; }
.faq-col { display: grid; gap: 14px; align-content: start; }
@media (min-width: 980px) {
  .faq { grid-template-columns: 1fr 1fr; gap: 18px 24px; align-items: start; }
  .faq-col { gap: 18px; }
}

/* Cards, not hairline rules: the tinted tiles are what carry colour through an
   otherwise white band. Left border is always 3px and starts transparent, so
   the red open-state accent cannot shift the text sideways. */
.faq details {
  background: var(--off-white);
  border: 1px solid var(--border);
  border-left: 3px solid transparent;
  border-radius: var(--radius-card);
  padding: 0 20px;
  transition: background .2s ease, box-shadow .2s ease, border-left-color .2s ease;
}
.faq details:hover { background: var(--white); }
.faq details[open] {
  background: var(--white);
  border-left-color: var(--red);
  box-shadow: var(--shadow-card);
}
.faq summary {
  cursor: pointer; list-style: none;
  font-family: var(--font-display); font-weight: 700; font-size: 1.2rem;
  line-height: 1.25;
  padding: 16px 34px 16px 0; position: relative; color: var(--ink);
}
@media (min-width: 720px) { .faq summary { font-size: 1.35rem; } }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: 4px; top: 50%;
  width: 11px; height: 11px; margin-top: -8px;
  border-right: 2.5px solid var(--red); border-bottom: 2.5px solid var(--red);
  transform: rotate(45deg); transition: transform .2s ease;
}
.faq details[open] summary::after { transform: rotate(225deg); margin-top: -3px; }
.faq .answer { padding: 0 0 20px; }
.faq .answer p { color: var(--slate); margin: 0 0 12px; }
.faq .answer p:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .faq details { transition: none; }
}

/* Seven questions cannot split evenly over two columns, so the right column
   ends a card short. This fills that slot instead of leaving a hole, and the
   navy is what stops the band reading as a sheet of white. Deliberately no
   button: the final CTA band is three sections down. */
.faq-ask {
  background: var(--navy); color: var(--on-dark);
  border-radius: var(--radius-card);
  padding: 24px 22px;
}
.faq-ask h3 {
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem;
  line-height: 1.25; color: #fff; margin: 0;
}
.faq-ask p { color: var(--on-dark-muted); margin: 10px 0 16px; font-size: .9375rem; }
.faq-ask__tel {
  font-family: var(--font-display); font-weight: 700; font-size: 1.75rem;
  color: #fff; text-decoration: none; line-height: 1;
}
.faq-ask__tel:hover { color: var(--on-dark-muted); }

/* ==========================================================================
   SERVICE AREA
   ========================================================================== */
.area-grid { display: grid; gap: 28px; }
@media (min-width: 900px) { .area-grid { grid-template-columns: minmax(0,1fr) 1fr; gap: 54px; align-items: start; } }
.area-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px 18px;
}
@media (min-width: 560px) { .area-list { grid-template-columns: repeat(3,1fr); } }
.area-list li {
  font-size: .95rem; font-weight: 600; color: var(--ink);
  padding: 7px 0 7px 16px; position: relative;
}
.area-list li::before {
  content: ""; position: absolute; left: 0; top: 50%; margin-top: -3px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--red);
}
.area-note {
  background: var(--white); border: 1px solid var(--border);
  border-left: 4px solid var(--blue);
  border-radius: var(--radius-card); padding: var(--card-pad);
}
.area-note h3 { font-size: 1.4rem; }
.area-note p { color: var(--slate); font-size: .98rem; margin: 10px 0 0; }

/* ==========================================================================
   FINAL CTA
   ========================================================================== */
/* Photo band with a navy scrim, same treatment as the hero. Navy stays as the
   background colour underneath so the band is never bare while the lazy photo
   is still loading, and so it degrades to a solid band if the image 404s. */
.final-cta { background: var(--navy); color: #fff; position: relative; overflow: hidden; }
.final-cta .cta-photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* Flatter than the hero scrim on purpose. The hero only has to protect text on
   its left, but this band has the outlined ghost button over on the right, and a
   white outline needs cover too. Holds at .88 across, easing to .78 mid-right. */
.final-cta .cta-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(96deg,
    rgba(11,31,58,.94) 0%, rgba(11,31,58,.88) 46%,
    rgba(11,31,58,.78) 72%, rgba(11,31,58,.86) 100%);
}
.final-cta .inner { position: relative; z-index: 2; display: grid; gap: 24px; align-items: center; }
@media (min-width: 900px) { .final-cta .inner { grid-template-columns: minmax(0,1fr) auto; gap: 44px; } }
.final-cta h2 { font-size: 2rem; color: #fff; }
@media (min-width: 720px) { .final-cta h2 { font-size: 2.6rem; } }
.final-cta p { color: var(--on-dark-muted); margin: 12px 0 0; max-width: 54ch; }
.final-cta .actions { display: flex; flex-wrap: wrap; gap: 12px; }
.final-cta .btn--primary { background: var(--red); }
.final-cta .btn--primary:hover { background: var(--red-dark); }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.site-footer { background: var(--navy); color: #fff; padding-block: 48px 28px; }
@media (min-width: 720px) { .site-footer { padding-block: 64px 28px; } }
.footer-grid { display: grid; gap: 32px; }
@media (min-width: 720px) { .footer-grid { grid-template-columns: 1.2fr 1.5fr 1fr; gap: 44px; } }
/* twelve services: two columns so the footer doesn't become a tower */
@media (min-width: 560px) { .footer-services { columns: 2; column-gap: 28px; } }
.footer-services li { break-inside: avoid; }
.site-footer img.footer-logo { height: 58px; width: auto; }
.site-footer h3 {
  font-size: 1.05rem; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: #fff; margin-bottom: 14px;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 9px; }
.site-footer a { color: var(--on-dark-muted); text-decoration: none; font-size: .95rem; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.site-footer p { color: var(--on-dark-muted); font-size: .95rem; margin: 12px 0 0; }
.footer-tel {
  font-family: var(--font-display); font-weight: 800; font-size: 1.7rem;
  color: #fff !important; text-decoration: none !important; display: inline-block; margin-top: 4px;
}
.footer-hours { font-size: .92rem; }
.footer-hours dt { font-weight: 700; color: #fff; }
.footer-hours dd { margin: 0 0 8px; color: var(--on-dark-muted); }
.footer-base {
  margin-top: 36px; padding-top: 20px;
  border-top: 1px solid rgba(255,255,255,.16);
  display: flex; flex-wrap: wrap; gap: 10px 20px; justify-content: space-between;
  font-size: .85rem; color: #94A8C2;
}
.footer-base a { font-size: .85rem; }

/* ==========================================================================
   MOBILE STICKY ACTION BAR
   ========================================================================== */
.action-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none; gap: 8px; padding: 9px 12px calc(9px + env(safe-area-inset-bottom));
  background: rgba(255,255,255,.97);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 18px rgba(11,31,58,.12);
}
.action-bar .btn { flex: 1; font-size: .95rem; padding: 12px 10px; }
.action-bar .btn--ghost-dark { color: var(--navy); border-color: var(--border); }
.action-bar .btn--ghost-dark:hover { background: var(--off-white); }
@media (max-width: 1079px) {
  .action-bar[data-visible="true"] { display: flex; }
  body { padding-bottom: 0; }
  body:has(.action-bar[data-visible="true"]) { padding-bottom: 74px; }
}

/* ==========================================================================
   DARK SECTION OVERRIDES
   Deliberately last in the file. Component rules like `.about-body p` carry the
   same specificity as `.section--dark p`, so source order is what decides it.
   Anything added below here wins for every section carrying .section--dark.
   ========================================================================== */
.section--dark { background: var(--navy); color: var(--on-dark); }
.section--dark h2,
.section--dark h3            { color: #fff; }
.section--dark p             { color: var(--on-dark-muted); }
.section--dark .eyebrow      { color: var(--on-dark-eyebrow); }
.section--dark a             { color: #fff; }
.section--dark a:hover       { color: var(--on-dark-muted); }
.section--dark .portrait     { background: rgba(255,255,255,.06); }
