/* ============================================================================
   Jupiterpest.com - Home page
   Figma: J0PCno2gHEW7c6FoluBkTo - node 48:11 - artboard 1600 - 10271

   SCALING SYSTEM
   --------------
   Artboard is 1600px wide, so 1vw = 16 design px.
   Every display size is  clamp(min, (figma/16)vw, figma).
   Every column width is a PERCENTAGE of its parent's CONTENT box.
   The page gutter is proportional: min(5.625vw, 90px)  (90 / 1600).

   PROVENANCE
   ----------
   The Figma MCP is capped at the seat level, so get_design_context has never
   run. Everything here was recovered by measurement instead:
     - geometry   from the one get_metadata call that succeeded
     - colours    sampled from the delivered SVGs and background images
     - type       solved from Figma box widths (see the type-token block)
   Values that no asset could supply are still marked TODO: body/muted/border
   colours, the gallery band fill (48:293), radii and the card shadow.
   ========================================================================== */

/* ---------- colour tokens ------------------------------------------------
   MEASURED - sampled out of the delivered SVGs and background images.
   Anything still marked TODO was not derivable from an asset and needs
   get_design_context to confirm.
   ------------------------------------------------------------------------ */
:root{
  /* brand - read from the icon strokes and fills */
  --c-brand:        #FFCC00;   /* icon strokes, pin fill, chevrons        */
  --c-brand-star:   #FFD700;   /* footer stars                            */
  --c-brand-amber:  #FFC107;   /* review-card stars                       */
  --c-brand-call:   #F5C841;   /* call icon                               */

  --c-ink:          #111111;   /* social + badge backgrounds              */
  --c-slate:        #1F2937;   /* why-choose icon strokes                 */
  --c-peach:        #FDF0EC;   /* tip-card icon circle background         */

  /* band gradient 48:212 (1600-1403) - sampled top ?' bottom */
  --c-cream-top:    #FAF0E7;
  --c-cream-mid:    #FCEFDE;
  --c-cream-bot:    #FCE9B1;

  --c-dark-contact: #0D0D0B;   /* contact-bg.jpg average                  */

  --c-body:         #656565;   /* client-supplied body copy colour           */
  --c-muted:        #7A7A7A;   /* TODO                                    */
  --c-line:         #E6E6E6;   /* TODO                                    */
  --c-page:         #FBF0E8;   /* client-supplied — whole-body base        */
  --c-form-icon:    #8DA39F;   /* stroke colour baked into the form SVGs   */
  --c-pest-icon:    #7A7A7A;   /* TODO - pest strip resting grey, no source */
  /* Warm chip/tint used by the video captions and the featured "why" card.
     TODO: taken from the 90% stop of the 48:212 gradient — closest sampled
     match, but not confirmed. One hex here updates both places. */
  --c-tint:         #FCEBC6;
  --c-panel:        #FCFAF7;   /* client-supplied - tip card fill            */
  --c-footer-text:  #CCCCCC;   /* client-supplied - footer contact + links   */
  --c-gallery:      #FCEBCA;   /* client-supplied - gallery band 48:293       */
  --c-surface:      #ffffff;
  --c-surface-alt:  #FAF0E7;
  --c-dark:         #111111;   /* TODO - gallery band 48:293, no asset    */
  --c-footer:       #111111;   /* TODO                                    */
  --c-on-dark:      #ffffff;
  --c-on-dark-mute: #B8B8B8;   /* TODO                                    */
  --c-on-brand:     #111111;   /* text on the gold fill - white is 1.7:1  */

  /* aliases kept so existing rules resolve */
  --c-brand-dark:   #111111;
  --c-accent:       #FFCC00;
  --c-band:         #FCEFDE;
  --c-band-review:  #FAF0E7;
}

/* ---------- fonts ------------------------------------------------------- */
@font-face{font-family:"ImpactJP";src:url("/fonts/impact.ttf") format("truetype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Fraunces";src:url("/fonts/Fraunces-SemiBoldItalic.ttf") format("truetype");font-weight:600;font-style:italic;font-display:swap}
@font-face{font-family:"Inter";src:url("/fonts/Inter-Regular.ttf")    format("truetype");font-weight:400;font-style:normal;font-display:swap}
/* Client-supplied. Inter ships one file per optical size; this is the 28pt cut,
   which is what the Regular and Bold above already are — mixing optical sizes
   inside one family changes the letterforms between weights. */
@font-face{font-family:"Inter";src:url("/fonts/Inter-Medium.ttf")     format("truetype");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Inter";src:url("/fonts/Inter-SemiBold.ttf")   format("truetype");font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:"Inter";src:url("/fonts/Inter-Bold.ttf")       format("truetype");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"Inter";src:url("/fonts/Inter-ExtraBold.ttf")  format("truetype");font-weight:800;font-style:normal;font-display:swap}
@font-face{font-family:"Outfit";src:url("/fonts/Outfit-Bold.ttf")     format("truetype");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"Manrope";src:url("/fonts/Manrope-Regular.ttf") format("truetype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Geist";src:url("/fonts/Geist-Regular.ttf")     format("truetype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Poppins";src:url("/fonts/Poppins-Light.ttf")    format("truetype");font-weight:300;font-style:normal;font-display:swap}
@font-face{font-family:"Poppins";src:url("/fonts/Poppins-Regular.ttf")  format("truetype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Poppins";src:url("/fonts/Poppins-Medium.ttf")   format("truetype");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Poppins";src:url("/fonts/Poppins-SemiBold.ttf") format("truetype");font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:"Poppins";src:url("/fonts/Poppins-Bold.ttf")     format("truetype");font-weight:700;font-style:normal;font-display:swap}

/* ---------- type tokens -------------------------------------------------
   SOLVED BY MEASUREMENT, not guessed. For each Figma text box of known width
   the font size was solved from the rendered advance width (width scales
   linearly with size). Residuals quoted below are vs the Figma box.

     Impact  56    "Complete Pest Control Services" -0.2% -
                   "Why Choose Jupiterpest.com" -0.2% - "Have Pest Problems- -0.2%
     Impact  40    "Termite Control" -0.2%
     Impact  33.5  "25+ Years Experience" 0.0% - "Certified Operators" -0.1%
                   - "Licensed in Florida" 0.0%
     Impact  27    "Service Areas" +1.0% - "Quick Links" -1.0% - "Contact" -0.8%
     Poppins 500/15  "Home" 0.0% - "Cockroaches" 0.0% - "Mice & Rats" -0.2%
     Poppins 500/20  "Learn More About Us" +1.0% - "View Our Work" +1.2%
     Poppins 600/19  "How Often Should Termite Inspections- +0.5%
     Poppins 400/17  "Palm Beach Gardens, FL" +0.5% - "Reviewed: Aug 14, 2026" +0.2%
   ------------------------------------------------------------------------ */
:root{
  --font:         "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-display: "ImpactJP", Impact, Haettenschweiler, "Arial Narrow Bold", sans-serif;
  --font-accent:  "Fraunces", Georgia, "Times New Roman", serif;   /* tip numbers */
  --font-ui:      "Inter",   system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-tip:     "Manrope", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-field:   "Geist",   system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --font-form:    "Outfit",  system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;

  --fs-h1:       clamp(32px, 3.5vw,     56px);    /* 580-112 ?' 2 lines @ lh56 */
  --fs-h2:       clamp(30px, 3.5vw,     56px);
  --fs-h3:       clamp(26px, 2.5vw,     40px);
  --fs-card-t:   clamp(22px, 2.0938vw,  33.5px);
  --fs-fheading: clamp(20px, 1.6875vw,  27px);
  --fs-lead:     clamp(14px, 1.0625vw,  17px);
  --fs-body:     clamp(14px, 1.0625vw,  17px);
  --fs-nav:      clamp(13px, .9375vw,   15px);
  --fs-btn:      clamp(14px, 1.2188vw, 19.5px);  /* solved: "Request Service" box 159 */
  --fs-faq:      clamp(15px, 1.1875vw,  19px);
  --fs-small:    clamp(12px, .875vw,    14px);
  --fs-topbar:   clamp(10px, .78125vw, 12.5px);  /* solved: 48:33 box 387 wide */

  --lh-tight: 1;        /* Impact headings: 56 / 56 */
  --lh-body:  1.5882;   /* 27 / 17 */

  --r-sm: 8px;    /* TODO */
  --r-md: 16px;   /* TODO */
  --r-lg: 24px;   /* TODO */
  --r-pill: 999px;

  --shadow: 0 4px 24px rgba(16, 26, 34, .08);   /* TODO */
}

/* ---------- layout constants (MEASURED from Figma) ---------------------- */
:root{
  --aw: 1600;                       /* artboard width          */
  --gutter: min(5.625vw, 90px);     /* 90 / 1600               */
  --content: 1420px;                /* 1600 ^' 2-90             */
  --header-h: 95px;                 /* overwritten by main.js  */
}

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

html{
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior: auto; }
  *, *::before, *::after{ animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

body{
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--c-body);
  background: var(--c-page);
}

img, svg{ display:block; max-width:100%; }
ul, ol{ margin:0; padding:0; list-style:none; }
/* the UA sheet gives <figure> `margin: 1em 40px` — that was shrinking every
   gallery tile by 80px across and 32px down inside its grid track */
figure{ margin:0; }
h1, h2, h3{
  margin:0; color: var(--c-ink);
  font-family: var(--font-display); font-weight:400;
  line-height: var(--lh-tight);
}
p{ margin:0; }
a{ color: inherit; text-decoration: none; }
button{ font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible{ outline: 2px solid var(--c-accent); outline-offset: 3px; }

.skip-link{
  position:absolute; left:-9999px; top:0; z-index:200;
  padding:12px 20px; background:var(--c-brand); color:var(--c-on-brand);
}
.skip-link:focus{ left:0; }

/* The page gutter. Content box is exactly 88.75vw (= 1420/1600) up to 1600px. */
.wrap{
  width:100%;
  max-width: calc(var(--content) + 2 * 90px);   /* 1600 */
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Anchor targets clear the sticky header. */
.section{ scroll-margin-top: var(--header-h); }

/* Copy that is still missing from the Figma export. Keeps the designed box
   height so section geometry can be measured before the text lands. */
.copy:empty{
  display:block;
  min-height: calc(var(--fig-h, 20) * 1px);
  border: 1px dashed rgba(200, 60, 60, .45);
  border-radius: 4px;
  background: repeating-linear-gradient(135deg,
              rgba(200,60,60,.05) 0 8px, transparent 8px 16px);
}
.copy--inline:empty{
  display:inline-block; min-width: 6em; min-height: 1em; vertical-align: top;
}
body.copy-hide-gaps .copy:empty{ border-color: transparent; background: none; }
/* A pill's own box height wins over the text-box height of the copy inside it. */
.pill.copy:empty{ display:flex; min-height: clamp(32px, 2.625vw, 42px); }
/* ...and `display:block` above must not resurrect a [hidden] accordion panel. */
.copy[hidden]{ display:none; }

/* Body copy blocks. Every Figma body box resolves to line-height 20 with the
   text wrapping to boxHeight/20 lines, so one shared rhythm covers them all.
   Paragraphs butt up with no gap: e.g. 48:132 is 868x160 = 8 lines x 20 with
   no room for a blank line between the two paragraphs. */
.prose{ font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333; }
.prose p{ margin:0; }

/* ============================================================ buttons === */
.btn{
  display:inline-flex; align-items:center; justify-content:center;
  gap: clamp(6px, .625vw, 10px);
  font-size: var(--fs-btn); font-weight:500;
  /* square corners — confirmed on the header phone pill and both hero buttons */
  border-radius: 0;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease;
  text-align:center;
}
/* Figma 48:49 "Request Service" 199 - 52 - label 159 - 14 ?' 20px inline pad */
/* Gold fill takes dark text - white on #FFCC00 is 1.7:1 and unreadable. */
.btn--primary{
  min-height: clamp(42px, 3.25vw, 52px);
  padding-inline: clamp(14px, 1.25vw, 20px);
  background: var(--c-brand); color: var(--c-on-brand); font-weight:600;
}
.btn--primary:hover{ background: var(--c-ink); color: var(--c-brand); }

.btn--outline{
  min-height: clamp(42px, 3.25vw, 52px);
  padding-inline: clamp(14px, 1.25vw, 20px);
  border: 1px solid currentColor; color: var(--c-on-dark);
}

/* Figma's buttons are FIXED-width frames with a centred label, not hug-content
   — the inline padding differs per instance (20 / 23 / 14.5 / 26.5) precisely
   because the widths are set, not the padding. */
.btn--primary, .btn--outline{ min-width: clamp(150px, 12.4375vw, 199px); }
.about__head .btn--primary,
.reviews__left .btn--primary{ min-width: clamp(190px, 15.8125vw, 253px); }
/* longest label on the page — its 14.5 padding is what keeps the frame at 367 */
.services__copy .btn--primary{
  min-width: clamp(240px, 22.9375vw, 367px);
  padding-inline: clamp(10px, .9063vw, 14.5px);
}

/* Figma 48:34 phone pill 194.16 - 51.59 - icon 25.16 - 23.59 @ x11 */
.btn--phone{
  min-height: clamp(42px, 3.2243vw, 51.59px);
  padding-inline: clamp(9px, .6875vw, 11px);
  gap: clamp(8px, .6875vw, 11px);
  background: var(--c-ink); color: var(--c-on-dark);
}
.btn--phone:hover{ background: #000; }
/* Mask, not a plain <img>: the glyph is one solid path, so `currentColor` lets
   the same file be gold on the black header and white on the hero photo. */
/* The icon is an INLINE <svg fill="currentColor"> — it must not be masked.
   A url() mask here is blocked on file:// and wipes the glyph out entirely. */
.btn--phone .btn__icon{
  flex:0 0 auto;
  width: clamp(19px, 1.5726vw, 25.16px);
  height: clamp(18px, 1.4743vw, 23.59px);
}
/* solved: 48:38/48:48/48:205/48:477 all put "561-222-9042" in a 136-wide box */
.btn--phone .btn__label{ font-size: clamp(15px, 1.2031vw, 19.25px); font-weight:700; }

.btn--submit{
  width:100%;
  min-height: clamp(42px, 2.9732vw, 47.57px);
  /* was --c-on-dark: white on the gold fill is 1.7:1. The supplied
     buttonarrow.svg has a black stroke, confirming a dark label. */
  background: var(--c-brand); color: var(--c-on-brand);
  border-radius: var(--r-sm);
  display:inline-flex; align-items:center; justify-content:center;
  gap: clamp(6px, .5vw, 8px);
  /* 48:352 · label box 151 wide → 13.5px, narrower than the other buttons'
     --fs-btn 16 (those measure 10.5-11.2 px/char in Figma, this one 7.55) */
  font-size: clamp(11px, .84375vw, 13.5px); font-weight:600;
}

/* Small label pills - Figma 48:227 460-40, 48:265 371-42, 48:413 191-42 */
/* block-level flex, not inline-flex: an inline-level pill sits on a line box
   whose strut adds a few px of phantom height to every parent it appears in. */
/* Pill text solved from the inner text nodes: 418/351/329/144/257 wide across
   the five pills all resolve to Poppins 18 (17.6-18.1). Padding-inline 21. */
/* max-content sizes the pill to its text (as in Figma) but must still be
   allowed to shrink, or a long label forces horizontal scroll on mobile. */
.pill{
  display:flex; width:max-content; max-width:100%;
  align-items:center; justify-content:center; text-align:center;
  min-height: clamp(32px, 2.625vw, 42px);
  padding-inline: clamp(14px, 1.3125vw, 21px);
  border-radius: var(--r-pill);
  background: transparent; border: 1px solid var(--c-brand); color: var(--c-ink);
  /* client-specified: Inter Medium 19. Was Poppins SemiBold 18 — this brings
     every eyebrow pill onto the same spec the service-card pills already use. */
  font-family: var(--font-ui);
  font-size: clamp(13px, 1.1875vw, 19px); font-weight:500; line-height: 1.1111;
}
/* The video captions are a FILLED tan chip with a small radius — not the
   outlined eyebrow pill the other sections use.
   TODO: --c-cream-mid is the closest colour sampled from the delivered assets;
   confirm the exact fill. */
.feature .pill{
  min-height: clamp(30px, 2.5vw, 40px);
  background: var(--c-tint); border: 0;
  border-radius: var(--r-sm);
}
/* on the dark bands the outline pill keeps its gold border AND gold text */
.reviews .pill, .contact .pill, .testimonials .pill{ color: var(--c-brand); }
/* the Service Areas eyebrow keeps its thin dark rule, but no fill —
   client-specified: the white capsule is gone, the page shows through. */
.areas .pill{ background: transparent; border-color: var(--c-ink); }

.section__title{ font-size: var(--fs-h2); }

/* Slider dots - Figma 48:187 - 8-8, pitch 52.8 (vertical) */
/* 48:187 / 48:233 · each marker is 8 wide × 44.8 TALL inside a 52.8 slot —
   vertical bars with an 8px gap, not round dots. */
.dot{
  display:block;
  width: clamp(5px, .5vw, 8px); height: clamp(28px, 2.8vw, 44.8px);
  border-radius:4px; background: currentColor; opacity:.4; padding:0;
}
/* Client-specified: active and hover are the brand gold. `background` is set
   outright rather than leaning on currentColor — the bars inherit ink in the
   About row and gold in the Reviews column, so only a literal colour makes both
   land on the same yellow. */
.dot.is-active,
.dot:hover,
.dot:focus-visible{ opacity:1; background: var(--c-brand); }
.dot{ position:relative; transition: background-color .18s ease, opacity .18s ease; }

/* Horizontal marker row. Only the <=900 layouts use it; on desktop the
   testimonials are a 3-up grid and need no markers at all. */
.dots-row{ display:none; }

@media (max-width: 900px){
  .dots-row{
    display:flex; justify-content:center; align-items:center;
    gap: 12px; margin-top: 16px; color: var(--c-ink);
  }
  /* Every marker row runs horizontally here, so the bar lies down with it —
     a 5x28 upright bar inside a horizontal row reads as a stray tick.
     ::after gives each one a 44x43 touch area without moving the paint; the
     6px side inset keeps neighbouring areas from overlapping across the 12px
     gap, so a tap near the edge still selects the marker it looks nearest. */
  .dot{ width: 32px; height: 5px; }
  .dot::after{ content:""; position:absolute; inset: -19px -6px; }
}

/* ============================================================ header ==== */
/* Figma 48:14 - 1600 - 95  (topbar 23 + navbar 72) */
/* Header is one dark bar in the design, not a dark strip over a white nav. */
.site-header{ position: sticky; top:0; z-index:100; background: var(--c-ink); }

/* Gold strip with black uppercase text — was a black strip with white text. */
.topbar{ background: var(--c-brand); color: var(--c-on-brand); }
.topbar__inner{
  min-height: clamp(20px, 1.4375vw, 23px);
  display:flex; align-items:center; justify-content:center;
}
/* 48:33 · text box 387 wide, centred on 800.5 */
.topbar__text{
  font-size: var(--fs-topbar); line-height:1;
  text-transform: uppercase; font-weight:600; letter-spacing:.02em;
  text-align:center;
}

.navbar{ background: #000; }   /* client-confirmed: pure black, not --c-ink */
.navbar__inner{
  min-height: clamp(56px, 4.5vw, 72px);
  display:flex; align-items:center;
}

/* logo 191.94 - 25.77 @ x90 (= gutter) */
.brand img{ width: clamp(140px, 11.9961vw, 191.94px); height:auto; }

/* nav starts at x462.93 ?' 180.99px after the logo ?' 180.99/1420 = 12.746% */
.nav{ margin-left: 12.746%; flex: 0 0 auto; }
/* Fixed gap, not %: .nav is shrink-to-fit, so a percentage gap would resolve
   against a width that the gap itself helps determine. */
.nav__list{ display:flex; align-items:center; gap: clamp(4px, .875vw, 14px); }
.nav__link{
  display:inline-flex; align-items:center; gap: clamp(4px, .375vw, 6px);
  /* client-specified: Inter Bold 15.4 */
  font-family: var(--font-ui); font-weight:700;
  font-size: clamp(13px, .9625vw, 15.4px); line-height:1.6; color: var(--c-on-dark);
  padding-inline: clamp(10px, 1.3125vw, 21px);
  padding-block: clamp(8px, .8469vw, 13.55px);
  border-radius: var(--r-sm);
  white-space: nowrap;
}
.nav__item{ position:relative; }
.nav__link:hover{ color: var(--c-brand); }
/* gold text only — the design has no pill behind the active item */
.nav__link.is-active{ color: var(--c-brand); background: none; }

/* ---- dropdown ------------------------------------------------------------
   Opens on hover AND on focus-within, so it is reachable by keyboard without
   any JS. main.js only adds the click/Escape behaviour for touch. */
.nav__menu{
  position:absolute; top:100%; left:0; z-index:110;
  min-width: max-content;
  padding: clamp(6px, .5vw, 8px) 0;
  background:#000; border:1px solid rgba(255,255,255,.18);
  opacity:0; visibility:hidden; transform: translateY(6px);
  transition: opacity .18s ease, transform .18s ease, visibility 0s linear .18s;
}
.nav__item--has-menu:hover > .nav__menu,
.nav__item--has-menu:focus-within > .nav__menu,
.nav__item--has-menu.is-open > .nav__menu{
  opacity:1; visibility:visible; transform:none;
  transition: opacity .18s ease, transform .18s ease, visibility 0s;
}
.nav__menu a{
  display:block; white-space:nowrap;
  padding: clamp(7px, .625vw, 10px) clamp(14px, 1.25vw, 20px);
  font-family: var(--font-ui); font-weight:400;
  font-size: clamp(13px, .875vw, 14px); line-height:1.5;
  color: var(--c-footer-text);
}
.nav__menu a:hover,
.nav__menu a:focus-visible{ color: var(--c-brand); background: rgba(255,255,255,.06); }
.nav__menu a[aria-current="page"]{ color: var(--c-brand); }
.nav__link--caret::after{ transition: transform .18s ease; }
.nav__item--has-menu:hover > .nav__link--caret::after,
.nav__item--has-menu.is-open > .nav__link--caret::after{ transform: rotate(180deg); }

/* caret - Figma 48:24 polygon 8 x 8. In the design it sits 6 after the label
   and only ~7 from the link's right edge, not the 21 the plain links use. */
.nav__link--caret{ padding-right: clamp(4px, .4375vw, 7px); }
.nav__link--caret::after{
  /* client-supplied: nav-caret.svg, a 7x6 gold triangle. Was a CSS clip-path
     square in currentColor, which drew it 8x8 and took the link's colour —
     white on the inactive items. The file's own #FFCC00 keeps it gold. */
  content:""; width: clamp(6px, .4375vw, 7px); height: clamp(5px, .375vw, 6px);
  /* no margin-left: the flex `gap` on .nav__link already supplies the 6 */
  background: url("/images/icons/nav-caret.svg") center / contain no-repeat;
}

/* 48:34 · outlined, not filled: gold 2px border on the black bar, gold label.
   The phone pills on gold panels stay filled. */
.navbar__phone{
  margin-left:auto;
  /* client-specified: white rule, gold label — the frame it was first read
     from showed the border gold like the text. */
  background: transparent; color: var(--c-brand);
  border: 1px solid #fff; border-radius: 0;
  /* the 1px border sits outside the shrink-to-fit content box, so the inline
     padding drops 11 → 10 to keep the frame at Figma's 194.16 */
  padding-inline: clamp(8px, .625vw, 10px);
}
.navbar__phone:hover{ background: var(--c-brand); color: var(--c-on-brand); }

/* Sits above the panel (.site-header z-index 100 vs .nav 95), so the same
   button both opens and closes the menu. */
.hamburger{
  display:none; margin-left:auto;
  width:44px; height:44px; padding:10px;
  align-items:center; justify-content:center;
  color: var(--c-on-dark);
}
.hamburger__bars{
  display:flex; flex-direction:column; justify-content:space-between;
  width:100%; height:100%;
}
.hamburger__bar{ display:block; height:2px; width:100%; background: currentColor; border-radius:2px; }
/* The close icon. Swapped by `display`, not by rotating the bars — a transform
   here is invisible to the preview pane's layout tree, so it could not be
   verified; a display swap can be. */
.hamburger__x{ display:none; width:100%; height:100%; }
.hamburger[aria-expanded="true"] .hamburger__bars{ display:none; }
.hamburger[aria-expanded="true"] .hamburger__x{ display:block; }

.nav-overlay{
  position:fixed; inset:0; z-index:90;
  background: rgba(16,26,34,.45);
}

/* ============================================================ 1 hero ==== */
/* Figma 48:39 bg 1600-766 - content 48:40 x891 w620, bottom edge y789 */
/* One hero, many pages. Each page swaps only --hero-img; the desktop background
   and the stacked mobile band both read it, so the photo is named once. */
.hero{
  --hero-img: url("/images/hero-bg.jpg");
  background: var(--c-surface-alt) var(--hero-img) center / cover no-repeat;
}
.hero--services{ --hero-img: url("/images/hero-services.jpg"); }
.hero--termite{ --hero-img: url("/images/hero-termite-control.jpg"); }
.hero--pest-control{ --hero-img: url("/images/hero-professional-pest-control.jpg"); }
.hero--pest-control .hero__title em{ display:block; }
.hero--rodent{ --hero-img: url("/images/hero-rodent-control.jpg"); }
.hero--rodent .hero__title em{ display:block; }
.hero--mosquito{ --hero-img: url("/images/hero-mosquito-control.jpg"); }
.hero--mosquito .hero__title em{ display:block; }
.hero--pine-beetle{ --hero-img: url("/images/hero-pine-beetle-treatment.jpg"); }
.hero--pine-beetle .hero__title em{ display:block; }
.hero--organic{ --hero-img: url("/images/hero-organic-pest-control.jpg"); }
.hero--organic .hero__title em{ display:block; }
.hero--no-tent{ --hero-img: url("/images/hero-no-tent-termite-control.jpg"); }
.hero--no-tent .hero__title em{ display:block; }
.hero--subterranean{ --hero-img: url("/images/hero-subterranean-termite-control.jpg"); }
.hero--subterranean .hero__title em{ display:block; }
.hero--flea-tick{ --hero-img: url("/images/hero-flea-and-tick-control.jpg"); }
.hero--flea-tick .hero__title em{ display:block; }
.hero--about{ --hero-img: url("/images/hero-about-us.jpg"); }
.hero--about .hero__title em{ display:block; }
/* Short title — one line in the design, so no em{display:block} here. */
.hero--service-areas{ --hero-img: url("/images/hero-service-areas.jpg"); }
/* Two gold words inside one white line, so the <em>s stay inline here. */
.hero--tips{ --hero-img: url("/images/hero-tips-and-articles.jpg"); }
.hero--reviews{ --hero-img: url("/images/hero-reviews-testimonials.jpg"); }
.hero--reviews .hero__title em{ display:block; }
.hero--gallery{ --hero-img: url("/images/hero-gallery.jpg"); }
/* Same two-line split the Services hero uses: gold falls on the whole second
   line, not the last two words. */
.hero--termite .hero__title em{ display:block; }
/* Services hero: gold falls on the whole second line, not the last two words.
   Measured on the photo behind the text — mean 13.4:1 white, sub-line worst
   case 12.3:1 — so it carries the type unaided, like the home hero. */
.hero--services .hero__title em{ display:block; }
.hero__inner{
  min-height: clamp(520px, 47.875vw, 766px);
  display:flex; align-items:flex-end; justify-content:flex-end;
  padding-bottom: clamp(40px, 4.5vw, 72px);
}

/* ---- article hero (tips/*.php) -------------------------------------------
   Client-specified: a 300 band with the copy centred, instead of the tall
   bottom-right banner every other page uses.
   Two classes deep on purpose. The 900 and 600 blocks both restate
   .hero__inner's min-height and they sit LATER in the file, so a one-class
   rule here would lose to them and the band would grow back to 430+ on a
   tablet. At (0,2,0) this holds at every width.
   min-height, not height: a long article title still needs somewhere to go. */
/* The tips banner is a bright photo — bees on sun-lit white boards — and the
   white heading vanished into it. Measured over the band the copy sits in:
   mean luminance 0.214, p95 0.603, brightest pixel 0.982. Unassisted, white
   text is 3.98:1 on the mean and 1.61:1 at p95.

   A flat 70% black puts p95 at 4.55:1, so even the 15px lead clears AA, and the
   mean lands at 9.2:1. 0.82 would clear the single brightest pixel too, but by
   then the photo is gone; at 0.70 the 56px heading still holds 3.05:1 against
   the very worst pixel, above the 3:1 bar large text has to meet.

   Layered into background-image rather than added as a pseudo-element: .hero
   already uses ::after for the tablet scrim, and stacking the two would
   double-darken the bottom of the band. */
.hero--post{
  background-image: linear-gradient(rgba(0,0,0,.70), rgba(0,0,0,.70)), var(--hero-img);
}
.hero--post .hero__inner{
  min-height: 300px;
  align-items: center; justify-content: center;
  /* 32, not 48: the longest of the six titles runs to two lines and its block
     measures 226, so 48 a side pushed the band to 322. At 32 the content plus
     padding is 290 and the 300 floor is what sets the height — every article
     lands on exactly 300 whether its title takes one line or two. */
  padding-block: clamp(16px, 2vw, 32px);
}
.hero--post .hero__content{ width: 100%; max-width: 900px; text-align: center; }
.hero--post .hero__title{ max-width: 100%; margin-inline: auto; }
.hero--post .hero__actions{ justify-content: center; }
.hero__content{ width: 43.662%; }              /* 620 / 1420 */
/* The photo behind this box measures mean luminance 0.022 / worst 0.182, so
   white text is 14.6:1 average and 4.53:1 at its worst pixel - passes 4.5:1
   with no scrim. Dark text here was invisible. */
.hero__title{
  max-width: 93.548%;                          /* 580 / 620  */
  font-size: var(--fs-h1);
  color: var(--c-on-dark);
}
/* "Florida Pests" is gold in the design — <em> is the hook, not italics */
.hero__title em{ font-style: normal; color: var(--c-brand); }

/* On the photo the phone pill is a thin outline. Label AND icon are the call
   gold #F5C841 — the icon follows because it is a currentColor mask. */
.hero .btn--phone{
  background: transparent; color: var(--c-brand-call);
  border: 1px solid #fff;
  padding-inline: clamp(8px, .625vw, 10px);
}
.hero .btn--phone:hover{ background: var(--c-brand-call); color: var(--c-ink); }
.hero__lead{ color: var(--c-on-dark); }
/* Figma 48:41 - 620 x 40. The copy wraps to 2 lines inside 620 at Poppins
   Regular up to 15px (3 lines at 16), and a 40px box over 2 lines fixes
   line-height at 20. Size is the largest that satisfies the wrap. */
/* client-specified: Inter SemiBold 15. Was inheriting Poppins from <body>; the
   size was already 15 at 1600, only the family and weight change. 600 is a real
   cut (Inter-SemiBold.ttf, 28pt like the other Inter weights loaded above), not
   a synthesised bold. */
.hero__lead{
  margin-top: clamp(12px, 1.3125vw, 21px);
  font-family: var(--font-ui); font-weight: 600;
  font-size: clamp(12px, .9375vw, 15px);
  line-height: 1.3333;                        /* 20 / 15 */
}
.hero__actions{
  margin-top: clamp(12px, 1.3125vw, 21px);
  display:flex; flex-wrap:wrap; gap: clamp(10px, 1.1775vw, 18.84px);
}

/* ---- intro band -----------------------------------------------------------
   Centred heading + lead over the same insect plate the home About band uses.
   Only the base colour under the plate differs, so it stays a variable.
   client-specified: #fffffff0 — white at 94%, so a little of the page cream
   reads through the plate. (Was a hex guessed off a screenshot.) */
.intro-band{
  --intro-base: #fffffff0;
  background: var(--intro-base) url("/images/about-bg-tinted.png") center / 100% 100% no-repeat;
  padding-block: clamp(44px, 4.875vw, 78px) clamp(48px, 5.25vw, 84px);
  text-align:center;
}
.intro-band__title{ max-width: 52.817%; margin-inline:auto; }        /* 750 / 1420 */
.intro-band__lead{
  /* 936/1420. Line 1 measures 909 and breaks after "business," as designed;
     adding the next word would need 962, so this sits safely between. */
  max-width: 65.915%; margin-inline:auto;
  margin-top: clamp(18px, 2.1875vw, 35px);
  /* client-specified: Inter Medium 15 / #656565, same as the About band's lead */
  font-family: var(--font-ui); font-weight:500;
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333;        /* 20 */
  color: var(--c-body);
}

/* Placeholder band on the inner pages until their sections are designed. */
.page-head{ padding-block: clamp(48px, 6.25vw, 100px); text-align:center; }
.page-head__title{ font-size: var(--fs-h1); }

/* ---- notice pages (404, thank-you) ---------------------------------------
   No design was supplied for either, so these are built from tokens the rest
   of the site already uses — nothing invented. */
.notice{ padding-block: clamp(56px, 6.875vw, 110px); }
.notice__inner{
  max-width: 760px; margin-inline:auto; text-align:center;
  display:flex; flex-direction:column; align-items:center;
}
.notice__code{
  margin:0; font-family: var(--font-display); line-height:1;
  font-size: clamp(72px, 10vw, 160px);
  color: var(--c-brand);
}
.notice__badge{
  display:flex; align-items:center; justify-content:center;
  width: clamp(56px, 5vw, 80px); height: clamp(56px, 5vw, 80px);
  border-radius: 50%;
  background: var(--c-brand); color: var(--c-on-brand);
}
.notice__badge svg{ width: 52%; height: 52%; }
.notice__title{ margin-top: clamp(16px, 1.75vw, 28px); }
.notice__lead{
  margin-top: clamp(12px, 1.25vw, 20px);
  max-width: 60ch;
  font-size: clamp(13px, 1vw, 16px); line-height:1.55;
  color: var(--c-body);
}
.notice__actions{
  margin-top: clamp(20px, 2.25vw, 36px);
  display:flex; flex-wrap:wrap; justify-content:center;
  gap: clamp(10px, 1.1775vw, 18.84px);
}
/* on cream, so the outlined pill needs an ink border — the hero's white one
   would be invisible here */
.notice .btn--phone{
  background: transparent; color: var(--c-ink);
  border: 1px solid var(--c-ink);
}
.notice .btn--phone:hover{ background: var(--c-ink); color: var(--c-brand); }
.notice__links{
  margin-top: clamp(26px, 3vw, 48px);
  display:flex; flex-wrap:wrap; justify-content:center;
  gap: clamp(10px, 1.25vw, 20px) clamp(16px, 2vw, 32px);
}
.notice__links a{
  display:inline-flex; align-items:center; gap: 8px;
  font-size: clamp(13px, .9375vw, 15px); font-weight:600;
  color: var(--c-ink);
  text-decoration: underline; text-underline-offset: .18em;
}
.notice__links a:hover{ color: var(--c-body); }
/* the chevron ships black; it reads on cream as-is */
.notice__links img{ width:7px; height:auto; flex:0 0 auto; }

@media (max-width: 600px){
  .notice__actions .btn{ width:100%; }
  .notice__links{ flex-direction:column; align-items:center; }
}

/* ============================================================ 2 pests === */
/* Figma 48:55 - 1421 - 136 - 9 items, item 129, pitch 161 ?' gap 32 */
.pests{ padding-block: clamp(20px, 2.125vw, 34px) clamp(24px, 2.6875vw, 43px); }
.pests__list{
  display:grid; grid-template-columns: repeat(9, minmax(0,1fr));
  gap: 2.2535%;                                 /* 32 / 1420 */
}
/* item box 129-128 in a 136-tall list; the icon SVG already carries the 12.5
   top inset, so only the trailing space is padding here */
.pest{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  padding-bottom: clamp(12px, 1.2188vw, 19.5px);
}
/* Figma: a 70-70 glyph sitting in a 70-92.5 box. The delivered SVGs are either
   70-93 (glyph already inset) or 70-70 - a fixed box + contain normalises both. */
/* Inline <svg fill="currentColor"> — NOT a url() mask. Chrome gives every
   file:// document its own opaque origin, so a mask-image pointing at a local
   SVG is blocked and the glyph disappears when index.html is opened directly. */
/* The exported SVGs already carry fill-opacity="0.25" — that IS the Figma
   resting state, so the colour underneath must be black. Tinting them grey as
   well stacked two lightenings and washed the strip out. */
.pest__icon{
  display:block;
  width: clamp(40px, 4.375vw, 70px);
  height: clamp(53px, 5.7813vw, 92.5px);
  color: #000000;
  transition: color .18s ease, background-color .18s ease, opacity .18s ease;
}
.pest__icon path{ transition: fill-opacity .18s ease; }
/* Ants ships as a raster, so it is recoloured through a mask — but from a
   data: URI, which is not a cross-origin fetch and so survives file://. */
.pest__icon--raster{ --ant: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABdCAYAAAAYPxDcAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAU8SURBVHhe7ZrrcdRADIBVAh2QElICJaQD0gF0AB1AB5cOQgf5mUnsmyuBEtKBzUiWlI28L/seuz72m9FA1vb6LGv18gI0Go1Go9FoNBqNRqPRqJSxgy927NSML3Brx6oGf/DYwzj08MMeOxU4N95ji8q5P5dyHKXc22Ob4BzK2bxSBFXOK3y3x5ZyNUoRUClDDwc7vhScY+zgpx3fLBih6E0f4JM9lgteS3NcINpdlKGDt7GHOzueC16Lc9jxzTP08DR08NuO54LX4hx2vGrobfbwzY67oG8gH/EMN/ZYClxGOf4Ff8MxVnly8MekIg8qZOjgL4r1E+Q/0A+hGMXhmF4XUSorZaxKMQgqhZTTwVd7zAXfujwAKauHJ/5bhcae4UYV3sHvmOPGe6ZeTFFouaCTTaTpVDK8wC075A9KUeXwPFlzTdEqusyKk6McXlZBpXxQTmT56Dy1K0UYX+Eh5hPIkXoU4ZNQJHJ81upIVwTyE543OfZTibBIPEmdRDk7Xj38w2dv2+dsUxKY51Cts43hKwM0rV8j7jxTxNpeL0ZAx+i+VQm/K0XzE1yO6F/0RlvDpvFODrNcHH81dPA47GGnN6qJnAyTs9Y3WQZjDw+zB84UVxGsqJlDtpCFRhLDk5PbAqAaZ8oz6DzKYOVBcZlxYcmRioR6N2xpbq4jYTm3ytboF0gZzgYncsl17lbVmuZjtorWhErYw05qIRKMNrhUsMTgzNd980MHO1+UstBcJZabWEMqZHL37oMCpbbJEVt75YRpcc4XtxZB0//IOtbQystJ/84UtyzICdP8wrCCnyWXFyP3R5DPwPOm83f24VNCfiizBMCGec4SPztstnGrEQVOjhP/nT18THAp6vKI3UeKylq+IpCD3cMvO+5Cy0BaBGsEr034DHLMNViLoOl/ZO0LKy3mELMUxPFdyfzqopDVZITRNdlvKgohZC0lwnMKp5OWk/RlW03KryBqsYmlVgxbG4WQCGOVYCU3FymWzOWiSR87Y2l42/MQKis4e/YohNqVwWs5g8Z7aH8ncG41cLpPTSqpdTj/mNJ8NPup1rrHt6yfTablcCf/x/NpQwB/WuEa6tGZ873WSizfKpFaR5YZPRTnJdZKPNajeQ/XUFPBWbt1rEWsxq2evYJLhs+1c1w10dCdKDOummirs5a0vgTWz/z3iiH/ghEpZjEcjq/W0Vo4FFOnLpToSbbLYX62O+Lq4KRPdy9IjsKWM/V95W9pTrGDPuXOz2ogy5jyEH37uJ/FJGsHPue9Ac7Fo+QzuSXCJhAFUCYrDW3ZlpohYimU00ybBZJ95qphH0EpvPkaGY5EAXGv59LgjZz31qyHNwWpg9XxgLNNiW2dyjybc8xc3D3a8TXWouLJhGlpbWkrCDvQWYtxyYYhK74+jzTi7XiVcEth1v/V8WPEFJPcl5mNVwnnIeNs/JgvBCIhZWc04YvDn2Rn614UdpR4HC054C3UVdSQeoU/dlzN/hjxWAanBNGvk1Xg7m5wOYePQUIvojoo8gRMWxvXK8S3PBGKTGY3RZXQg3h8ARLt2KUkoGx16h5rqobUj+QyId7n9UiseNxEZOIqOJpw0ScUz8NHJWAtAik7cU5RQpueLYuWlKcUsJDvyjivGEtCZ0o5tiqPgbtBq/5Em7M/zkV7LE4NxRVz8POsj1BSWQ1s+t6IdE5yfFsxNCIViA6SVQ97+GyPFYe7/8XeWqjVUZzS63ypf7sYpWuWaiNT6VwiN4e6OOQAA6XAJaD7LwjxjUaj0Wg0Go1Go9FYzj+MrGvWcv0cmgAAAABJRU5ErkJggg=="); }
/* the ant is a mask, so it has no fill-opacity — match the 0.25 here */
.pest__icon--raster{
  background-color: #000000; opacity: .25;
  -webkit-mask: var(--ant) center / contain no-repeat;
          mask: var(--ant) center / contain no-repeat;
}
/* Active/hover treatment, read off the "Ants" item in the design: pale gold
   wash, gold glyph, gold bar along the bottom edge. The bar is an inset
   box-shadow, not a border, so hovering does not change the strip's height. */
.pest{ transition: background-color .18s ease, box-shadow .18s ease; }
.pest:hover,
.pest:focus-within{
  background: rgba(255, 204, 0, .22);
  box-shadow: inset 0 -3px 0 var(--c-brand);
}
.pest:hover .pest__icon,
.pest:focus-within .pest__icon{ color: var(--c-brand); }
.pest:hover .pest__icon path,
.pest:focus-within .pest__icon path{ fill-opacity: 1; }
.pest:hover .pest__icon--raster,
.pest:focus-within .pest__icon--raster{ background-color: var(--c-brand); opacity: 1; }
.pest__name{
  font-size: var(--fs-nav); font-weight:500; color: var(--c-ink);
  line-height: 1.6;                             /* 24 / 15 */
}

/* ============================================================ 3 about === */
/* Figma 48:53 bg y1074-1840 - 48:126 content y1149-1774 */
/* Figma 48:54 - 1600-766 faint pest-pattern plate, exactly the section box */
.about{
  /* The 1600×766 plate is the faint pest pattern: near-white (#F6F6F6) art at a
     uniform alpha of 69/255. Figma puts a #F7BE00 @ 35% overlay on that layer,
     which is what turns it tan. The overlay is pre-composited into
     about-bg-tinted.png (untinted original kept as about-bg.png) — a CSS layer
     could not reproduce it without a mask, and masks die on file://. */
  /* 100% 100%, not cover: the section is taller than the 1600x766 plate, so
     `cover` scaled it up and cropped the insects off the left and right edges.
     Stretching 5% vertically keeps every insect where the design puts it. */
  /* client-tested: white base, not cream — the plate's own near-white wash is
     what produces the pale cream once composited. */
  background: #ffffff url("/images/about-bg-tinted.png") center / 100% 100% no-repeat;
  /* client-tested: uncapped vw padding. At 1600 this is the same 75/66 the
     clamp produced; it just keeps scaling past both ends now. */
  padding-block: 4.6875vw 4.125vw;
}
.about__head{ text-align:center; }
.about__title{ max-width: 45.704%; margin-inline:auto; font-size: var(--fs-h2); } /* 649/1420 */
/* client-specified: 15px / line-height auto / Medium 500, centred.
   Colour now comes from --c-body, which is the same #656565 site-wide. */
.about__lead{
  max-width: 66.8%; margin-inline:auto;         /* client-specified */
  margin-top: clamp(16px, 1.875vw, 30px);
  /* client-specified: Inter Medium 15 / #656565. Size, weight and colour were
     already right; the family was inheriting Poppins from <body>. */
  font-family: var(--font-ui);
  font-size: clamp(12px, .9375vw, 15px);
  line-height: normal;
  font-weight: 500;
  color: var(--c-body);
  text-align: center;
}
.about__lead p + p{ margin-top: clamp(14px, 1.25vw, 20px); }   /* a full blank line */
/* client-specified: inline links in the band are underlined, like the ones in
   the cards. The global `a{text-decoration:none}` was stripping them here. */
.about__lead a{
  color: inherit;
  text-decoration: underline;
  text-underline-offset: .18em;
}
.about__lead a:hover,
.about__lead a:focus-visible{ color: var(--c-ink); }
.about__head .btn{ margin-top: clamp(16px, 1.875vw, 30px); }

/* client-specified: breathing room under the band on the two pages where it sits
   straight on top of the gold CTA. 4.25vw = 68 at 1600 — client's number, set
   below the 6.25vw the renders measure there (99 on Pine Beetle, 100 on
   Organic). Uncapped like the band's own padding so the two keep their
   proportion past 1600. Opt-in: on the home page this band is followed by the
   services section with no gap, and that must not move. Reset below 900 — the
   request was for desktop, and the phone stack is left exactly as it was. */
.about--spaced{ margin-bottom: 4.25vw; }

/* The band is a fixed 508 in every render that has one — Pine Beetle, Organic
   and No Tent all measure 4727-5235 or its equivalent, even though their
   content differs by a heading line and a paragraph. So the height is the
   constant and the padding absorbs the difference, rather than the other way
   round. Centred so the extra space splits evenly, which is what the renders
   show (95 above / 105 below on the shortest of the three). */
.about--fixed{
  min-height: clamp(320px, 31.75vw, 508px);
  display:grid; align-content:center;
}

/* 3 cards - 471 - 203 - gap 9 */
.review-cards{
  margin-top: clamp(22px, 2.375vw, 38px);
  display:grid; grid-template-columns: repeat(3, minmax(0,1fr));
  gap: .6338%;                                  /* 9 / 1420 */
}
.review-card{
  background: rgba(255,255,255,.73);   /* client-supplied: #FFFFFF @ 73% */
  border-radius: var(--r-md);
  min-height: clamp(150px, 12.6875vw, 203px);
  padding: clamp(18px, 1.625vw, 26px) clamp(18px, 2.25vw, 36px);
  display:flex; flex-direction:column; justify-content:space-between;
}
.review-card__text{ font-size: var(--fs-body); }
.review-card__stars{ width: clamp(104px, 8.6875vw, 139px); height:auto; }

/* ============================================================ 4 services  */
/* Figma 48:173 heading y1922 - 48:174 slider y2044-2647 */
.services{ padding-top: clamp(44px, 5.125vw, 82px); }
/* Figma box 724 is the text's own width, not a constraint - capping at it makes
   the line wrap the moment the viewport is under 1600. */
.services__title{ text-align:center; max-width: 100%; margin-inline:auto; }

/* Client-specified: this section is NOT a carousel. One card, in normal flow.
   Extra .services__panel blocks simply stack, so a page that needs several
   services can add them without touching this file. */
.services__slider{ margin-top: clamp(34px, 4.125vw, 66px); position:relative; }
.services__track{ display:grid; gap: clamp(20px, 2vw, 32px); }
.services__panel{
  display:grid;
  grid-template-columns: 40.845% 57.746%;       /* 580 | 820 */
  gap: 1.4085%;                                 /* 20 / 1420 */
  align-items:stretch;
}

/* ---- carousel slide mechanics — reviews only ----------------------------
   Every slide occupies the SAME grid cell (grid-area 1/1), so the track is as
   tall as the tallest slide and nothing is absolutely positioned — heights
   stay automatic at every width. The track clips the horizontal travel, which
   is what keeps the slide-out from ever creating a horizontal scrollbar.
   If a renderer ignores `transform` the effect degrades to a clean fade. */
.reviews__right{
  display: grid;
  overflow: hidden;
}
.review-slide{
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transform: translateX(8%);
  transition: opacity .5s ease, transform .5s ease, visibility 0s linear .5s;
}
.review-slide.is-active{
  opacity: 1; visibility: visible;
  transform: none;
  transition: opacity .5s ease, transform .5s ease, visibility 0s;
}
/* the slide being replaced travels out to the left */
.review-slide.is-prev{ transform: translateX(-8%); }
/* 48:175 · the card is an irregular torn shape, exported at its exact 580×603.
   Its "torn" areas are painted #FBF0E8 — the page colour — so it blends without
   needing transparency. No border-radius: the shape carries its own edges. */
.services__copy{
  position:relative;
  /* service-card-plate.png is service-card-bg.png with its cut corners made
     genuinely transparent instead of painted the page cream. On the cream pages
     this renders identically — the paint was rgb(251,240,232), which is exactly
     the body background it was standing in for — but No Tent Termite Control
     puts this card on a black section, where the painted corners showed as two
     light triangles. The original file is kept alongside it. */
  background: url("/images/service-card-plate.png") center / 100% 100% no-repeat;
  border-radius: 0;
  /* The card's designed height, driven by its photo. 603 is the standard plate;
     pages whose photo is a different height (Rodent Control ships 820x441) set
     --card-h on the panel and the copy follows. Written as the Figma number so
     it reads straight off the design: n x .0625vw is n/1600 of the viewport,
     capped at n px. */
  min-height: clamp(240px, calc(var(--card-h, 603) * .0625vw), calc(var(--card-h, 603) * 1px));
  /* Vertical padding is a Figma number too, not a fixed proportion of the card:
     the 603 plate uses 76/85, the shorter 441 plate on Rodent Control uses
     42/34. Scaling 76/85 down with the card would have given 56/62 and pushed
     that card 55px past its designed height. */
  /* Horizontal insets are per-card too — a page whose copy is long is drawn
     with a wider measure. Defaults are the standard plate's 44 / 70 of 580. */
  padding: clamp(20px, calc(var(--card-pt, 76) * .0625vw), calc(var(--card-pt, 76) * 1px))
           var(--pad-r, 12.069%)
           clamp(20px, calc(var(--card-pb, 85) * .0625vw), calc(var(--card-pb, 85) * 1px))
           var(--pad-l, 7.586%);
  /* Column, with the copy taking up the slack, so the button sits at the FOOT
     of the card rather than trailing the text. That is what the design shows:
     the gap above the button varies card to card — tiny under a long block like
     Subterranean, large under a short one like Rodent — which only happens if
     the button is anchored to the bottom. */
  display:flex; flex-direction:column;
  /* A grid item's automatic minimum is its min-content width, and the wide
     button's `width: max-content` raised that — the 1fr track below 900 grew
     to 415px inside a 320px page and put 75px of horizontal scroll on the
     whole site. min-width:0 lets the card shrink to its track again; the
     button is capped by its own max-width, so nothing is clipped. */
  min-width: 0;
}                                               /* 76 / 70 / 85 / 44 */
.services__text{ flex: 1 1 auto; }
/* A flex child stretches by default; the button is a fixed-width frame in the
   design, so it opts out or it would run the full width of the card. */
.services__copy .btn{ align-self: flex-start; }
/* client-specified: card buttons are bold */
.services__copy .btn--primary{ font-weight: 700; }
.services__name{
  display:flex; align-items:center; gap: clamp(5px, .4375vw, 7px);
  font-size: var(--fs-h3); line-height: 1.4;    /* 56 / 40 */
}
.services__mark{
  flex:0 0 auto;
  width: clamp(18px, 1.75vw, 28px); height:auto;
}
/* 48:184 - 466 x 280 = 14 lines @ 20 */
/* client-specified: Inter Medium 15px. Fluid off the 1600 artboard the same
   way every other 15px block on this site is (.about__lead, .intro-band__lead),
   so it scales instead of pinning. Inter-Medium.ttf is loaded at the top of
   this file, so 500 is the real cut, not a synthesised one. */
.services__text{
  margin-top: clamp(14px, 1.4375vw, 23px);
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333;   /* 20 / 15 */
}
/* That margin is the gap BELOW the heading, so it only applies when there is
   one. Two cards open straight on the copy — Rodent Control row 2 and Pine
   Beetle row 2 — and there the margin pushed the first line 23px past the
   plate's own padding-top. Two classes deep so it outranks the rule above. */
.services__copy .services__text:first-child{ margin-top: 0; }
.services__copy .btn{ margin-top: clamp(14px, 1.4375vw, 23px); }

.services__media{
  position:relative; overflow:hidden;
  border-radius: 0; background: var(--c-surface-alt);   /* square in the design */
}
.services__media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }

/* ---- service rows (Services page) ----------------------------------------
   The home page's service card, repeated and alternated. It reuses
   .services__panel / __copy / __media as-is, so a change to the card shows up
   on both pages — the only new behaviour is which side the photo sits on. */
.service-rows{
  padding-block: clamp(34px, 4.125vw, 66px) clamp(40px, 4.5vw, 72px);
}
/* Every block in a card is separated by the same step — paragraph to paragraph,
   list to paragraph, paragraph to list, pill to anything. It was written as
   `p + p, p + ul` before, which left `ul + p` with nothing: lists carry no
   bottom margin, so the text under every list sat flush against it on all four
   service pages.
   Two classes deep on purpose: `.prose p{margin:0}` is (0,1,1) and would
   otherwise outrank a single-class rule here. */
/* Scoped to .services__copy, not .service-rows: the home page's card sits in
   .services__slider instead, so the rule never reached it and its two
   paragraphs ran together with no blank line between them. */
.services__copy .services__text > * + *{ margin-top: clamp(14px, 1.25vw, 20px); }
/* Row 5 is the only card with a list. Bullets indent inside the copy column
   rather than hanging outside it, which is what the design shows. */
.services__list{
  /* spacing comes from the flow rule above, not from the list itself */
  margin: 0;
  padding-left: 1.35em;
  list-style: disc;
}
.services__list li{ margin-top: 2px; }
.services__list li::marker{ font-size: .85em; }
/* Organic Pest Control's bullets are multi-line paragraphs, and that frame sets
   them a full line apart — measured 40px between the last line of one bullet
   and the first of the next, against 20 inside a bullet. Every other list on
   the site runs its items solid, so this is opt-in per list rather than a
   change to .services__list. Child combinator keeps it above the 2px rule. */
.services__list--spaced > li + li{ margin-top: var(--list-gap, clamp(14px, 1.25vw, 20px)); }
/* Location lists run as plain lines in the design — no markers, no indent —
   but they are still a list, so the markup stays a <ul>. */
.services__list--plain{ padding-left: 0; list-style: none; }
/* The outlined lead-in above a list. Same pill the eyebrows use, but on the
   cream card it takes the Service Areas treatment — white fill, ink rule —
   rather than the gold outline, which would read as a second button.
   TODO: border colour read off the page render; confirm against Figma. */
/* client-specified: Inter Medium 19px, and it holds one line at desktop. */
.services__pill{
  /* client-specified: 10px above and below the label inside the pill. .pill on
     its own only sets a min-height, so a wrapped label filled the box edge to
     edge. */
  padding-block: 10px;
  /* client-specified: transparent, so the card's plate reads straight through
     it. Only the ink rule marks the shape. */
  background: transparent;
  border-color: var(--c-ink);
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: clamp(13px, 1.1875vw, 19px);      /* 19 / 1600 */
  /* client-specified: text reads left, not centred. .pill centres its label for
     the eyebrow use, which is a single short line; a lead-in that wraps needs
     its lines to start together. */
  justify-content: flex-start;
  text-align: left;
  /* Same right-edge allowance the heading and wide button get. Without it the
     Professional Pest Control lead-in wanted 491px against a 485px measure and
     broke onto a second line over 6px. `width:max-content` means a pill that
     does not need the room simply does not take it. */
  max-width: calc(100% + var(--pad-r-esc));
}
/* Inline links inside the copy: body colour, underlined — they are not calls
   to action, so they must not compete with the gold button below them. */
.services__text a{
  color: inherit;
  text-decoration: underline;
  text-underline-offset: .18em;
}
/* client-specified: the numbered step labels inside a card are black, the same
   as the pills above them — the copy under each one stays body grey. */
.services__text strong{ color: var(--c-ink); }
.services__text a:hover,
.services__text a:focus-visible{ color: var(--c-ink); }
/* The heading glyph is a pest icon, not the quote mark the home page uses.
   The pest SVGs ship with fill-opacity="0.25" for the strip's resting state,
   so these headings point at mark-*.svg — the same artwork re-saved solid,
   because an <img> cannot have the file's own fill-opacity overridden in CSS. */
/* client-specified: bigger, and sitting slightly high against the heading.
   The size came from cropping, not scaling: the pest artwork is drawn inside a
   70x93 frame with ~29% empty above/below and up to 34% either side, so at a
   1em box the glyph was rendering well under 1em. The mark-*.svg viewBox is now
   the ink's own bounding box, which makes the drawing 1.41x taller at the same
   box height AND slightly narrower — width matters here because it counts
   against the heading's one-line fit. `top` rather than a transform: the
   preview pane does not honour transforms, so this stays measurable. */
/* Scoped to .services__copy, not .service-rows: the Gallery page uses the same
   plate inside .gallery-rows, and without this its icon fell back to the base
   rule — wrong size, and centred on the whole two-line heading so it floated
   in the gap between the lines instead of sitting on line one. */
.services__copy .services__mark{
  /* 1.1em everywhere, but a page can state its own: the Flea & Tick frame sets
     its tick at 29x34 ink, where the termites on the other pages measure ~39
     tall. Left as a default so no existing card moves. */
  width: auto; height: var(--mark-h, 1.1em);
  /* Aligned to the FIRST line, not to the middle of the whole heading. On a
     heading that wraps, `center` left the glyph floating at half-height with
     nothing beside it; flex-start plus half the leftover leading
     ((1.4 - 1.1) / 2 = .15em) sits it on line one. A single-line heading looks
     identical either way. */
  align-self: flex-start;
  margin-top: .15em;
  position: relative; top: -0.06em;
}
/* ---- reaching past the card's right padding -------------------------------
   Three things in a card may need to run wider than the copy measure and out
   to the plate's edge: an over-long heading, an over-long button label, and the
   outlined lead-in. They all need the SAME distance, and that distance changes
   with the padding — so it is stated once here as a variable and every consumer
   reads it. Restating the number at each consumer is what produced two
   specificity bugs earlier: a 3-class override quietly beat the mobile reset.

   The number is the right padding expressed as a fraction of the CONTENT box,
   not the border box: a percentage margin/width resolves against the parent's
   content, while the padding is a percentage of its border box. For the
   standard card, content is 1 - .12069 - .07586 = .80345, so .12069 / .80345
   = .15021. --tight changes the left inset, so its figure differs. */
.services__copy{ --pad-r-esc: 15.021%; }
.services__copy--tight{
  /* This card's copy runs long enough that the standard 7.586% left inset
     leaves no room for its heading — on the Professional Pest Control render
     the text starts ~20px from the plate edge, not 44, and the heading needs
     546px in a 574px plate. Right stays put so the paragraph measure matches
     every other card. .12069 / (1 - .035 - .12069) = .14295 */
  --pad-l: 3.5%;
  --pad-r-esc: 14.295%;
}
.services__name--flush{ margin-right: calc(-1 * var(--pad-r-esc)); }
/* Same card, same reason: this button's label is the longest on the page and
   its natural width is 519px against a 461px content box — squeezed by 58px,
   which pushed the label onto a second line and made the button 62px tall
   against everyone else's 52px.

   max-width has to exceed 100%: 100% of the containing block IS the content
   box, so it re-imposes the very limit being escaped. The +--pad-r-esc is the
   same allowance the heading gets — out to the card's right edge, no further. */
.services__copy .btn--wide{ width: max-content; max-width: calc(100% + var(--pad-r-esc)); }
.service-rows .wrap{
  display:grid;
  row-gap: clamp(20px, 2.5vw, 40px);
}
/* Even rows put the photo first. `order` rather than `direction` or a second
   grid definition: the DOM keeps heading-then-photo, so the reading order and
   the tab order stay correct however the columns are painted. */
.service-rows .services__panel:nth-child(even){
  grid-template-columns: 57.746% 40.845%;      /* mirrored 820 | 580 */
}
.service-rows .services__panel:nth-child(even) .services__copy{ order:2; }
.service-rows .services__panel:nth-child(even) .services__media{ order:1; }
/* The torn plate is NOT mirrored on the reversed rows — client-confirmed. Every
   card carries the plate the same way round, whichever side the photo is on. */

/* No Tent Termite Control sets one of its rows on a black full-bleed band.
   Measured off that render: the band runs 2877-3828 around a 712-tall row that
   starts at 2997, so 120 top and 119 bottom. Nothing inside changes — the card
   is the same plate, the same insets, the same ink colour. */
.service-rows--dark{
  background: var(--c-ink);
  /* Figma number, like the card's own insets: No Tent's band measures 120,
     Subterranean's 106, so the page states it rather than sharing one value. */
  padding-block: clamp(48px, calc(var(--dark-pad, 120) * .0625vw), calc(var(--dark-pad, 120) * 1px));
}

/* One step inside a card whose parts run on the same 20px rhythm as the copy —
   a bold label, a short list, then a closing line, with no block gaps between
   them. Wrapping them lets the step take the normal step from its siblings
   while its own parts stay solid. */
.services__step > * + *{ margin-top: 0; }

/* ---- prose + photo split --------------------------------------------------
   Two headings and their copy in the left column, one photo spanning the whole
   right column. No plate — the text sits straight on the page cream.
   The tracks are the home page's Seasonal Threats section exactly: 600 | 72 |
   748 against a 1420 content box, and the photo here is 748 wide, so it runs at
   its natural size rather than being cropped to a ratio. */
/* Figma numbers again — No Tent measures 91/101, Subterranean 99/98. */
.split{
  padding-block: clamp(56px, calc(var(--split-pt, 91) * .0625vw), calc(var(--split-pt, 91) * 1px))
                 clamp(60px, calc(var(--split-pb, 101) * .0625vw), calc(var(--split-pb, 101) * 1px));
}
.split__inner{
  display:grid; grid-template-columns: 42.254% 52.676%;
  gap: 5.0704%;
  /* Centred, not top-aligned: on both renders the photo is the shorter column
     on one page and the taller on the other, and in each the slack splits
     roughly evenly above and below rather than falling to the bottom. */
  align-items:center;
}
.split__copy{ min-width: 0; }
.split__title{ font-size: var(--fs-h2); }
/* Every block is one step apart; a heading takes a bigger one on both sides.
   Measured 31.5 below a heading, 32.5 above one, 20 between blocks.
   Two classes deep on purpose — the same trap the service cards hit: the copy
   carries .prose, and `.prose p{margin:0}` is (0,1,1), so a one-class rule here
   lost and every paragraph sat flush against the one above it. */
.split .split__copy > * + *{ margin-top: clamp(14px, 1.25vw, 20px); }
.split .split__copy > .split__title + *{ margin-top: clamp(20px, 1.9375vw, 31px); }
.split .split__copy > * + .split__title{ margin-top: clamp(22px, 2.0313vw, 32.5px); }
.split__copy p{ font-family: var(--font-ui); font-weight: 500;
                font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333; }
/* client-specified: the run-in labels are black, not body grey. Sampled from
   the render, their darkest pixel is 0,0,0 against 66 for the copy under them. */
.split__copy strong{ font-weight: 700; color: var(--c-ink); }
.split__copy a{ color: inherit; text-decoration: underline; text-underline-offset: .18em; }
.split__media img{ display:block; width:100%; height:auto; }

/* This page's band heading runs to one line at 787 — wider than the 45.704%
   the two-line headings need. 60% is 852, which clears it; it stays centred. */
.about__title--wide{ max-width: 60%; }
/* Same page's lead measures 854 at its widest, not the shared 948. Left at 948
   the closing paragraph pulled up onto one line instead of the designed two. */
.about__lead--tight{ max-width: 60.141%; }

/* Photo not delivered yet — same dashed marker the missing-copy blocks use, so
   an absent asset reads as "pending", never as a broken or empty box. */
/* No aspect-ratio here. The panel stretches its items, which makes the block's
   HEIGHT definite — and an element with an aspect-ratio then computes its WIDTH
   from that height instead of filling its track. It rendered 820 wide in an 811
   track (9px past the card) and 844 in the row whose heading wraps to two
   lines. The real .services__media has no ratio at this width either: it takes
   its height from the copy column and the photo fills it absolutely. Below 900
   the base rule's 820/603 applies, where one column makes the width definite
   and the ratio is safe. */
.services__media--pending{
  border: 1px dashed rgba(200, 60, 60, .45);
  background:
    repeating-linear-gradient(135deg, rgba(200,60,60,.05) 0 8px, transparent 8px 16px),
    var(--c-surface-alt);
}
body.copy-hide-gaps .services__media--pending{ border-color: transparent; background: var(--c-surface-alt); }

/* CTA band - Figma 48:196/48:197 y2677-2990 h313 */
.cta-band{
  margin-top: clamp(18px, 1.875vw, 30px);
  display:grid; grid-template-columns: 40.845% 57.746%;
  gap: 1.4085%;
  align-items:stretch;
}
.cta-band__figure{
  position:relative; height:100%; min-height: clamp(180px, 19.5625vw, 313px);
  border-radius: 0; overflow:hidden; background: var(--c-surface-alt);
}
.cta-band__figure img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
/* Black panel, not gold: first heading line gold, second white. */
/* 48:196 · the delivered 820x313 plate carries the corner glow, so the CSS
   gradient guess is gone. NOT clipped: the roach is meant to hang past the
   panel's bottom-right corner. Kept as PNG rather than JPEG — a smooth gradient
   over large flat black is exactly where JPEG bands. */
.cta-band__right{
  position:relative;
  border-radius: 0; color: var(--c-on-dark);
  background: #000 url("/images/cta-band-bg.png") center / 100% 100% no-repeat;
  min-height: clamp(180px, 19.5625vw, 313px);
  /* title box is 717 wide inside an 820 card → 39 left / 64 right, not 64/64 */
  padding: clamp(26px, 3.6875vw, 59px) 7.8049% clamp(26px, 3.65vw, 58.4px) 4.7561%;
  display:flex; flex-direction:column; justify-content:center;
  align-items:flex-start;
  gap: clamp(14px, 2vw, 32px);
}
.cta-band__title{
  max-width: 100%;                               /* content box is exactly 717 */
  color: var(--c-on-dark); font-size: var(--fs-h2);
  position:relative; z-index:1;
}
.cta-band__title em{ font-style:normal; color: var(--c-brand); }
/* the phone pill on the black panel is outlined, like the hero's */
.cta-band__right .btn--phone{
  position:relative; z-index:1;
  background: transparent; color: var(--c-brand-call);   /* label + icon #F5C841 */
  border: 1px solid #fff;
  padding-inline: clamp(8px, .625vw, 10px);
}
.cta-band__right .btn--phone:hover{ background:#fff; color: var(--c-ink); }
/* 48:206 · 260 wide at x1290 y2834 — it overhangs the 820x313 panel by 40 to
   the right and 104 below, so it is deliberately not clipped. */
.cta-band__roach{
  position:absolute; z-index:2;
  right: -4.878%;                                  /* -40 / 820  */
  bottom: -33.227%;                                /* -104 / 313 */
  width: 31.707%;                                  /*  260 / 820 */
  height:auto; pointer-events:none;
}

/* About Us runs the shared CTA band as its own section rather than tacked onto
   the end of another one, so it carries the band's own top inset: 98 from the
   black band above it in that render. */
.about-cta{ padding-top: clamp(40px, 6.125vw, 98px); }

.rule{
  height: clamp(5px, .5625vw, 9px);
  margin-top: clamp(60px, 7.4375vw, 119px);
  background: var(--c-brand);
}

/* ============================================================ 5 seasonal  */
/* Figma 48:215 - y3203-3885 - text 600 | gap 72 | media 748 */
.seasonal{ background: var(--c-cream-top); padding-block: clamp(48px, 5.3125vw, 85px) 0; }
.seasonal__inner{
  display:grid; grid-template-columns: 42.254% 52.676%;
  gap: 5.0704%;
  align-items:center;
}
/* Was 91.5% (549/600). That is the heading's own width at 1600, not a limit —
   and as the column shrank between 1200 and 1600 the cap squeezed the heading
   to three lines while --fs-h2 was still near its maximum. Full column, so it
   holds the designed two lines all the way across desktop. */
.seasonal__copy .section__title{ max-width: 100%; }
/* 48:218 - 600 x 200 = 10 lines @ 20 */
.seasonal__body{
  margin-top: clamp(16px, 1.6875vw, 27px);
  font-size: clamp(12px, .9688vw, 15.5px); line-height: 1.2903;   /* 20 */
}
/* the two paragraphs are separated by a blank line, same as the About block */
.seasonal__body p + p{ margin-top: clamp(14px, 1.25vw, 20px); }
.seasonal__copy .btn{ margin-top: clamp(16px, 1.6875vw, 27px); }

/* Square corners. 48:221 (661×682) sits behind 48:222 (748×682) at the same
   height, so it is fully covered in the design — dropped rather than rendered
   as a plate that could flash if the photo ever fails to load. */
.seasonal__media{
  position:relative; aspect-ratio: 748 / 682;
  border-radius: 0;
}
.seasonal__media img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; border-radius: 0;
}

/* ============================================================ 6 features  */
/* Figma 48:223 - y3980-4464 - two 700 - 415 cards, gap 20 */
/* the 73px gap up to the reviews band is cream too, so it belongs to this
   section's padding rather than a margin on .band */
.features{ background: var(--c-cream-top); padding-block: clamp(50px, 5.9375vw, 95px) clamp(46px, 4.5625vw, 73px); }
.features__grid{
  display:grid; grid-template-columns: repeat(2, minmax(0,1fr));
  gap: 1.4085%;                                          /* 20 / 1420 */
}
/* min-width:0 - a grid item defaults to min-width:auto, which would grow the
   track to the pill's max-content width instead of letting the pill shrink. */
.feature{ display:flex; flex-direction:column; align-items:center; min-width:0; }
.feature .pill{ margin-bottom: clamp(16px, 1.8125vw, 29px); }
/* square corners — the video stills sit flush, no rounding in the design */
.feature__media{
  position:relative; width:100%; aspect-ratio: 700 / 415;
  border-radius: 0; overflow:hidden; background: var(--c-surface-alt);
}
.feature__media img,
.feature__media iframe{ position:absolute; inset:0; width:100%; height:100%; border:0; }
.feature__media img{ object-fit:cover; }
/* the thumbnail is a <button> — strip the UA chrome, keep the focus ring */
.video{ padding:0; border:0; cursor:pointer; display:block; }
.video img{ transition: transform .3s ease; }
.video:hover img{ transform: scale(1.02); }

/* ============================================================ 7 reviews = */
/* Figma 48:1474 - y4537-4973 - left 649 | gap 51 | right 670 */
/* Figma 48:212 - a single gradient plate spanning reviews + why */
/* The 48:212 gradient used to paint here. Removed on request — the only
   section it was visible behind was Why Choose (Reviews is opaque #111 on top),
   which now sits on the flat --c-page. Kept as a wrapper so the section
   grouping and its paddings stay intact. */
/* 48:212 · 1600×1403. Sampled every 10% off the delivered PNG — it is a pure
   vertical gradient (max horizontal variation across the width: 2/255), so CSS
   reproduces it exactly at zero bytes and scales to any height. */
.band{
  background: linear-gradient(180deg,
    #FBF0E8   0%,
    #FBF0E8  30%,
    #FBEFE2  40%,
    #FCEFDD  50%,
    #FBEDD7  60%,
    #FCEDD2  70%,
    #FCECCC  80%,
    #FCEBC6  90%,
    #FDE8AF 100%);
}
/* the two sections inside it must not paint their own fill over the gradient */
.band .seasonal,
.band .features{ background: transparent; }
/* 48:213 - a 1600x436 dark plate sitting on top of the cream gradient */
.reviews{
  background: var(--c-ink); color: var(--c-on-dark-mute);
  padding-block: clamp(48px, 4.9375vw, 79px);
}
/* Dark-band colours live on the component rules further down — declaring them
   here as well was dead code: those later rules re-set `color` and won.       */
.reviews .section__title{ color: var(--c-on-dark); }
.reviews__inner{
  position:relative;
  display:grid; grid-template-columns: 45.704% 47.183%;
  gap: 3.5915%;
  justify-content:start; align-items:start;
}
/* divider - Figma 48:269 x721 ?' 44.437% of the 1420 content box */
.reviews__inner::before{
  content:""; position:absolute; left:44.437%; top:0; bottom:0;
  width:1px; background: rgba(255,255,255,.58);   /* client-specified */
}
/* pill ends 4660, title starts 4696 (36); title ends 4808, button 4841 (33) */
.reviews__left .section__title{ margin-block: clamp(20px, 2.25vw, 36px) clamp(18px, 2.0625vw, 33px); }
.reviews__rating{
  display:flex; align-items:center; gap: clamp(8px, .75vw, 12px);
  min-height: clamp(24px, 2.0625vw, 33px);           /* Figma 48:246 - 670 - 33 */
}
/* the 139-24 five-star SVG scaled to 116 reproduces Figma's 20px star / 24 pitch */
.reviews__stars{ width: clamp(87px, 7.25vw, 116px); height:auto; }
.reviews__source{ width: clamp(18px, 1.5625vw, 25px); height:auto; }
/* on the black band, not on cream — --c-muted is only 4.4:1 here */
.reviews__date{ font-size: var(--fs-body); line-height:1.5882; color: var(--c-on-dark); }
/* 48:244 - 609 x 145 = 6 lines @ 24. The right column is 670 wide, so the
   quote is capped at its own 609 box rather than filling the column. */
.reviews__quote{
  margin: clamp(14px, 1.5625vw, 25px) 0 0;
  max-width: 90.896%;                                    /* 609 / 670 */
  /* client-specified: Inter Medium 20. The size was already 20 at 1600; the
     family was inheriting Poppins from <body>. */
  font-family: var(--font-ui); font-weight: 500;
  font-size: clamp(15px, 1.25vw, 20px); line-height: 1.2083;      /* 24.17 */
  color: var(--c-on-dark);                 /* black band — not --c-body */
}
/* 48:245 - 127 x 29 */
.reviews__author{
  margin-top: clamp(10px, 1.4375vw, 23px);
  /* client-specified: Inter ExtraBold 26. 800 is a real cut — Inter-ExtraBold
     is loaded at the top of this file — not a synthesised weight. */
  font-family: var(--font-ui); font-weight: 800;
  font-size: clamp(18px, 1.625vw, 26px); line-height: 1.6111;
  color: var(--c-brand);                      /* gold on the black band — 12.5:1 */
}
/* slide mechanics for .review-slide live with the carousel block above */
/* Figma 48:233 - vertical column of 4 dots, x1502 (flush to the content edge),
   34 below the block top, pitch 52.8 ?' gap 44.8 */
.reviews__dots{
  /* the containing block is .wrap, so right:0 would land on its PADDING edge
     (1600) rather than the content edge (1510) — offset by the gutter */
  position:absolute; right: var(--gutter); top: clamp(18px, 2.125vw, 34px);
  display:flex; flex-direction:column; align-items:center;
  gap: clamp(5px, .5vw, 8px); color: var(--c-brand);
}

/* ============================================================ 8 why ===== */
/* Figma 48:1448 - y5065-5656 - inner 1180 wide, 3 cards 378.49, gap 22.31 */
.why{
  padding-block: clamp(56px, 5.75vw, 92px);
}
.why__inner{ max-width: 83.099%; margin-inline:auto; }   /* 1180 / 1420 */
.why__title{ text-align:center; }
.why__grid{
  margin-top: clamp(28px, 4vw, 64px);
  display:grid; grid-template-columns: repeat(3, minmax(0,1fr));
  gap: 1.8907%;                                          /* 22.31 / 1180 */
}
/* client-specified: 1.11px black border, square corners */
.why-card{
  background: transparent;                     /* fill only appears on hover */
  border: 1.11px solid #000; border-radius: 0;
  min-height: clamp(240px, 22.6816vw, 362.91px);
  padding: clamp(22px, 2.25vw, 36px) clamp(18px, 2.1vw, 34px) clamp(24px, 2.4938vw, 39.9px);
  display:flex; flex-direction:column; align-items:center; text-align:center;
}
.why-card__icon{ height: clamp(72px, 7.5625vw, 121px); width:auto; }   /* 48:278 h121 */
/* The design shows the middle card filled — that is the HOVER state drawn on
   one card, not a permanent variant. Every card gets it on hover. */
.why-card{ transition: background-color .18s ease; }
.why-card:hover,
.why-card:focus-within{ background: var(--c-tint); }
.why-card__title{
  margin-top: clamp(14px, 1.5vw, 24px);
  font-size: var(--fs-card-t); line-height: 1.8806;   /* 63 / 33.5 */
}
/* 48:276/282/288 - ~300 x 67 */
.why-card__text{
  margin-top: clamp(8px, .75vw, 12px);
  /* client-specified: all three run to three lines, as the design draws them.
     The copy filled the card's full inner width and the first two wrapped to
     two. 90% is the measured window: at 95% card 1 is still two lines, at 85%
     card 3 spills to four, so this sits in the middle of the only band where
     all three are three. Centred, so the narrower measure stays centred. */
  max-width: 90%; margin-inline: auto;
  /* client-specified: Inter Medium 15 / #656565 */
  font-family: var(--font-ui); font-weight: 500; color: var(--c-body);
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.4667;   /* 22 */
}
.why__cta{ display:flex; width:max-content; margin: clamp(28px, 3.5vw, 56px) auto 0; }

/* ============================================================ 9 gallery = */
/* Figma 53:2228 - y5748-6570 - text 400 | gap 80 | media 940 (460+20+460) */
/* 48:293 - 1600x822. Cream in the design, not a dark band. */
/* 48:293 · 1600×822. No asset was ever supplied for this fill, so this is the
   closest colour sampled from the design's own gradient (its 100% stop).
   TODO: replace with the real hex — this one line is the only place it lives. */
.gallery{
  background: var(--c-gallery); color: var(--c-body);
  padding-block: clamp(44px, 4.3125vw, 69px) clamp(44px, 4.375vw, 70px);
}
.gallery__inner{
  display:grid; grid-template-columns: 28.169% 66.197%;
  gap: 5.6338%;
  align-items:center;
}
.gallery__copy .section__title{ color: var(--c-ink); }
.gallery__copy .btn{ margin-top: clamp(18px, 1.9375vw, 31px); }
.gallery__grid{
  display:grid;
  grid-template-columns: repeat(2, minmax(0,1fr));
  grid-template-rows: 343fr 320fr;
  column-gap: 2.1277%;                                   /* 20 / 940 */
  row-gap: clamp(8px, 1.25vw, 20px);
  aspect-ratio: 940 / 683;
}
.gallery__item{ position:relative; min-height:0; border-radius: 0; overflow:hidden; }
.gallery__item img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.gallery__item--tall{ grid-column:1; grid-row: 1 / 3; }
.gallery__item--top{ grid-column:2; grid-row:1; }
.gallery__item--bottom{ grid-column:2; grid-row:2; }

/* ============================================================ 10 areas == */
/* Figma 48:359 - y6646-7305 - inner 1180 - grid 3-3, card 383.36, gaps 21.75 */
.areas{ padding-block: clamp(48px, 4.75vw, 76px) clamp(48px, 5.625vw, 90px); }
.areas__inner{ max-width: 83.099%; margin-inline:auto; }
.areas__head{ text-align:center; display:flex; flex-direction:column; align-items:center; }
/* Figma box is 716 wide but that is the text width, not a constraint - the head
   is centred, so a wider box only delays the wrap. */
.areas__head .section__title{
  max-width: 100%;
  margin-top: clamp(12px, 1.375vw, 22px);
}
/* 48:412 - 996 x 120 = 6 lines @ 20 */
.areas__body{
  max-width: 89.407%;                                    /* client-specified */
  margin-top: clamp(14px, 1.875vw, 30px);
  /* 15, not 16: at 16 the second paragraph runs to 3 lines. At 15 it wraps to
     2, which with the blank line makes the block 3+1+2 = 120 — Figma's height. */
  /* client-specified: Inter Medium 15 */
  font-family: var(--font-ui); font-weight: 500;
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333;     /* 20 */
}
/* the two paragraphs are separated by a blank line, as in About and Seasonal */
.areas__body p + p{ margin-top: clamp(14px, 1.25vw, 20px); }
.areas__grid{
  margin-top: clamp(28px, 3.25vw, 52px);
  display:grid; grid-template-columns: repeat(3, minmax(0,1fr));
  column-gap: 1.8432%;                                   /* 21.75 / 1180 */
  row-gap: clamp(12px, 1.3594vw, 21.75px);
}
.area-card{
  display:flex; align-items:center; gap: clamp(10px, 1.0313vw, 16.5px);
  min-height: clamp(66px, 6.1175vw, 97.88px);
  padding-inline: clamp(12px, 1.3594vw, 21.75px);
  border:1.36px solid var(--c-brand); border-radius: 0;   /* gold rule, square */
  background: var(--c-surface);
}
/* the yellow disc is baked into pin.svg - no wrapper needed */
.area-card__pin{
  flex: 0 0 auto;
  width: clamp(32px, 2.75vw, 44px); height: clamp(32px, 2.75vw, 44px);
}
/* client-specified: Inter 19. Weight left at the 600 the design draws. */
.area-card__name{
  font-family: var(--font-ui); font-weight:600; color: var(--c-ink);
  font-size: clamp(14px, 1.1875vw, 19px);
}
/* Service Areas page only: a delivered map still, not an embed. 1187x502, and
   the render puts it on the same 1188-wide centred column as the card grid, 61
   below it. Square corners, like every other plate on the site. */
.areas__map{ margin-top: clamp(28px, 3.8125vw, 61px); }
/* The embed holds the still's 1187x502 box rather than the iframe's own 600x450
   attribute, so the section keeps the height the render measured. The floor
   stops it collapsing to a 147px letterbox on a phone, where 2.365:1 would be
   too shallow to read or pan. */
.areas__map iframe{
  /* height:auto is load-bearing — the iframe's height="502" attribute is a
     presentational hint that outranks aspect-ratio, so without it the map
     stayed 502 tall at every width instead of following the box. */
  display:block; width:100%; height:auto; aspect-ratio: 1187 / 502; min-height: 260px;
  border:0; border-radius: 0;
}

/* ---- article inner page (tips/*.php) -------------------------------------
   No design was supplied for the article page itself, so this is assembled
   from tokens the Tips listing already uses: the same badge, the same title
   size, the same photo radius and the same measure as .article-rows__lead.
   Nothing new is invented visually. */
.post{ padding-block: clamp(40px, 5.5vw, 88px) clamp(44px, 6vw, 96px); }
/* 693 is the photo's native width and the width the Tips listing gives it, so
   the image is never stretched up. A hard px cap, NOT a percentage: this sits
   inside .wrap, and a % would be measured against the full-bleed section while
   the gutters ate into it — which rendered the column 190px wide at 390.
   Below 693 it simply goes fluid. Doubles as a sane reading measure; the title
   is not repeated here because the hero above already carries it as the h1. */
.post__inner{ max-width: 693px; margin-inline: auto; }
.post__badge{ justify-content: flex-start; }
.post__media{
  margin-top: clamp(20px, 2.25vw, 36px);
  border-radius: clamp(10px, 1.25vw, 20px); overflow: hidden;
}
.post__media img{ display:block; width:100%; height:auto; }
.post__body{ margin-top: clamp(20px, 2.25vw, 36px); }
/* two classes deep: `.prose p{margin:0}` is (0,1,1) and beats a one-class rule */
.post .post__body > * + *{ margin-top: clamp(14px, 1.25vw, 20px); }
.post__back{ margin-top: clamp(24px, 2.75vw, 44px); }

/* ============================================================ 11 contact  */
/* Figma 73:2140 - y7395-8139 - faq 743 | gap 129 | form 500 */
/* Figma 73:2140 - 1600-744 dark plate, exactly the section box */
.contact{
  background: var(--c-dark-contact) url("/images/contact-bg.jpg") center / cover no-repeat;
  color: var(--c-on-dark-mute);
  padding-block: clamp(28px, 2.6875vw, 43px);   /* form card starts 43 in */
}
.contact .section__title{ color: var(--c-on-dark); }
.contact .faq__item{ background: rgba(255,255,255,.06); }
.contact .faq__label{ color: var(--c-on-dark); }
.contact__faq{ margin-top: clamp(22px, 2.9375vw, 47px); }  /* faq col starts 90 in */
.contact__inner{
  display:grid; grid-template-columns: 52.324% 35.211%;
  column-gap: 9.0845%;
  align-items:start;
}
.contact__faq .section__title{
  max-width: 57.739%;                                    /* 429 / 743 */
  margin-block: clamp(12px, 1.375vw, 22px) clamp(20px, 2.4375vw, 39px);
}

/* ---- contact-us.php · the details column ---------------------------------
   Same section, same plate, same grid and the same form card as the home
   page's Contact band — only the left column changes. The design has no hero
   and no pest strip here, so the plate starts straight under the header.
   The card is the taller item and the details column is centred against it,
   which is the one behaviour this variant has to override.                  */
.contact--details .contact__inner{ align-items:center; }
.contact__details{ color: var(--c-on-dark); }
/* 92-370 x 319-364 — one line at the same 56 as every other section title.
   Solved, not guessed: Impact 56 measures asc 45 / desc 1 against a 56 line
   box, so the cap ink sits 5 below the box top. 40 here puts it on 319. */
.contact__title{ margin-top: clamp(20px, 2.5vw, 40px); }
/* 568 wide inside a 743 column: the rules stop short, they are not full bleed */
.ct-list{ max-width: 76.447%; margin-top: clamp(15px, 1.5625vw, 25px); }
/* rows sit 81 apart — 32 of content, 24 above, 24 below, plus the 1px rule */
.ct-item{
  display:flex; align-items:center;
  gap: clamp(8px, .75vw, 12px);
  padding-block: clamp(14px, 1.5vw, 24px);
  font-family: var(--font-ui);
  /* Solved from the rendered advance width, not read off a layer: the Figma
     ink runs 133-320 for "Jupiterpest.com" and 133-390 for the phone line.
     Inter Regular 26 measures 186.6 and 255.2 against those; 24 would be 15
     short. Regular, not Medium — 500 overshoots the first by 3. */
  font-size: clamp(17px, 1.625vw, 26px);
  line-height: clamp(22px, 2vw, 32px);
  color: var(--c-on-dark);
}
.ct-item + .ct-item{ border-top: 1px solid rgba(255,255,255,.22); }
/* Sized by HEIGHT only. The globe and the phone are 17x17 and the pin is
   14x17, and the design lets each keep its own width rather than padding them
   into a fixed slot — so the labels start 133 / 128 / 133, not all at 133. */
.ct-item__icon{ flex:0 0 auto; width:auto; height: clamp(22px, 2vw, 32px); }
.ct-item a{ color:inherit; text-decoration:none; }
.ct-item a:hover,
.ct-item a:focus-visible{ text-decoration:underline; text-underline-offset:.18em; }
/* four 40s, 13 apart — .social already carries the tile size */
.ct-social{
  margin-top: clamp(18px, 1.75vw, 28px);
  display:flex; gap: clamp(9px, .8125vw, 13px);
}

/* accordion - rows 743 - 74, pitch 88 ?' gap 14 */
.faq{ display:flex; flex-direction:column; gap: clamp(8px, .875vw, 14px); }
/* dark row with a white rule, square corners */
.faq__item{
  background: rgba(255,255,255,.06);
  border: 1px solid #FFFFFF;
  border-radius: 0;
}
.faq__q{
  width:100%; display:flex; align-items:center; justify-content:space-between;
  gap: clamp(12px, 1.25vw, 20px);
  /* 72, not 74: the 1px rule on .faq__item sits outside this box and the row
     must total Figma's 74 */
  min-height: clamp(54px, 4.5vw, 72px);
  padding-inline: clamp(16px, 1.5625vw, 25px);
  text-align:left;
}
.faq__label{ font-size: var(--fs-faq); font-weight:600; line-height:1.263; color: var(--c-on-dark); }
/* 48:424 · 32×32 — a filled gold disc with a dark "+" */
.faq__sign{
  flex:0 0 auto; width: clamp(24px, 2vw, 32px); height: clamp(24px, 2vw, 32px);
  display:grid; place-items:center;
  border-radius:50%; background: var(--c-brand); color: var(--c-on-brand);
  font-size: clamp(16px, 1.375vw, 22px); line-height:1; font-weight:600;
  transition: transform .18s ease;
}
.faq__item.is-open .faq__sign{ transform: rotate(45deg); }
.faq__a{
  font-size: clamp(13px, 1vw, 16px); line-height: 1.625;
  color: var(--c-on-dark-mute);
  display: grid;                       /* see the 0fr/1fr note below */
}
/* Three elements, and each one has exactly one job:
     .faq__a        the grid whose single row animates 0fr -> 1fr
     .faq__a-inner  the clipper — overflow:hidden and NO padding of its own
     p              the copy, and the only thing carrying padding
   The padding has to sit on the grandchild. Put it on the clipper and a closed
   row still stands 25px tall: border-box folds padding into a height the box
   HAS, and it cannot shrink a box below its own padding — so every collapsed
   answer left a dead 25px strip under its question. Measured: 25 before this
   split, 0 after. */
.faq__a-inner{ min-height: 0; overflow: hidden; }
.faq__a p{
  margin: 0;
  padding: 0 clamp(16px, 1.5625vw, 25px) clamp(16px, 1.5625vw, 25px);
}
/* Animating to "the height of the content" is impossible with height/max-height
   unless JS measures it first: `auto` is not an interpolatable value and a
   guessed max-height either clips a long answer or coasts through empty space
   on a short one. Transitioning the GRID ROW from 0fr to 1fr does interpolate,
   and it resolves to whatever the answer actually needs — so all four rows open
   at the same speed regardless of how much copy they hold.

   Collapsed only once main.js has run and added .is-ready. With JS off the
   answers stay open and readable, which is better than the old `hidden`
   attribute left every one of them permanently unreachable. */
.faq.is-ready .faq__a{
  grid-template-rows: 0fr;
  opacity: 0; visibility: hidden;
  /* visibility flips only at the END of the close, so the text does not vanish
     before it has finished sliding away — and while closed it is out of the
     accessibility tree and out of the tab order, which is what `hidden` gave. */
  transition: grid-template-rows .34s cubic-bezier(.4, 0, .2, 1),
              opacity .2s ease,
              visibility 0s linear .34s;
}
.faq.is-ready .faq__item.is-open .faq__a{
  grid-template-rows: 1fr;
  opacity: 1; visibility: visible;
  /* opening, the fade trails the slide slightly so the copy arrives settled */
  transition: grid-template-rows .34s cubic-bezier(.4, 0, .2, 1),
              opacity .26s ease .08s,
              visibility 0s;
}
/* the row's tint lifts while it is open, so the active question reads as active */
.faq__item{ transition: background-color .24s ease; }
.faq__item.is-open{ background: rgba(255,255,255,.10); }

/* form card - Figma 48:303 - 500 - 658.07 */
/* dark card, square corners — the gold accent bar runs across the top */
.form-card{
  position:relative;
  background: #000000; border-radius: 0;
  border: 0.89px solid #5F5F5F;                /* client-specified, all round */
  box-shadow: var(--shadow);
  overflow:hidden;
  padding: clamp(28px, 2.6786vw, 42.86px) 7.1428% clamp(28px, 2.6786vw, 42.86px);
}
.form-card__accent{
  position:absolute; inset:0 0 auto; height: clamp(4px, .3348vw, 5.36px);
  background: var(--c-brand);
}
/* 48:306 · 414 × 72 → 2 lines, so line-height is 36.
   Poppins Bold here, not the Impact display face the other headings use. */
.form-card__title{
  /* client-specified: Outfit Bold. Was Poppins Bold — the size below was solved
     against Poppins' measure, but the line break is a hard <br> in the markup,
     so it holds whichever face is used. */
  font-family: var(--font-form); font-weight:700;
  /* 27, not 28: at 27 "Get Fast, Reliable Pest Control" measures 414.7 — exactly
     Figma's 414 text box — so the line breaks after "Control" as designed. */
  font-size: clamp(20px, 1.6875vw, 27px); line-height: 1.3333;   /* 36 */
  color: var(--c-on-dark);
}

.form{ margin-top: clamp(16px, 1.7857vw, 28.57px); display:flex; flex-direction:column; gap: clamp(12px, 1.1161vw, 17.86px); }
.field{ display:flex; flex-direction:column; gap: clamp(5px, .4464vw, 7.14px); }
.field-row{ display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 3.3333%; } /* 14.29 / 428.57 */

/* ---- form feedback -------------------------------------------------------
   The honeypot must be gone for people but still be a real, fillable input for
   a bot. `display:none` and `visibility:hidden` are what the better bots check
   for, so this is moved off-screen instead — and left:-9999px rather than a
   transform, because a transformed box can still be reported on screen. */
.field--trap{
  position:absolute !important;
  left:-9999px !important; top:auto !important;
  width:1px !important; height:1px !important;
  overflow:hidden !important;
}
/* Red on the black form card: #FF6B6B is 6.3:1 there. The site's own gold is
   the "required" marker already, so errors need a colour of their own. */
.field__error{
  margin: 0;
  font-family: var(--font-field, var(--font));
  font-size: clamp(11px, .8125vw, 13px); line-height:1.4;
  color: #FF6B6B;
}
.field__error::before{ content: "! "; font-weight:700; }
.form__alert{
  margin: 0 0 clamp(8px, .75vw, 12px);
  padding: clamp(10px, .875vw, 14px) clamp(12px, 1vw, 16px);
  border: 1px solid #FF6B6B; border-radius: var(--r-sm);
  background: rgba(255,107,107,.12);
  font-size: clamp(12px, .875vw, 14px); line-height:1.45;
  color: #FFC9C9;                         /* 12.9:1 on the card, measured */
}
/* Belt and braces: an alert with nothing in it must never paint a bare red box.
   Same trap as `.copy[hidden]` further up — a styled wrapper that outlives its
   content is worse than no wrapper at all. */
.form__alert:empty{ display:none; }
.field__error:empty{ display:none; }
/* label box is 15 tall in Figma - do not inherit the 1.55 body line-height */
/* Size solved from the Figma label widths: "Full Name" 66, "Describe the issue"
   119, "Target Service Type" 127 — all land on 13px Poppins SemiBold. Box is 15. */
.field__label{
  font-size: clamp(11px, .8125vw, 13px); font-weight:600;
  text-transform: uppercase; letter-spacing:.04em;
  color: var(--c-on-dark-mute);
  height: clamp(13px, .9375vw, 15px); line-height: clamp(13px, .9375vw, 15px);
}
.field__req{ color: var(--c-brand); margin-left:3px; }
.field__control{
  position:relative; display:flex; align-items:center;
  min-height: clamp(38px, 2.625vw, 42px);
  border:1px solid rgba(255,255,255,.18); border-radius: var(--r-sm);
  background: #000000;
  padding-inline: clamp(10px, .8929vw, 14.29px);
  gap: clamp(6px, .6696vw, 10.71px);
}
.field__control--area{ align-items:flex-start; min-height: clamp(72px, 6.1384vw, 98.21px); padding-block: clamp(8px, .7813vw, 12.5px); }
/* The wrapper is a row flex with align-items:flex-start so the icon sits on the
   first line — but that also stopped the textarea stretching, so it kept its
   own `rows` height and left a dead strip of box below it. Only the textarea
   opts back into stretch; the icon keeps its top alignment. */
.field__control--area textarea{ align-self: stretch; }
/* Figma gives every field icon a 16.071 square FRAME; the delivered SVGs are the
   vectors inside it, at their own sizes (user 12x14, mail 16x13, phone 16x16,
   bug 16x15, bar 14x12). `scale-down` keeps the outer box fixed so every label
   starts at the same x, while drawing the leaf at its designed size — never
   upscaled, so the 1.786 stroke stays 1.786. */
.field__icon{
  flex:0 0 auto;
  width:  clamp(14px, 1.0045vw, 16.07px);
  height: clamp(14px, 1.0045vw, 16.07px);
  object-fit: scale-down;
}
.field__control input,
.field__control select,
.field__control textarea{
  flex:1 1 auto; min-width:0; width:100%;
  border:0; background:none;
  font-family: var(--font-field);                /* client-specified: Geist */
  font-size: clamp(11px, .8369vw, 13.39px);      /* client-specified */
  color: var(--c-on-dark);
  resize: vertical;
}
/* Focus belongs on the WRAPPER, because the wrapper is what reads as the
   field — the black box, the border and the icon. The global
   `:focus-visible{ outline: 2px solid gold; outline-offset: 3px }` was landing
   on the bare <input> inside it, so clicking a field drew a second gold box
   just inside the first. The ring moves out to .field__control and the inner
   outline is dropped; focus is still unmistakable, and now it frames the icon
   too instead of cutting it off. */
.field__control:focus-within{
  border-color: var(--c-brand);
  box-shadow: 0 0 0 1px var(--c-brand);
}
.field__control input:focus,
.field__control input:focus-visible,
.field__control select:focus,
.field__control select:focus-visible,
.field__control textarea:focus,
.field__control textarea:focus-visible,
.select__toggle:focus,
.select__toggle:focus-visible{ outline: none; }
/* same sage the field icons are drawn in */
.field__control input::placeholder,
.field__control textarea::placeholder{ color: var(--c-form-icon); opacity:1; }
.field__control select{ color: var(--c-on-dark); appearance:none; }
/* These reach the OS popup in Chromium — colour and background do, padding
   does NOT, which is why the rows sat flush against the popup's left edge.
   Kept as the no-JS fallback; main.js replaces the popup with .select__list. */
.field__control select option{
  color: var(--c-ink); background: #fff;
  padding: 10px 12px; line-height: 1.9;
  font-size: 14px;
}

/* ---- styled select (main.js section 8) -----------------------------------
   The native <select> keeps the value and still posts it; everything visible
   is the button and the list below. Only applied once JS has enhanced it, so
   the plain control above stays the fallback. */
.field__control--select{ cursor:pointer; }
.field__control--select.is-open{ border-color: var(--c-brand); }
.select__native{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  border:0; clip-path: inset(50%); overflow:hidden; white-space:nowrap;
}
.select__toggle{
  flex:1 1 auto; min-width:0; width:100%;
  display:block; text-align:left;
  padding:0; border:0; background:none; cursor:pointer;
  font-family: var(--font-field);
  font-size: clamp(11px, .8369vw, 13.39px);
  color: var(--c-on-dark);
}
.select__value{ display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.select__list{
  position:absolute; z-index:30;
  left:-1px; right:-1px; top:calc(100% + 6px);
  margin:0; padding:6px; list-style:none;
  background:#0B0B0B;
  border:1px solid rgba(255,255,255,.18); border-radius: var(--r-sm);
  box-shadow: 0 18px 40px rgba(0,0,0,.55);
  max-height: 268px; overflow-y:auto;
}
/* the padding the OS popup threw away */
.select__option{
  padding: 10px 12px;
  border-radius: calc(var(--r-sm) - 2px);
  font-family: var(--font-field);
  font-size: clamp(11px, .8369vw, 13.39px);
  line-height: 1.35;
  color: var(--c-on-dark-mute);
  cursor:pointer;
}
.select__option + .select__option{ margin-top: 2px; }
.select__option.is-active{ background: rgba(255,255,255,.09); color: var(--c-on-dark); }
.select__option[aria-selected="true"]{ background: var(--c-brand); color: var(--c-ink); }

/* 48:341 chevron 14.29 frame · 48:353 arrow 14.29 · 48:356 lock 12.5 */
.field__chevron{
  flex:0 0 auto;
  width:  clamp(12px, .8929vw, 14.29px);
  height: clamp(12px, .8929vw, 14.29px);
  object-fit: scale-down;
}
/* it opens the list now, so it has to look and behave like a control */
.field__control--select .field__chevron{ cursor:pointer; transition: transform .18s ease; }
.field__control--select.is-open .field__chevron{ transform: rotate(180deg); }
.btn__arrow{
  flex:0 0 auto;
  width:  clamp(12px, .8929vw, 14.29px);
  height: clamp(12px, .8929vw, 14.29px);
  object-fit: scale-down;
}
.form__lock{
  flex:0 0 auto;
  width:  clamp(11px, .78125vw, 12.5px);
  height: clamp(11px, .78125vw, 12.5px);
  object-fit: scale-down;
}
/* the flex `gap` already contributes 17.86 - this tops it up to Figma's 28.57 */
.form__footer{ margin-top: clamp(4px, .6696vw, 10.71px); }
.form__privacy{
  margin-top: clamp(10px, .8929vw, 14.29px);
  min-height: clamp(13px, .9375vw, 15px);
  display:flex; align-items:center; justify-content:center; gap: clamp(5px, .4464vw, 7.14px);
  /* 48:358 · text box 304 wide → 10.5px measured, not the 14px --fs-small */
  /* the card is dark now — --c-body would be 3.24:1 here */
  font-size: clamp(9px, .65625vw, 10.5px); line-height: clamp(13px, .9375vw, 15px); color: var(--c-on-dark-mute);
}

/* ============================================================ 12 tips === */
/* Figma 57:7760 - y8219-9098 - media 693 | gap 27 | cards 700 */
.tips{ padding-block: clamp(50px, 5vw, 80px) clamp(60px, 6.3125vw, 101px); }
.tips__head{ text-align:center; display:flex; flex-direction:column; align-items:center; }
.tips__head .section__title{ max-width: 100%; }           /* Figma box 716 = text width */
/* 48:446 - 996 x 20, one line */
/* client-specified: Inter Regular 20 / #000 */
.tips__sub{
  max-width: 70.141%; margin-top: clamp(14px, 1.875vw, 30px);
  font-family: var(--font-ui); font-weight: 400;
  font-size: clamp(15px, 1.25vw, 20px); line-height: 1.3333;
  color: #000000;
}  /* 996 / 1420 */

.tips__inner{
  margin-top: clamp(30px, 3.75vw, 60px);
  display:grid; grid-template-columns: 48.803% 49.296%;
  gap: 1.9014%;
  /* NOT stretch: a stretched grid item with aspect-ratio derives its WIDTH from
     the row height and overflows its track into the next column. */
  align-items:start;
}
.tips__media{
  position:relative; margin:0; width:100%; aspect-ratio: 693 / 713;
  border-radius: 0; overflow:hidden; background: var(--c-surface-alt);
}
.tips__media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }

/* client-specified gap */
.tips__list{ display:grid; grid-template-rows: 1fr 1fr; gap: clamp(12px, 1.15vw, 25px); }
.tip-card{
  background: var(--c-panel); border-radius: 0;   /* square in the design */
  border: 1px solid #EAE3D8;                      /* client-specified */
  min-height: clamp(220px, 21.5vw, 344px);
  padding: 5.7143%;                                       /* 40 / 700 */
  display:flex; flex-direction:column;
}
.tip-card__badge{ display:flex; align-items:center; gap: clamp(8px, .75vw, 12px); }
/* the peach circle is baked into the 48-48 SVG */
.tip-card__icon{ width: clamp(36px, 3vw, 48px); height: clamp(36px, 3vw, 48px); }
/* 48:454/463 · Fraunces 72pt SemiBold Italic */
.tip-card__num{
  font-family: var(--font-accent); font-weight:600; font-style:italic;
  font-size: var(--fs-body); color: var(--c-brand);
}
/* 48:455/464 - 620 x 68 (2 lines) */
.tip-card__title{
  margin-top: clamp(10px, 1.25vw, 20px);
  font-size: clamp(20px, 1.75vw, 28px); line-height: 1.2143;   /* 34 */
}
/* 48:456/465 - 620 x 104 (4 lines @ 26) */
.tip-card__text{
  margin-top: clamp(12px, 1.5vw, 24px);
  font-family: var(--font-tip);                  /* client-specified: Manrope */
  font-size: clamp(13px, 1vw, 16px); line-height: 1.625;
}

/* ---- article rows (Tips and Articles) -------------------------------------
   The home page's tip card, one per row, with its photo beside it and its
   button underneath. Measured off the 6400x22744 render (4x the artboard):

     row 1   photo x90-783 (694)  gap 26  card x810-1510 (700)
     rows alternate; the photo is always 694 and the card always ~700
     photo 693x436 · card 345 tall · card->button 40 · button 200x53
     row pitch 511 (437 photo + 74 gap)

   The column split is the same 48.803 / 1.9014 / 49.296 the Tips section on the
   home page already uses, so the two stay in step. */
.article-rows{ padding-block: clamp(30px, 3.75vw, 60px) clamp(48px, 6.125vw, 98px); }
/* 996 / 1420, the same measure the home page's tips sub-head uses. At the full
   width this ran to two lines; the render sets it in three. */
.article-rows__lead{
  max-width: 70.141%; margin-inline: auto;
  margin-top: clamp(14px, 1.875vw, 30px);
  font-family: var(--font-ui); font-weight: 500;
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333;
  color: var(--c-body);
}
/* 70 clear of the head paragraph — the list had no margin at all and the first
   photo sat straight under the copy. */
.article-rows__list{
  margin-top: clamp(32px, 4.375vw, 70px);
  display:grid; row-gap: clamp(28px, 4.625vw, 74px);
}
.article-row{
  display:grid; grid-template-columns: 48.803% 49.296%;
  gap: 1.9014%;
  /* NOT stretch: the media carries an aspect-ratio, and a stretched item would
     take its width from the row height and spill into the next track — the
     same trap .tips__inner documents. */
  align-items:start;
}
/* Even rows put the card first. `order` rather than a reversed grid so the DOM
   keeps photo-then-card and the reading order stays right. The tracks swap too:
   the photo stays 694 and the card 700 whichever side each is on. */
.article-row:nth-child(even){ grid-template-columns: 49.296% 48.803%; }
.article-row:nth-child(even) .article-row__media{ order:2; }
.article-row:nth-child(even) .article-row__body{ order:1; }

.article-row__media{
  position:relative; margin:0; width:100%; aspect-ratio: 693 / 436;
  border-radius: 0; overflow:hidden; background: var(--c-surface-alt);
}
.article-row__media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }

/* The button sits BELOW the card, not inside it — so the column is a flex
   stack and the button shrinks to its own width instead of filling. */
.article-row__body{ display:flex; flex-direction:column; align-items:flex-start; }
.article-row__body .btn{ margin-top: clamp(18px, 2.5vw, 40px); }

.article-rows__more{
  display:flex; width:max-content;
  margin: clamp(40px, 6.1875vw, 99px) auto 0;
}

/* ---- testimonials (Reviews and Testimonials page) -------------------------
   The home page's review typography — stars, source, date, quote, author — on
   the same black band, but laid out as two independent columns of reviews under
   a centred head instead of a carousel. Measured off the 6400x14264 render
   (4x the artboard):

     band 1078-2477 · pill 615-985 x1158-1200 · title 283-1317, one line
     columns 674 | 72 | 674  (the right column's first review starts at x836)
     quote 20/29 here, not the 20/24.17 the carousel uses
     review -> review 73 · button 226x53, centred, 92 below the last author
   ------------------------------------------------------------------------- */
.testimonials{
  background: var(--c-ink); color: var(--c-on-dark);
  padding-block: clamp(44px, 5vw, 80px) clamp(56px, 6.5625vw, 105px);
}
.testimonials__head{ text-align:center; display:flex; flex-direction:column; align-items:center; }
.testimonials__head .section__title{
  color: var(--c-on-dark); max-width: 100%;
  margin-top: clamp(16px, 1.8125vw, 29px);
}
.testimonials__grid{
  margin-top: clamp(36px, 5.1875vw, 83px);
  display:grid; grid-template-columns: repeat(2, minmax(0,1fr));
  gap: 5.0704%;                                            /* 72 / 1420 */
  align-items:start;
}
/* Two independent stacks, not a row grid: in the render the second review
   starts at a different y in each column, so the columns do not share rows. */
.testimonials__col{ display:flex; flex-direction:column; gap: clamp(32px, 4.5625vw, 73px); }
.testimonials .reviews__quote{
  max-width: 100%;                    /* the carousel's 609/670 cap is not here */
  line-height: 1.45;                  /* 29 / 20 — the carousel sets 24.17 */
}
.testimonials__more{ display:flex; width:max-content; margin: clamp(40px, 5.75vw, 92px) auto 0; }

/* ---- gallery rows ---------------------------------------------------------
   The service card's torn plate beside a YouTube facade, alternating sides.
   Measured off the 6400x19800 render (4x the artboard):
     row 1  plate x90-626 (536) | gap 53 | video x679-1508 (829)
     video 829x470 · rows at y1106 / 1675 / 2244, so 470 tall, 99 apart
   ------------------------------------------------------------------------- */
.gallery-rows{ padding-block: clamp(34px, 4.125vw, 66px) clamp(40px, 4.5vw, 72px); }
.gallery-rows__list{ display:grid; row-gap: clamp(28px, 6.1875vw, 99px); }
.gallery-row{
  display:grid; grid-template-columns: 37.746% 58.380%;
  gap: 3.873%;                                          /* 55 / 1420 */
  align-items:start;
}
/* Even rows put the video first; the tracks swap with them so the plate stays
   536 and the video 829 whichever side each is on. */
.gallery-row:nth-child(even){ grid-template-columns: 58.380% 37.746%; }
.gallery-row:nth-child(even) .gallery-row__media{ order:1; }
.gallery-row:nth-child(even) .services__copy{ order:2; }

/* No border here. The gold frame is baked into the exported still — its outer
   ~5px is #FFCC00 — so adding one drew a second frame outside the first. The
   baked frame is rounded at ~10px against the image's own 829 width, and the
   radius here matches it so the square box does not leave dark corners
   outside the curve. .625vw keeps the two in step as the box scales. */
.gallery-row__media{
  position:relative; width:100%; aspect-ratio: 829 / 470;
  border: 0; border-radius: clamp(10px, 1.25vw, 20px);   /* client-specified */
  overflow:hidden; background: var(--c-ink);
}

/* client-specified: the copy sits centred in the plate, not pinned to the top.
   The render agrees — row 1 leaves 83 above the ink and 82 below it, row 3
   leaves 114 and 110. The text block has to stop growing for the centring to
   show; --card-pt / --card-pb stay on as minimums. */
.gallery-row .services__copy{ justify-content:center; }
.gallery-row .services__text{ flex: 0 0 auto; }
.gallery-row__media img,
.gallery-row__media iframe{ position:absolute; inset:0; width:100%; height:100%; border:0; }
.gallery-row__media img{ object-fit:cover; }

/* ---- proven results band --------------------------------------------------
   Band y2815-3859 on the warm plate; title one line, then a 3 x 2 grid of
   459x343 stills, 20 apart, on the same 1420 content box. */
.proof{
  background: var(--c-cream-bot);
  padding-block: clamp(48px, 7.6875vw, 123px) clamp(44px, 6.625vw, 106px);
}
.proof__title{ text-align:center; }
.proof__grid{
  margin-top: clamp(24px, 3.5vw, 56px);
  display:grid; grid-template-columns: repeat(3, minmax(0,1fr));
  gap: clamp(10px, 1.4085vw, 20px);                     /* 20 / 1420 */
}
/* Each tile is the button that opens the lightbox. */
.proof__tile{
  position:relative; display:block; width:100%; padding:0;
  border:0; border-radius:0; overflow:hidden; cursor: zoom-in;
  aspect-ratio: 459 / 343; background: var(--c-surface-alt);
}
.proof__tile img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition: transform .3s ease;
}
.proof__tile:hover img,
.proof__tile:focus-visible img{ transform: scale(1.04); }

/* ---- lightbox -------------------------------------------------------------
   Built by main.js on first use. Without JS the tiles are plain buttons that
   do nothing, so the markup carries no dead dialog. */
.lightbox{
  position:fixed; inset:0; z-index:200;
  display:flex; flex-direction:column;
  background: rgba(0,0,0,.92);
  /* opacity rather than display, so the fade has something to animate */
  opacity:0; visibility:hidden;
  transition: opacity .2s ease, visibility 0s linear .2s;
}
.lightbox.is-open{ opacity:1; visibility:visible; transition-delay: 0s; }
.lightbox__stage{
  flex:1 1 auto; min-height:0; position:relative;
  display:flex; align-items:center; justify-content:center;
  overflow:hidden;
}
.lightbox__img{
  max-width:100%; max-height:100%;
  transform-origin:center center;
  transition: transform .2s ease;
  user-select:none; -webkit-user-drag:none;
}
/* zoomed in, the image can be dragged around inside the stage */
.lightbox__stage.is-zoomed{ cursor:grab; }
.lightbox__stage.is-zoomed.is-panning{ cursor:grabbing; }
.lightbox__stage.is-zoomed .lightbox__img{ transition:none; max-width:none; max-height:none; }

.lightbox__bar{
  flex:0 0 auto;
  display:flex; align-items:center; justify-content:center; flex-wrap:wrap;
  gap: clamp(6px, .75vw, 12px);
  padding: clamp(10px, 1vw, 16px) var(--gutter);
  color: var(--c-on-dark);
}
.lightbox__count{
  font-family: var(--font-ui); font-weight:500;
  font-size: clamp(12px, .9375vw, 15px);
  min-width: 5.5em; text-align:center;
}
.lightbox__btn{
  width: clamp(36px, 2.75vw, 44px); height: clamp(36px, 2.75vw, 44px);
  flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  padding:0; border:1px solid rgba(255,255,255,.35); border-radius:50%;
  background: transparent; color: var(--c-on-dark);
  cursor:pointer;
  transition: background-color .18s ease, color .18s ease, opacity .18s ease;
}
.lightbox__btn:hover,
.lightbox__btn:focus-visible{ background: var(--c-brand); color: var(--c-ink); border-color: var(--c-brand); }
.lightbox__btn[disabled]{ opacity:.3; cursor:default; }
.lightbox__btn[disabled]:hover{ background:transparent; color: var(--c-on-dark); border-color: rgba(255,255,255,.35); }
.lightbox__btn svg{ width:16px; height:16px; }
.lightbox__btn--prev svg{ width:10px; height:15px; }
.lightbox__btn--next svg{ width:10px; height:15px; transform: rotate(180deg); }
/* close sits over the stage, clear of the controls */
.lightbox__btn--close{
  position:absolute; top: clamp(10px, 1vw, 16px); right: var(--gutter); z-index:2;
}
body.lightbox-open{ overflow:hidden; }

/* ---- slider controls ------------------------------------------------------
   Built by main.js, never in the markup: with JS off there are no buttons to
   press and the scroller still works by touch and trackpad. Shared look for
   both kinds — the overflow scroller and the class-toggled carousels. */
.scroller-btn,
.carousel-nav__btn{
  flex: 0 0 auto;
  width: clamp(32px, 2.25vw, 36px); height: clamp(32px, 2.25vw, 36px);
  display:inline-flex; align-items:center; justify-content:center;
  padding:0; border:1px solid var(--c-brand); border-radius:50%;
  background: transparent; color: var(--c-brand);
  cursor:pointer;
  transition: background-color .18s ease, color .18s ease, opacity .18s ease;
}
.scroller-btn:hover, .scroller-btn:focus-visible,
.carousel-nav__btn:hover, .carousel-nav__btn:focus-visible{
  background: var(--c-brand); color: var(--c-ink);
}
.scroller-btn svg,
.carousel-nav__btn svg{ width: 8px; height: 12px; }
.scroller-btn--next svg,
.carousel-nav__btn--next svg{ transform: rotate(180deg); }
.scroller-btn[disabled]{ opacity:.3; cursor:default; }
.scroller-btn[disabled]:hover{ background:transparent; color: var(--c-brand); }

/* Hidden until the strip actually overflows — JS adds .is-on. */
.scroller-nav{ display:none; justify-content:center; gap:10px; margin-top: 14px; }
.scroller-nav.is-on{ display:flex; }

/* grab cursor only once there is something to drag */
.is-draggable{ cursor:grab; }
.is-draggable.is-dragging{ cursor:grabbing; user-select:none; }
/* Snap fights both a finger on the track and a programmatic smooth scroll —
   it re-anchored the arrows' scroll straight back to the start. Off for the
   duration of either, and the paging target is a whole number of items so
   turning it back on causes no jump. */
/* The doubled class is a specificity bump, not a typo. Written once these were
   (0,1,0) and LOST to `.pests__list{ scroll-snap-type: x proximity }` in the
   600 block simply because that rule comes later in the file — so snap stayed
   on through the whole tween, re-anchoring scrollLeft on every frame and
   turning the arrows' glide into a jump. At (0,2,0) the state wins wherever a
   component sets snap on itself, including inside a media block. */
.is-dragging.is-dragging,
.is-paging.is-paging{ scroll-snap-type:none; }
.is-dragging.is-dragging,
.is-paging.is-paging{ scroll-behavior:auto; }

/* Client-specified: no arrows on desktop. Above 900 the bars carry position
   and the drag below carries movement; on the About testimonials there is not
   even a carousel up here, it is a 3-up grid, so arrows were meaningless.
   They come back at 900, where the grid collapses and touch takes over. */
/* One row: prev arrow, bars, next arrow — centred as a group (main.js builds
   it and puts each button in separately, so they bracket the bars).
   No margin here on desktop: up there the arrows are hidden and .dots-row is
   too, so the row is a zero-height box and any margin would show as a stray gap
   under the grid. The spacing is added back in the 900 block. */
.carousel-controls{
  display:flex; align-items:center; justify-content:center;
  gap: clamp(12px, 1.25vw, 20px);
}
/* nothing stacks any more, so the children's own top margins go — .reviews__dots
   included, which picks one up in the 900 block when it stops being absolute */
.carousel-controls .dots-row,
.carousel-controls .reviews__dots{ margin-top: 0; }
/* Client-specified: no arrows on desktop. Hidden per BUTTON now rather than on
   a wrapper, because there is no wrapper left to hide. */
.carousel-controls .carousel-nav__btn{ display: none; }

/* ---- drag ----------------------------------------------------------------
   The slides are stacked in one grid cell and swapped by class, not scrolled,
   so this is a gesture rather than a scroll: main.js follows the pointer for
   feedback and commits past a threshold. `is-swipeable` is only set while the
   slides really are stacked, so the About grid never claims to be draggable.
   pan-y keeps the page scrolling vertically under a horizontal drag. */
.is-swipeable{ cursor: grab; touch-action: pan-y; }
.is-swipeable.is-swiping{ cursor: grabbing; user-select: none; }

/* ============================================================ 13 cta ==== */
/* Figma 48:467 - y9199-9653 - text 482 | gap 127 | media 811 */
.cta{
  background: var(--c-brand); color: var(--c-on-brand);
  padding-block: clamp(16px, 1.5625vw, 25px) clamp(16px, 1.5vw, 24px);
}
/* client-specified: the heading sits on one line. It needed 482px against the
   designed 33.944% column's 477px — 5px short — so the column takes a little
   from the gap. The van keeps its 57.113%. */
.cta__inner{
  display:grid; grid-template-columns: 35% 57.113%;
  gap: 7.887%;
  align-items:center;
}
/* client-specified: copy is vertically centred against the van. It used to be
   pinned to the top with a 129px offset, which put its centre 47px below the
   van's; align-items:center on the grid does the whole job once that is gone. */
.cta__copy{ align-self: center; }
.cta__title{ color: var(--c-on-brand); font-size: var(--fs-h2); }
/* 48:471 - 353 x 20, one line */
.cta__sub{
  margin-top: clamp(10px, 1.125vw, 18px);
  /* client-specified: Inter Medium 15. Weight and size were already right; the
     family was inheriting Poppins from <body>. */
  font-family: var(--font-ui);
  font-size: clamp(12px, .9375vw, 15px); line-height: 1.3333;
  color: #656565; font-weight:500;              /* client-specified */
}
.cta__actions{ margin-top: clamp(16px, 2.125vw, 34px); display:flex; flex-wrap:wrap; gap: clamp(10px, 1.1875vw, 19px); }
/* On the gold band the roles invert: the phone pill is a dark outline and the
   primary button is solid black — gold-on-gold would be invisible. */
.cta .btn--phone{
  background: transparent; color: var(--c-on-brand);
  border: 1px solid var(--c-on-brand);
  padding-inline: clamp(8px, .625vw, 10px);
}
.cta .btn--phone:hover{ background: var(--c-on-brand); color: var(--c-brand); }
.cta .btn--primary{ background: var(--c-ink); color: var(--c-on-dark); }
.cta .btn--primary:hover{ background:#000; color: var(--c-brand); }
.cta__media{ position:relative; aspect-ratio: 811 / 405; }
.cta__media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }

/* ============================================================ footer ==== */
/* Figma 48:480 - y9653-10289 - 4 cols 298, pitch 314 (gap 16), block 1280 */
.site-footer{
  background: #000000; color: var(--c-on-dark);
  padding-block: clamp(44px, 5vw, 80px) clamp(36px, 4.25vw, 68px);
  /* client-specified: everything in the footer is Inter. Sizes and weights are
     unchanged; this only replaces the families the elements were inheriting —
     Poppins from <body> for the copy, and the display face on the four column
     headings via the global h1/h2/h3 rule, which needs its own override. */
  font-family: var(--font-ui);
}
.site-footer h1, .site-footer h2, .site-footer h3,
.site-footer h4, .site-footer h5, .site-footer h6{ font-family: var(--font-ui); }
.footer__cols{
  max-width: 90.141%; margin-inline:auto;                 /* 1280 / 1420 */
  display:grid; grid-template-columns: repeat(4, minmax(0,1fr));
  gap: 1.25%;                                             /* 16 / 1280 */
}
.footer__heading{
  color: var(--c-on-dark); font-size: var(--fs-fheading);
  line-height: 1.2222;                                    /* 33 / 27 */
  margin-bottom: clamp(12px, 1vw, 16px);
}
.footer__list{ display:flex; flex-direction:column; gap: clamp(6px, .625vw, 10px); }
.footer__item{ display:flex; align-items:center; gap: clamp(8px, .625vw, 10px); font-size: var(--fs-link, var(--fs-body)); line-height: 1.5882; color: var(--c-footer-text); }
/* fixed 17 slot + contain: globe and phone are 17x17, the pin is 14x17. Sizing
   by width alone stretched the pin to 17x21 and pushed its label out of line. */
.footer__item img{
  flex:0 0 auto;
  width: clamp(13px, 1.0625vw, 17px);
  height: clamp(13px, 1.0625vw, 17px);
  object-fit: contain;
}
/* every footer link is underlined in the design */
.footer__item a,
.footer__links a,
.footer__viewall,
.footer__copy a{ text-decoration: underline; text-underline-offset: .18em; }

.footer__social{ margin-top: clamp(12px, 1vw, 16px); display:flex; gap: clamp(10px, 1vw, 16px); }
/* the rounded tile + border are baked into the 40-40 social SVGs */
.social{ display:block; }
.social img{ width: clamp(32px, 2.5vw, 40px); height:auto; }

.reviews-box{
  margin-top: clamp(12px, 1vw, 16px);
  background: #333333; border: 0;               /* client-specified */
  border-radius: var(--r-md);
  padding: clamp(12px, 1vw, 16px);
}
.reviews-box__head{ display:flex; align-items:center; gap: clamp(8px, .625vw, 10px); }
/* badge tile baked into reviews-badge.svg */
.reviews-box__icon{ width: clamp(22px, 1.75vw, 28px); height:auto; }
.reviews-box__title{ color: var(--c-on-dark); font-weight:600; font-size: var(--fs-body); line-height:1.5294; }
.reviews-box__body{ margin-top: clamp(8px, .75vw, 12px); }
.reviews-box__stars{ width: clamp(78px, 6.5vw, 104px); height:auto; }  /* 5 - 16px, pitch 22 */
/* Wraps. In the 2-column footer around 880-900 the four words need 148px in a
   146px box and "Reviews" pushed 2px past the card. Nowrap had no way out of
   that; wrapping costs nothing at any width where the row already fits. */
.reviews-box__stats{
  margin-top: clamp(4px, .5vw, 8px); display:flex; align-items:baseline;
  flex-wrap: wrap;
  gap: clamp(4px, .375vw, 6px);
  font-size: var(--fs-body); line-height: 1.5;              /* box is 27 tall */
}
.reviews-box__num{ color: var(--c-on-dark); font-weight:700; }
.reviews-box__cta{
  margin-top: clamp(8px, .75vw, 12px);
  display:flex; align-items:center; justify-content:center;
  min-height: clamp(38px, 2.9375vw, 47px); border-radius: var(--r-sm);
  background: var(--c-brand); color: var(--c-on-brand); font-weight:600;
}
.reviews-box__cta:hover{ background:#ffd633; }

.footer__links{ display:flex; flex-direction:column; gap: clamp(5px, .5vw, 8px); }
.footer__links a,
.footer__viewall{
  display:inline-flex; align-items:center; gap: clamp(6px, .5vw, 8px);
  font-size: var(--fs-body); line-height: 1.5882;         /* 17 / 27 */
  color: var(--c-footer-text);
}
.footer__links a:hover, .footer__viewall:hover{ color: var(--c-on-dark); }
.footer__links img, .footer__viewall img{ width: clamp(6px, .4375vw, 7px); height:auto; flex:0 0 auto; }
.footer__viewall{ margin-top: clamp(12px, 1vw, 16px); font-weight:600; }

/* margin lives on the wrapper, not the divider - a child's top margin would
   collapse straight through this box and land outside it. */
.footer__bottom{ max-width: 90.141%; margin: clamp(16px, 1.5vw, 24px) auto 0; }
.footer__divider{ height:1px; background: rgba(255,255,255,.12); }
.footer__bar{
  margin-top: clamp(12px, 1.0625vw, 17px);
  min-height: clamp(40px, 2.6875vw, 43px);
  display:flex; align-items:center; justify-content:space-between;
  gap: clamp(16px, 1.5vw, 24px); flex-wrap:wrap;
}
.footer__social--bottom{ margin-top: 0; }
.footer__brand img{ width: clamp(150px, 12.9969vw, 207.95px); height:auto; }
/* client-specified: the copyright line reads bold */
.footer__copy{ font-size: var(--fs-body); font-weight: 700; }

/* ============================================================================
   BREAKPOINTS - 1200 (stack) - 900 - 600
   Every column width restated here so nothing above survives into the
   stacked layout.
   ========================================================================== */

@media (max-width: 1200px){
  .nav{ margin-left: 6%; }
  .nav__link{ padding-inline: 10px; }

  .hero__content{ width: 56%; }
  .hero__title{ max-width: 100%; }

  /* client-specified: the short second row is centred, not left-aligned.
     Ten half-columns, each pest spanning two, so the 4-item row can start on
     column 2 and land exactly centred — a plain 5-column grid always packs a
     short row to the left. Item width is unchanged: 2 tracks + the gap they
     absorb = the same 18% a 5-column grid gave. */
  .pests__list{ grid-template-columns: repeat(10, minmax(0,1fr)); gap: 2.5%; }
  .pest{ grid-column: span 2; }
  .pest:nth-child(6){ grid-column: 2 / span 2; }

  .about__title{ max-width: 70%; }
  .about__lead{ max-width: 85%; }

  .services__panel{ grid-template-columns: 46% 52.5%; gap: 1.5%; }
  /* Mirror of the line above. The Services page's alternation selector is
     3 classes deep, so it beats this one-class rule on specificity no matter
     which comes first — without restating it, odd rows narrowed to 46/52.5
     here while even rows stayed on the 1600 split and the two stopped being
     mirrors of each other. */
  .service-rows .services__panel:nth-child(even){ grid-template-columns: 52.5% 46%; }
  /* Padding is 8% here rather than 12.069%, so the escape distance changes with
     it and every consumer follows: .08 / (1 - .08 - .08) = .09524. Set through
     the same variables the base rule reads, so a page that overrode --pad-l /
     --pad-r inline keeps its own values below 1200 as well. */
  .services__copy{ --pad-l: 8%; --pad-r: 8%; --pad-r-esc: 9.524%; }
  /* keeps the tighter left inset proportional; .08 / (1 - .035 - .08) = .09040 */
  .services__copy--tight{ --pad-l: 3.5%; --pad-r-esc: 9.040%; }
  .cta-band{ grid-template-columns: 40.845% 57.746%; }

  .seasonal__inner{ grid-template-columns: 46% 48%; gap: 6%; }
  /* Same easing the seasonal split gets, and for the same reason: the 600/748
     tracks are the 1600 measures and squeeze the copy too hard below that. */
  .split__inner{ grid-template-columns: 46% 48%; gap: 6%; }

  .reviews__inner{ grid-template-columns: 47% 47%; gap: 6%; }
  .reviews__inner::before{ left: 50%; }

  .why__inner, .areas__inner{ max-width: 100%; }

  .gallery__inner{ grid-template-columns: 32% 62%; gap: 6%; }

  .contact__inner{ grid-template-columns: 55% 39%; column-gap: 6%; }

  .tips__inner{ grid-template-columns: 48.803% 49.296%; gap: 1.9014%; }
  .tips__head .section__title{ max-width: 70%; }
  .tips__head .copy{ max-width: 85%; }

  .cta__inner{ grid-template-columns: 38% 55%; gap: 7%; }
}

@media (max-width: 900px){
  /* --- header: off-canvas nav --- */
  .navbar__inner{ position:relative; }
  /* Must out-stack the panel. .nav is a DESCENDANT of .site-header, so the
     header's z-index:100 is never compared against the panel's 95 — inside the
     header's own stacking context a positioned .nav paints over a static
     button, and the open panel simply covered the toggle. Same context, higher
     z-index, so the X stays on top of the panel that slid under it. */
  .hamburger{ display:flex; order:3; position:relative; z-index:96; }
  .navbar__phone{ margin-left:auto; order:2; }
  .brand{ order:1; }

  /* Driven by `right`, not `transform`: a percentage translate depends on the
     panel's own resolved width, and `visibility` keeps the closed panel out of
     the tab order and off the hit-test map. */
  .nav{
    --panel-w: min(84vw, 360px);
    position: fixed; top:0; bottom:0; left:auto;
    right: calc(-1 * var(--panel-w));
    z-index:95;
    width: var(--panel-w);
    margin-left: 0;
    /* Black, matching the navbar it slides out of. It used to be --c-surface
       (#ffffff) while .nav__link keeps its on-navbar white — white on white,
       1.00:1, so every link was invisible and only the gold active item showed
       at 1.51:1. That was the whole "menu nazar nahi aa raha". */
    background: #000;
    border-left: 1px solid rgba(255,255,255,.14);
    padding: calc(var(--header-h) + 16px) 24px 24px;
    visibility: hidden;
    transition: right .28s ease, visibility 0s linear .28s;
    overflow-y:auto;
    box-shadow: -8px 0 32px rgba(16,26,34,.18);
  }
  .nav.is-open{
    right: 0; visibility: visible;
    transition: right .28s ease, visibility 0s linear 0s;
  }
  .nav__list{ flex-direction:column; align-items:stretch; gap: 4px; }
  .nav__link{ padding: 14px 12px; font-size: 17px; }
  /* in the panel the submenu is an inline accordion, not a floating layer */
  .nav__menu{
    position:static; opacity:1; visibility:visible; transform:none;
    background:none; border:0; padding: 0 0 4px 12px;
    display:none; transition:none;
  }
  .nav__item--has-menu.is-open > .nav__menu{ display:block; }
  .nav__item--has-menu:hover > .nav__menu,
  .nav__item--has-menu:focus-within > .nav__menu{ display:none; }
  .nav__item--has-menu.is-open:hover > .nav__menu,
  .nav__item--has-menu.is-open:focus-within > .nav__menu{ display:block; }
  .nav__menu a{ padding: 10px 12px; font-size: 15px; }

  /* --- hero: client-specified — the copy stays ON the photo at tablet.
     The crop is anchored left of centre so the technician is not cut, and a
     bottom-up scrim carries white text over the bright van. --- */
  .hero{ position:relative; background-position: 34% center; }
  /* The article hero carries its own flat overlay at every width, so both of
     the van-photo treatments are switched off here: the 34% crop anchor, and
     the bottom-up scrim below, which would darken that edge twice over. */
  .hero--post{ background-position: center; }
  .hero--post::after{ display:none; }
  /* The scrim rides on the full-bleed section, NOT on .wrap. Hung off .wrap it
     had to reach back over the gutter with negative offsets, and an absolutely
     positioned box that sticks out to the right still counts toward
     scrollWidth — that was 44px of horizontal scroll at 768. */
  .hero::after{
    content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
    /* Sized from the composited pixels, not by eye: at a softer .78/.62/38%
       ramp the headline's worst pixel over the van's white lettering was
       2.12:1. These stops hold the whole title box above 4.5:1. */
    background: linear-gradient(to top,
                rgba(0,0,0,.82) 0%, rgba(0,0,0,.72) 55%, rgba(0,0,0,0) 96%);
  }
  .hero__inner{
    position:relative; z-index:1;
    min-height: clamp(430px, 62vw, 560px);
    padding-bottom: clamp(28px, 4vw, 44px);
  }
  /* client-specified: hero copy centred at tablet */
  .hero__content{ width:100%; text-align:center; }
  .hero__title{ max-width:100%; }
  .hero__actions{ justify-content:center; }

  /* client-specified: 5 then 4, second row centred. The 10-track columns and
     the nth-child(6) start come from the 1200 block; only the gaps change. */
  .pests__list{ gap: 3%; row-gap: 20px; }

  .about__title, .about__lead{ max-width:100%; }
  /* Desktop-only, as asked — the phone stack keeps the band flush to the CTA. */
  .about--spaced{ margin-bottom: 0; }
  /* Same reset the About band gets, and for the same reason: 52.817% and
     65.915% are the Figma measures against a 1420 content box. Left on, they
     squeezed the heading into a 183px column at 390 and broke it over five
     lines. The band is centred, so full width is the correct measure here. */
  .intro-band__title, .intro-band__lead{ max-width:100%; }

  /* --- testimonials become a carousel here (client-specified) --------------
     One grid cell, every card stacked in it, so the box takes the height of
     the tallest card and nothing is positioned absolutely. */
  .review-cards{
    display:grid; grid-template-columns: 1fr; gap:0;
    overflow:hidden;
  }
  .review-card{
    grid-area: 1 / 1; min-height:0;
    opacity:0; visibility:hidden; transform: translateX(8%);
    transition: opacity .5s ease, transform .5s ease, visibility 0s linear .5s;
  }
  .review-card.is-active{
    opacity:1; visibility:visible; transform:none;
    transition: opacity .5s ease, transform .5s ease, visibility 0s linear 0s;
  }
  .review-card.is-prev{ transform: translateX(-8%); }
  /* hover fill belongs to .why-card, not here — keep the carousel card inert */

  /* client-specified: service card content centred at tablet */
  .services__title{ max-width:100%; }
  /* minmax(0,…), not a bare 1fr: a bare 1fr carries an auto minimum that
     content can push past. Paired with min-width:0 on the card above. */
  .services__panel{ grid-template-columns: minmax(0, 1fr); gap: 16px; }
  /* The Services page's alternation is a 3-class selector, so it outranks the
     single-column rule above no matter where that sits. Restated here at equal
     specificity, or every even row would stay two columns on a phone. */
  .service-rows .services__panel:nth-child(even){ grid-template-columns: minmax(0, 1fr); }
  /* Stacked, every row reads heading-then-photo — the same order the home
     page's card uses on a phone. Alternating it here would make the page lurch
     between "heading first" and "photo first" as you scroll, which is only
     meaningful when the two sit side by side. */
  .service-rows .services__panel:nth-child(even) .services__copy{ order:0; }
  .service-rows .services__panel:nth-child(even) .services__media{ order:0; }
  /* Content is centred here, so the button centres with it — the flex-start
     opt-out above would otherwise pin it to the left of a centred card. */
  /* client-specified: card content reads LEFT on mobile. Only the button's own
     label stays centred, and the button spans the content width. */
  .services__copy{ min-height:0; padding: 28px 24px 32px; text-align:left; }
  .services__copy .btn{
    align-self: stretch;
    /* A wrapped label had nowhere to breathe: line-height 22.2 over two lines
       is 44.5 against a 42px min-height with zero vertical padding, so the
       text filled the button edge to edge. Tighter leading plus real padding
       gives the two-line labels a proper box; one-line ones barely move. */
    line-height: 1.2;
    padding-block: 12px;
  }
  /* One column here: the card is full width and centred, so both the heading
     and the wide button have room to spare and sit inside the padding like
     everything else. */
  /* One column: nothing needs to reach past the padding, and zeroing the one
     variable turns it off for the heading, the wide button and the pill at
     once. Both card variants are single-class here, and this rule comes last,
     so it wins over --tight without a specificity fight. */
  .services__copy,
  .services__copy--tight{ --pad-r-esc: 0%; }
  .services__name{ justify-content:flex-start; margin-left:0; }
  /* The glyph used to carry ~34% empty space inside its own frame, and that
     read as the gap. Cropping the viewBox to the ink made the drawing bigger
     but took the spacing with it, so the termite was touching the "O" of
     "Organic". The gap has to be a real one now. */
  .services__copy .services__name{ gap: 10px; }
  /* `.services__mark{ left:24px; top:28px }` used to live here. It was inert —
     the mark had no `position` — until the glyph gained `position:relative` for
     its -0.06em nudge, and then it woke up and shoved the icon 24px right and
     28px down, straight over the heading text. Deleted rather than countered:
     a dead offset that springs to life later is worse than no offset. */
  /* Stacked, the media needs a ratio of its own — but not every page's photo is
     820x603 (the Professional Pest Control card ships 820x681), so the card
     passes its own through --media-ar and this stays the default. */
  .services__media{ aspect-ratio: var(--media-ar, 820 / 603); }

  .cta-band{ grid-template-columns: 1fr; gap: 16px; }
  /* The plate is exactly 580x313. Held to its own ratio the whole frame shows;
     the old min-height made it a ~4:1 letterbox and cut both faces off. */
  .cta-band__figure{ min-height:0; aspect-ratio: 580 / 313; }
  .cta-band__right{ min-height:0; padding: 28px 24px; }
  /* client-specified: the roach must not cross the copy. It is pulled out of
     the panel and the title is capped short of it, so the two never meet. */
  .cta-band__roach{ width: 21%; right: -2%; bottom: -9%; }
  .cta-band__title{ max-width: 74%; }
  .cta-band__right .btn--phone{ align-self:flex-start; }

  /* The split stacks copy-then-photo, left aligned — it is body copy with
     sub-headings, not a centred lead, so centring it would strand the bold
     labels. Photo keeps its own ratio because it is no longer in a fixed
     track. */
  .split__inner{ grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .split{ padding-block: 40px; }

  /* The article rows stack photo-then-card. Left as two columns they squeezed
     the photo to 169px on a phone and drove the cards past 600 tall. The even
     rows' order and swapped tracks are both restated here — a 2-class
     :nth-child selector outranks a 1-class one wherever it sits. */
  .article-row,
  .article-row:nth-child(even){ grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .article-row:nth-child(even) .article-row__media{ order:0; }
  .article-row:nth-child(even) .article-row__body{ order:0; }
  /* Full-width button, same as the service cards get down here. */
  .article-row__body{ align-items: stretch; }
  .article-rows__list{ row-gap: 32px; }

  /* Gallery rows stack plate-then-video. Left as two columns the plate was
     131px wide and 959 tall on a phone, with a 202px video beside it. Both
     the even rows' order and their swapped tracks are restated — a 2-class
     :nth-child selector outranks a 1-class one wherever it sits. */
  .gallery-row,
  .gallery-row:nth-child(even){ grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .gallery-row:nth-child(even) .gallery-row__media{ order:0; }
  .gallery-row:nth-child(even) .services__copy{ order:0; }
  .gallery-rows__list{ row-gap: 32px; }
  /* Three 109px tiles do not read as photographs. */
  .proof__grid{ grid-template-columns: repeat(2, minmax(0,1fr)); }

  /* One column of reviews: at two, each was down to ~155px on a phone. The
     column gap becomes the review gap once they are in a single stack. */
  .testimonials__grid{ grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .testimonials__col{ gap: 32px; }
  /* client-specified: centred down here, the same way the home page's review
     carousel centres. The stars row is a flex, so it needs its own rule. */
  .testimonial{ text-align:center; }
  .testimonial .reviews__rating{ justify-content:center; }

  /* client-specified: seasonal copy centred at tablet */
  .seasonal__inner{ grid-template-columns: 1fr; gap: 24px; }
  .seasonal__copy{ text-align:center; }
  .seasonal__copy .section__title{ max-width:100%; }
  .seasonal__media{ aspect-ratio: 16 / 10; }

  .features__grid{ grid-template-columns: 1fr; gap: 24px; }

  /* client-specified: reviews band centred, markers centred under it */
  .reviews__inner{ grid-template-columns: 1fr; gap: 24px; }
  .reviews__inner::before{ display:none; }
  .reviews__left, .reviews__right{ text-align:center; }
  .reviews__left .pill{ margin-inline:auto; }
  .reviews__rating{ justify-content:center; }
  .reviews__quote{ max-width:100%; }
  .reviews__dots{
    position:static; flex-direction:row; justify-content:center;
    gap:10px; margin-top:16px;
  }
  /* the bars go horizontal down here, so the arrows follow them */
  /* arrows return once the layout collapses — see the desktop rule */
  .carousel-controls .carousel-nav__btn{ display: inline-flex; }
  .carousel-controls{ margin-top: 16px; }
  /* .reviews .carousel-nav is gone: it dated from when the arrows were pinned
     to the band's right edge on desktop, and everything it set is now the
     default. Left in, its margin-top:14px beat .carousel-controls .carousel-nav
     on source order — same specificity, later in the file — and pushed the
     arrows 14px below the bars inside the row that is supposed to align them. */

  /* client-specified: 2 cards on top, the third full width underneath */
  .why__grid{ grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; }
  .why-card{ min-height:0; }
  .why-card:last-child{ grid-column: 1 / -1; }

  /* client-specified: gallery copy centred at tablet */
  /* client-specified: stacked, the button follows the photos. The heading and
     the button are one .gallery__copy cell in the markup, which is exactly what
     the desktop two-column grid needs — so rather than duplicating the button,
     the cell is dissolved with display:contents and its two children are
     ordered around the grid. Same trick the reviews box uses further down. */
  .gallery__inner{ grid-template-columns: 1fr; gap: 24px; }
  .gallery__copy{ display: contents; }
  .gallery__copy .section__title{ order: 1; text-align: center; }
  .gallery__grid{ order: 2; aspect-ratio: 940 / 683; }
  /* margin-top off: the grid's 24px gap is the spacing now, on both sides */
  .gallery__copy .btn{ order: 3; margin-top: 0; justify-self: center; }

  .areas__head .section__title, .areas__head .copy{ max-width:100%; }
  /* The Service Areas lead carries a designed desktop break; below 900 the
     column is too narrow for it to land anywhere useful, so it wraps freely.
     The Tips and Articles lead carries one for the same reason. */
  .areas__body br,
  .article-rows__lead br{ display:none; }

  /* client-specified: no forced line breaks in ANY heading below 900. Every
     <br> in a heading on this site is a DESKTOP break — measured against a wide
     column so the phrase lands where the design draws it. In a narrow column
     the same break falls mid-phrase and reads as a mistake ("Jupiter, FL Rodent
     / Control Company"). Suppressed rather than deleted, so desktop keeps the
     designed break. Covers all 12 across 8 files, including any added later. */
  h1 br, h2 br, h3 br, h4 br, h5 br, h6 br{ display:none; }
  .areas__grid{ grid-template-columns: repeat(2, minmax(0,1fr)); column-gap:2.5%; row-gap:12px; }

  /* Client-specified: the whole Contact section reads LEFT on a stacked
     layout — eyebrow, heading and the form's heading included. This reverses
     the earlier "centred at tablet" call; the accordion rows and the form
     labels were already left, so now nothing in the section is centred and
     everything hangs off one edge. */
  .contact{ padding-bottom: 40px; }                 /* client-specified */
  .contact__inner{ grid-template-columns: 1fr; row-gap: 28px; }
  .contact__faq{ text-align:left; }
  .contact__faq .section__title{ max-width:100%; }
  .contact__faq .pill{ margin-inline:0; }
  /* contact-us.php's details column follows the FAQ column's rule — same
     section, same layout, so it turns with it. The rules still run the full
     stacked width rather than the desktop 568/743. */
  .contact--details .contact__inner{ align-items:start; }
  .contact__details{ text-align:left; }
  .contact__details .pill{ margin-inline:0; }
  .ct-list{ max-width:100%; }
  .ct-item{ justify-content:flex-start; }
  .ct-social{ justify-content:flex-start; }
  .faq__q{ text-align:left; }
  .faq__a{ text-align:left; }                       /* client-specified */
  .form-card{ padding-inline: 24px; }
  .form-card__head{ text-align:left; }
  /* its <br> is dropped by the blanket heading rule above */

  .tips__head .section__title, .tips__head .copy{ max-width:100%; }
  .tips__inner{ grid-template-columns: 1fr; gap: 20px; }
  .tips__media{ aspect-ratio: 16 / 10; }
  .tips__list{ grid-template-rows:auto auto; }
  .tip-card{ min-height:0; padding: 24px; }

  /* client-specified: 40px top and bottom, equal. The copy's own 129px top
     offset exists only to line it up against the van in the two-column desktop
     layout — kept here it would sit on top of the 40px and read as ~100px. */
  .cta{ padding-block: 40px; }
  .cta__inner{ grid-template-columns: 1fr; gap: 24px; }
  .cta__media{ aspect-ratio: 811 / 405; }

  .footer__cols{ max-width:100%; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 28px 4%; }
  .footer__bottom{ max-width:100%; }
  .footer__bar{ justify-content:center; text-align:center; }
}

@media (max-width: 600px){
  .topbar__text{ font-size: 10px; }
  .navbar__phone .btn__label{ display:none; }
  .navbar__phone{ padding-inline: 10px; min-width:44px; }

  /* client-specified: 30 top and bottom on phones, wherever this band appears —
     home, Pine Beetle, Organic, No Tent. Its own padding is uncapped vw, so it
     had shrunk to 18/16 by 390 and the plate was crowding the heading. */
  .about{ padding-block: 30px; }
  /* With a flat padding the box should be the content, not a desktop measure:
     the fixed 508 clamps to a 320 floor down here, which would re-open the gap
     this rule just closed on any page whose band is short. */
  .about--fixed{ min-height: 0; }

  /* --- hero: the copy stays on the photo all the way down.
     It used to drop onto a cream band below a 16:9 photo strip, and the title
     kept its on-photo white — white on cream is 1.00:1, so the headline was
     literally invisible and only the gold <em> showed. Keeping the photo keeps
     the designed white/gold intact; the crop is anchored at 34% so the
     technician stays in frame instead of being zoomed past. --- */
  .hero__inner{ min-height: clamp(400px, 112vw, 470px); }

  /* client-specified: a swipeable strip on phones rather than the five-row,
     two-up grid it stacked into. Flex + overflow-x, so it needs no JS and no
     buttons — the row scrolls with a thumb and snaps. `grid-column` on the
     items is inert once the parent is a flex container, but nth-child(6) is
     reset anyway so nothing is left waiting to spring back.
     ~30% each shows three and a bit, which is what tells you it scrolls. */
  .pests__list{
    display:flex; flex-wrap:nowrap;
    overflow-x:auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    gap: 12px;
    /* no visible track: the strip is short and a scrollbar would sit on the
       labels. The thumb still works, it just is not painted. */
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .pests__list::-webkit-scrollbar{ display:none; }
  .pest{
    grid-column: auto;
    flex: 0 0 30%; scroll-snap-align: start;
  }
  .pest:nth-child(6){ grid-column: auto; }

  /* one column below tablet, so the third card needs no span of its own */
  .why__grid{ grid-template-columns: 1fr; }

  .cta-band__roach{ width: 24%; bottom: -8%; }
  /* client-specified: two lines on mobile, smaller type. The 70% cap was there
     to keep the roach off the copy, but with only two lines the title sits at
     the top of the panel and the roach is 90 below it — so the cap comes off
     and the size is solved instead. "Speak With a Jupiterpest.com" measures
     12.1x the font size in Impact, and the panel's content box is the viewport
     less 92 (page gutters + panel padding); 5.9vw stays inside that from 360
     up, so the line breaks after "Jupiterpest.com" on its own. */
  .cta-band__title{ max-width: 100%; font-size: clamp(20px, 5.9vw, 30px); }

  .hero__actions .btn,
  .cta__actions .btn{ width:100%; }

  .field-row{ grid-template-columns: 1fr; gap: 12px; }

  /* client-specified: one per row, pin + label centred as a pair */
  .areas__grid{ grid-template-columns: 1fr; }
  /* client-specified: locations read left, not centred. At one card per row the
     centring put every pin at a different x — the row is full width, so the
     pair floats to wherever that name happens to be wide. Left-aligned they
     line up down a single edge. flex-start rather than dropping the rule
     outright, so it stays explicit against the desktop default. */
  .area-card{ justify-content: flex-start; }
  /* client-specified: drop the 89.407% cap so the copy is as wide as the
     heading, and inset it by 10px only. */
  .areas__body{ max-width: 100%; padding-inline: 10px; }

  /* client-specified: CTA copy centred on mobile */
  .cta__copy{ text-align: center; }
  .cta__actions{ justify-content: center; }

  .gallery__grid{
    grid-template-columns: 1fr; grid-template-rows: auto auto auto;
    aspect-ratio: auto; row-gap: 12px;
  }
  .gallery__item{ aspect-ratio: 16 / 11; }
  .gallery__item--tall{ grid-column:1; grid-row:1; aspect-ratio: 460 / 683; }
  .gallery__item--top{ grid-column:1; grid-row:2; }
  .gallery__item--bottom{ grid-column:1; grid-row:3; }

  /* client-specified: the reviews box moves to the very end, after Service
     Areas. In the markup it sits inside the Contact column, which is where the
     design puts it on desktop — so rather than duplicating it, the column is
     dissolved with display:contents and the box is ordered last among the
     children that get promoted. `gap` has to go to 0 and the spacing come from
     margins: one gap value would otherwise also open up between the Contact
     column's own heading, list and socials. */
  .footer__cols{ display:flex; flex-direction:column; gap:0; }
  .footer__col:first-child{ display:contents; }
  .footer__col{ margin-bottom: 28px; }
  .footer__cols .footer__social{ margin-bottom: 28px; }
  .reviews-box{ order:9; margin-top:0; }

  .footer__bar{ flex-direction:column; }
}

