/* ============================================================
   cleanouts.css — the /cleanouts landing page

   Loaded AFTER v2.css and junk-removal.css, and only by /cleanouts/.
   Everything shared comes from those two: the colour tokens, the
   header, the footer, the quickbar and the type scale from v2.css;
   .shead, .btn, .shero, .bax, .hsteps, .faq, .jrevs and .cta from
   junk-removal.css, whose own header says it is deliberately generic
   and meant to be loaded by any service page.

   THIS FILE IS ONLY WHAT THE CLEANOUTS PAGE ADDS. If a rule here
   would be useful to a second page, it belongs in junk-removal.css
   instead — but move it deliberately, because that file is loaded by
   a page that currently ranks.

   ------------------------------------------------------------
   SECTION BACKGROUND RHYTHM — read this before changing any
   background on this page.

   The standing rule from junk-removal.css: NO TWO ADJACENT SECTIONS
   SHARE A BACKGROUND. That is what stops a fourteen-section page
   reading as one long scroll. The page is also a LIGHT page, which
   is a standing client instruction rather than a preference here.

     hero        dark photograph    .shero
     what it is  white              .ctrust
     three tracks  surface          .tracks
     included    white              .incl
     before/after  surface          .bax        (its own default)
     which one   white              .choose
     how it works  surface          .hsteps     (its own default)
     what it costs  white           .cost
     reviews     surface            .jrevs      (its own default)
     quote form  DARK               .qform      gradient + noise
     areas       white              .areas
     faq         surface            .faq        OVERRIDDEN, see below
     final cta   white              .cta        OVERRIDDEN, see below
     footer      dark               .sfoot

   Two of those are overrides and they are the only two: .faq ships
   white and .cta ships --surface in junk-removal.css, and on THIS
   page's order both would collide with the section above them. They
   are one line each and they are marked. Changing any other
   background means re-checking the whole column.

   ------------------------------------------------------------
   THE DARK-SECTION RULE. There is exactly one dark fill above the
   footer on this page — .qform — and it is a two-stop gradient with
   a .noise-overlay div on top at opacity .035. A FLAT dark fill is
   what the display pipeline (GPU driver, Windows colour management,
   panel firmware) spatial-dithers into a faint diagonal hairline
   running the full width of the band, at a slope of about 0.13. The
   gradient and the noise give the dither nothing to latch onto. The
   noise div is load-bearing markup, not decoration — do not drop it,
   and do not "simplify" the gradient to a solid.
   ============================================================ */


/* ============================================================
   PAGE TOKENS
   ============================================================ */
.ctrust,
.tracks,
.incl,
.choose,
.cost,
.qform,
.areas {
  /* Same rail and section padding as every junk-removal.css section,
     read from that file's :root rather than redeclared, so a change
     there moves this page too. */
  padding: var(--sec-pad) 20px;
}

.ctrust__inner,
.tracks__inner,
.incl__inner,
.choose__inner,
.cost__inner,
.qform__inner,
.areas__inner {
  max-width: var(--rail);
  margin: 0 auto;
}


/* ------------------------------------------------------------
   EYEBROW CONTRAST — a fix, not a restyle.

   junk-removal.css puts .shead__eyebrow on --accent-green #71B745
   and its own comment calls that "a live accessibility bug": the
   green measures 2.24:1 on --surface and 2.46:1 on white, and it is
   14-18px text, so it needs 4.5:1. That file already carries the
   value that fixes it — --after-ink #176928, which measures 6.20:1
   on --surface and 6.80:1 on white — and says so.

   This page uses it. The fix is scoped to this page rather than
   applied in junk-removal.css because that file is loaded by a page
   that ranks and a colour change there is the client's call, not a
   side effect of building a new page. Take it site-wide when
   somebody picks that bug up.

   ON THE DARK SECTION THE GREEN IS FINE and stays: #71B745 on
   #131B2E measures 7.4:1. --after-ink on the same ground would be
   2.2:1 and unreadable, so .qform explicitly opts back out. */
.ctrust .shead__eyebrow,
.tracks .shead__eyebrow,
.incl .shead__eyebrow,
.choose .shead__eyebrow,
.cost .shead__eyebrow,
.jrevs .shead__eyebrow,
.areas .shead__eyebrow,
.faq .shead__eyebrow,
.hsteps .shead__eyebrow,
.bax__eyebrow {
  color: var(--after-ink);
}
.qform .shead__eyebrow { color: var(--accent-green); }


/* ============================================================
   HERO ADDITIONS

   .shero itself, its photograph, its scrim, its title and its lede
   are all junk-removal.css's and are untouched. This page adds two
   things to the centred column: a locality eyebrow above the H1 and
   a row of proof chips below the buttons.
   ============================================================ */

/* THE SCRIM IS STRONGER HERE THAN junk-removal.css's, AND IT WAS
   MEASURED RATHER THAN GUESSED. That file's own note says "IF THE
   PHOTOGRAPH CHANGES, RE-MEASURE", so this is that measurement.

   Its ellipse — 78% x 62% at 50% 50%, falling to 2% at the edges —
   was tuned for gallery-12.jpg, a frame whose bright areas are sky
   ABOVE the type and a fence BEHIND a single subject. cta-cleanout.webp
   is a different problem: the bright pixels are distributed through
   the middle of the frame (a white mattress, pale cartons, a chrome
   shopping cart) exactly where the headline sits, and the crop lands
   on different parts of them at every aspect ratio.

   Measured with the repo's own method — record the copy's real boxes,
   hide the copy, screenshot the untouched background, take the p90
   luminance (the near-worst realistic backdrop, not the flattering
   mean) and compute the ratio against white. WCAG floors: 3.0 for the
   headline, which is large text, 4.5 for everything else.

   With junk-removal.css's scrim, unmodified:

                 1920   1440   1180    820    390    375   floor
     eyebrow     4.30   5.65   5.77   2.98   2.50   2.47    4.5
     headline    5.12   5.60   5.33   6.12   2.79   2.75    3.0
     lede        6.79   8.15   8.36   8.23   5.45   5.66    4.5
     chips       7.09  14.59  12.24  14.32  12.56  12.70    4.5

   Five failures, and the two on a phone are the ones that matter:
   the H1 itself was under its floor on the width most of this page's
   traffic arrives at.

   THE FIX IS A SECOND LAYER, NOT A BIGGER ELLIPSE. Widening the
   ellipse darkens the mid-zone between the centre and the edges,
   which is the exact thing junk-removal.css's note says made an
   earlier scrim read as a tint on the photograph. A soft vertical
   veil weighted to the top and bottom instead leaves the centre of
   the picture — the pile itself, the thing worth looking at — close
   to untouched, and puts the density where the eyebrow and the
   headline actually sit. The ellipse underneath is kept and only
   slightly deepened.

   After, same method, same boxes, p90 again:

                 1920   1440   1180    820    390    375   floor
     eyebrow     9.21  10.69  10.86   6.14   6.18   6.19    4.5
     headline    9.34   9.81   9.43   9.28   6.14   6.11    3.0
     lede       10.99  12.09  12.26  11.33   8.49   8.65    4.5
     chips      11.21  16.57  15.25  15.96  15.33  15.45    4.5

   Every cell clears its floor, and the phone column clears it by
   about 2x rather than by 4x — which is the point. The first attempt
   at the mobile veil bought 10.44 on the headline and turned the
   photograph into a dark texture; it was backed off until the pile,
   the cartons and the shopping cart read as objects again. Contrast
   you cannot see the picture through is not free.

   RE-MEASURE IF THE PHOTOGRAPH CHANGES. The script that produces the
   table above is the same method as tools/luma-v2.js. */
.shero__scrim {
  background:
    radial-gradient(ellipse 86% 70% at 50% 48%,
                    rgba(8, 14, 28, .70)  0%,
                    rgba(8, 14, 28, .40) 58%,
                    rgba(8, 14, 28, .04) 100%),
    linear-gradient(180deg,
                    rgba(8, 14, 28, .46)  0%,
                    rgba(8, 14, 28, .16) 38%,
                    rgba(8, 14, 28, .16) 62%,
                    rgba(8, 14, 28, .40) 100%);
}

/* A PHONE IS A DIFFERENT PHOTOGRAPH. At 375 the cover crop throws
   away most of the frame's width and lands the type on the pale
   cartons and the mattress, which is where the two failures above
   came from. The band is also short enough that the type fills it,
   so there is no "rest of the picture" left to protect — the veil
   can be flat and heavy without costing anything. */
@media (max-width: 700px) {
  .shero__scrim {
    background:
      radial-gradient(ellipse 120% 70% at 50% 46%,
                      rgba(8, 14, 28, .58)  0%,
                      rgba(8, 14, 28, .44) 60%,
                      rgba(8, 14, 28, .22) 100%),
      linear-gradient(180deg,
                      rgba(8, 14, 28, .40)  0%,
                      rgba(8, 14, 28, .20) 40%,
                      rgba(8, 14, 28, .20) 60%,
                      rgba(8, 14, 28, .40) 100%);
  }
}

/* Above the H1 rather than below it, so the first thing read on a
   page reached from a local search is the locality. Small, spaced
   and quiet — it is a label, not a line of copy. */
.shero__eyebrow {
  margin: 0 0 18px;
  font-size: clamp(13px, 1.1vw, 15px);
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  /* Not --accent-green: this sits at the top of the scrim ellipse
     where the photograph is at its brightest, and the green loses.
     White at 88% keeps it subordinate to the headline without
     dropping below the ratio the scrim was measured for. */
  color: rgba(255, 255, 255, .88);
}

/* THREE BUTTONS, NOT TWO, so wrapping is the normal case rather than
   the exception. junk-removal.css's .shero__actions already wraps
   and centres; this only tightens the gap so three pills fit on one
   line at 1024 and break to 2 + 1 rather than 1 + 1 + 1 at 768. */
.shero__actions { gap: 12px; }

.shero__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 22px;
  margin: clamp(26px, 3.4vh, 40px) 0 0;
  padding: 0;
  list-style: none;
}

.shero__chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: clamp(13px, 1vw, 15px);
  font-weight: 600;
  line-height: 1.3;
  /* Full white, not the 82% the lede uses. These sit lowest in the
     ellipse, where the scrim has already fallen off to almost
     nothing, and they are the smallest type on the photograph. */
  color: #FFFFFF;
  text-shadow: 0 1px 12px rgba(8, 14, 28, .55);
}

.shero__chip svg {
  width: 16px;
  height: 16px;
  flex: none;
  fill: var(--accent-green);
}


/* ============================================================
   .ctrust — "a cleanout is the whole space"

   A spacer with a job. It is the shortest section on the page and
   the most important 40 words on it: most people searching for a
   cleanout have not settled on cleanout vs junk removal vs dumpster,
   and the ones who guess wrong bounce. Deliberately typographic —
   no photograph, no cards, nothing to look at but the sentence.
   ============================================================ */
.ctrust {
  background: #FFFFFF;
  text-align: center;
}

.ctrust__inner { max-width: 940px; }

.ctrust__title {
  margin: 0;
  font-family: var(--font);
  font-weight: 300;
  font-size: clamp(26px, 3vw, 40px);
  line-height: 1.12;
  letter-spacing: -.04em;
  color: var(--head-ink);
  text-wrap: balance;
}

.ctrust__copy {
  margin: 22px 0 0;
  font-size: clamp(16px, 1.15vw, 18px);
  font-weight: 500;
  line-height: 1.65;
  color: var(--body-ink);
  text-wrap: pretty;
}

.ctrust__meta {
  margin: 26px 0 0;
  padding-top: 22px;
  border-top: 1px solid #E4E7EE;
  font-size: 15px;
  font-weight: 500;
  color: var(--body-ink);
}
.ctrust__meta strong { color: var(--head-ink); font-weight: 700; }


/* ============================================================
   .tracks — residential / estate / commercial

   ONE CLASSIFICATION, NOT TWO. /junk-removal-page classifies twice —
   a seventeen-slide photo rail of job types, then separate
   Residential and Commercial sections restating the split in prose.
   Here it happens once, on the axis people search on, and the
   specific job types hang off it as chips so every secondary keyword
   still lives in visible text.
   ============================================================ */
.tracks { background: var(--surface); }

.tracks__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
  margin-top: 46px;
}

.track {
  display: flex;
  flex-direction: column;
  padding: 22px 22px 28px;
  border: 1px solid #E4E7EE;
  border-radius: 20px;
  background: #FFFFFF;
}

/* THE PHOTO TRIO, AND THE CROP IS REMOVING A LABEL RATHER THAN
   FRAMING. Every haul-*.webp the client exported has its own blue
   "16. ESTATE CLEANOUTS INTERIOR" plate burned into the pixels along
   the bottom, over a strip of letterbox padding — the live site does
   not overlay its captions in CSS. Measured on haul-homes.webp
   (459x424) the plate occupies the bottom ~12.3%; 119% height
   anchored to the top drops the bottom ~16% outside the box, which
   clears it with margin. Same numbers as .clears__shot in
   junk-removal.css, and if that rule ever changes because the client
   supplies clean originals, this one changes with it.

   THREE SMALL SQUARES RATHER THAN ONE BIG ONE, and that is the
   resolution talking. These files are 459x424 — under half the
   pixels a 380px card wants on a 2x screen — so they are soft at
   card size and sharp at thumbnail size. */
.track__shots {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-bottom: 24px;
}

.track__shot {
  position: relative;
  display: block;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 12px;
  background: #1A2338;   /* holds the box while the photo decodes */
}

.track__shot img {
  width: 100%;
  height: 119%;
  object-fit: cover;
  object-position: 50% 0;
  display: block;
}

.track__name {
  margin: 0 0 12px;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(19px, 1.5vw, 22px);
  line-height: 1.2;
  letter-spacing: -.02em;
  color: var(--head-ink);
}

.track__copy {
  margin: 0 0 22px;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.62;
  color: var(--body-ink);
  text-wrap: pretty;
}

/* The chips are the keyword surface of this page and they are also
   the fastest way for a visitor to find their own job in the list.
   Not links: there are no per-type pages, and 24 chips pointing at
   #quote would be 24 identical destinations. */
.track__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: auto 0 0;
  padding: 22px 0 0;
  border-top: 1px solid #EDEFF4;
  list-style: none;
}

.track__chips li {
  padding: 6px 12px;
  border: 1px solid #DDE2EC;
  border-radius: 100px;
  background: var(--surface);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  /* 7.9:1 on --surface. The chips are the smallest type in the
     section and they carry the keywords, so they do not get to be
     decorative grey. */
  color: #3C4358;
}

.tracks__foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  margin-top: 40px;
}


/* ============================================================
   .incl — what's included / what we cannot take

   TWO PANELS, AND THE SECOND IS NOT AN AFTERTHOUGHT. A cleanout
   quote goes wrong when the crew arrives to half a garage of paint
   cans, so the exclusions sit beside the inclusions rather than
   three screens down in an FAQ answer.
   ============================================================ */
.incl { background: #FFFFFF; }

.incl__grid {
  display: grid;
  /* 1.15fr / 1fr rather than even halves: the "in the price" column
     carries nine items to the other's six plus two paragraphs, and
     even halves left it noticeably shorter. */
  grid-template-columns: 1.15fr 1fr;
  gap: 22px;
  margin-top: 44px;
}

.incl__panel {
  padding: 34px 32px;
  border-radius: 20px;
}

.incl__panel--yes {
  border: 1px solid #CFE3D6;
  background: #F4FAF6;
}

.incl__panel--no {
  border: 1px solid #E4E7EE;
  background: var(--surface);
}

.incl__head {
  margin: 0 0 22px;
  font-family: var(--font);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -.01em;
  color: var(--head-ink);
}

.incl__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.incl__list li {
  position: relative;
  padding: 0 0 0 30px;
  margin-bottom: 13px;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.55;
  color: var(--body-ink);
}
.incl__list li:last-child { margin-bottom: 0; }

/* CSS marks rather than inline SVG or an emoji. Two lists of nine
   and six would be 15 repeated <svg> blocks in the markup for a
   glyph that carries no information a screen reader needs — the
   heading above each list already says which list it is. */
.incl__list li::before {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}

.incl__panel--yes .incl__list li::before {
  content: '\2713';           /* check mark */
  background: #176928;
  color: #FFFFFF;
}

.incl__panel--no .incl__list li::before {
  content: '\00D7';           /* multiplication sign, i.e. a cross */
  background: #D7DCE6;
  color: #3C4358;
  font-size: 14px;
}

.incl__note {
  margin: 0 0 22px;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.6;
  color: #5A6076;
}
.incl__note--tail {
  margin: 22px 0 0;
  padding-top: 20px;
  border-top: 1px solid #DDE2EC;
}


/* ============================================================
   .choose — cleanout vs junk removal vs dumpster

   THE SECTION THAT EXISTS TO SEND PEOPLE AWAY. It stops the
   wrong-fit visitor bouncing, it is the internal link out to the two
   sibling service pages, and it is the clearest signal a crawler
   gets that this page is about something /junk-removal-page is not.
   Cutting it makes this page a paraphrase of one that already ranks.
   ============================================================ */
.choose { background: var(--surface); }

.choose__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
  margin-top: 46px;
  align-items: stretch;
}

.choose__card {
  display: flex;
  flex-direction: column;
  padding: 32px 30px;
  border: 1px solid #E4E7EE;
  border-radius: 20px;
  background: #FFFFFF;
}

/* The card the visitor is standing on gets the brand border and a
   tinted ground, so the comparison reads as "here is where you are,
   and here are the two alternatives" rather than as three equal
   options one of which happens to be this page. */
.choose__card--this {
  border-color: var(--blue);
  box-shadow: 0 0 0 1px var(--blue);
  background: #F7FAFF;
}

.choose__tag {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #7A8195;
}
.choose__card--this .choose__tag { color: var(--blue-line); }

.choose__name {
  margin: 0 0 16px;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(21px, 1.7vw, 25px);
  line-height: 1.15;
  letter-spacing: -.025em;
  color: var(--head-ink);
}

.choose__when {
  margin: 0 0 20px;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.62;
  color: var(--body-ink);
  text-wrap: pretty;
}
.choose__when strong { color: var(--head-ink); font-weight: 700; }

.choose__pts {
  margin: 0 0 26px;
  padding: 20px 0 0;
  border-top: 1px solid #EDEFF4;
  list-style: none;
}
.choose__pts li {
  position: relative;
  padding-left: 18px;
  margin-bottom: 10px;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.5;
  color: #5A6076;
}
.choose__pts li:last-child { margin-bottom: 0; }
.choose__pts li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-green);
}

/* Buttons sit on the bottom edge of every card regardless of how
   much copy is above them, so the three read as one row. */
.choose__card .btn {
  margin-top: auto;
  width: 100%;
}

.choose__note {
  max-width: 62rem;
  margin: 36px auto 0;
  text-align: center;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.65;
  color: var(--body-ink);
}
.choose__note a {
  color: var(--blue-line);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.choose__note a:hover { color: var(--blue-hover); }


/* ============================================================
   .cost — what drives the price

   NO FIGURES ANYWHERE IN THIS SECTION, deliberately. This site
   publishes no cleanout prices, and inventing a "from $XXX" here
   would be a fabricated business fact on the page whose whole job is
   to be trusted. What it gives instead is the three variables an
   estimator actually uses, so a visitor can place themselves before
   they call. /pricing carries whatever real numbers the client
   publishes and this links to it.
   ============================================================ */
.cost { background: #FFFFFF; }

.cost__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
  margin-top: 46px;
}

.cost__card {
  padding: 32px 30px;
  border: 1px solid #E4E7EE;
  border-radius: 20px;
  background: var(--surface);
}

.cost__n {
  display: block;
  margin-bottom: 16px;
  font-family: var(--font);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .12em;
  color: var(--after-ink);
}

.cost__name {
  margin: 0 0 14px;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(20px, 1.6vw, 24px);
  line-height: 1.15;
  letter-spacing: -.025em;
  color: var(--head-ink);
}

.cost__copy {
  margin: 0;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.62;
  color: var(--body-ink);
  text-wrap: pretty;
}

.cost__note {
  max-width: 62rem;
  margin: 36px auto 0;
  text-align: center;
  font-size: 15.5px;
  font-weight: 600;
  line-height: 1.65;
  color: var(--head-ink);
}
.cost__note a {
  color: var(--blue-line);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cost__note a:hover { color: var(--blue-hover); }


/* ------------------------------------------------------------
   .bax__label — a 12px floor instead of 11.

   junk-removal.css sets the Before/After labels to
   clamp(11px, 1vw, 13px), which resolves to 11px on anything under
   1100px wide — i.e. on every phone and tablet. "BEFORE" and "AFTER"
   are not decoration; they are the two words that make the whole
   comparison legible, and 11px is under the floor tools/cleanouts-qa.js
   enforces for readable type.

   Raising the bottom of the clamp rather than the whole thing: the
   ceiling stays 13px, so nothing changes above 1300px and the
   section looks identical on desktop. Page-local for the same reason
   the eyebrow fix is — junk-removal.css is loaded by a page that
   ranks, and a type change there is not this build's call. */
.bax__label { font-size: clamp(12px, 1vw, 13px); }


/* ============================================================
   .jrevs — review grid tuning

   The component is junk-removal.css's and until this page nothing
   used it. Two adjustments only: six cards instead of three wants
   two rows of three rather than one row of six, which the existing
   repeat(3, 1fr) already gives; and the source markup puts
   .jrevs__src inside .jrevs__name, so the name needs to stop being
   inline for the source line to sit under it.
   ============================================================ */
.jrevs__name { display: block; margin: 0; }


/* ============================================================
   .qform — the quote form

   THE PAGE'S CONVERSION FLOOR, and the only dark fill above the
   footer. Gradient plus the noise layer, per the dark-section rule
   at the top of this file.

   THE FORM IS ON THE PAGE. Every "Get A Free Quote" on this page is
   an in-page #quote jump rather than a link to /get-a-free-quote, so
   nobody is handed a navigation before they are handed a form. The
   ids are exactly the ones /assets/js/quote-form.js binds to; see
   the block comment above the markup.
   ============================================================ */
.qform {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Two stops, five luminance steps apart. It does not need to read
     as a gradient — it needs to not be flat. */
  background: linear-gradient(180deg, #131B2E 0%, #0F172A 100%);
  color: #FFFFFF;
  scroll-margin-top: 96px;   /* clears the sticky header on a #quote jump */
}

.qform__inner {
  position: relative;
  z-index: 2;
  display: grid;
  /* Copy narrower than the form. The form is the thing being used;
     the copy is the reason to use it. */
  grid-template-columns: 1fr 1.08fr;
  gap: clamp(32px, 4vw, 64px);
  align-items: start;
}

.qform__title {
  margin: 0;
  font-family: var(--font);
  font-weight: 300;
  font-size: clamp(28px, 3.2vw, 44px);
  line-height: 1.1;
  letter-spacing: -.04em;
  color: #FFFFFF;
  text-wrap: balance;
}

.qform__lede {
  margin: 20px 0 0;
  font-size: clamp(16px, 1.15vw, 18px);
  font-weight: 500;
  line-height: 1.65;
  color: rgba(255, 255, 255, .82);
  text-wrap: pretty;
}

.qform__pts {
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
}
.qform__pts li {
  position: relative;
  padding-left: 30px;
  margin-bottom: 13px;
  font-size: 15.5px;
  font-weight: 600;
  line-height: 1.5;
  color: #FFFFFF;
}
.qform__pts li::before {
  content: '\2713';
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent-green);
  color: #0B1226;
  font-size: 12px;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}

.qform__or {
  margin: 30px 0 0;
  padding-top: 26px;
  border-top: 1px solid rgba(255, 255, 255, .16);
  font-size: 15px;
  font-weight: 500;
  color: rgba(255, 255, 255, .72);
}
.qform__or span { margin: 0 6px; color: rgba(255, 255, 255, .4); }

.qform__tel {
  display: inline-block;
  /* 44px of tappable height on a phone even though this reads as a
     text link — it is one of the three actions the page exists for. */
  min-height: 44px;
  padding: 11px 0;
  color: #FFFFFF;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.qform__tel:hover { color: var(--accent-green); }

/* --- the card ------------------------------------------------ */
.qform__card {
  padding: clamp(26px, 2.6vw, 38px);
  border-radius: 22px;
  background: #FFFFFF;
  box-shadow: 0 24px 60px rgba(4, 8, 20, .34);
}

.qform__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.qform__field { margin-bottom: 16px; }
.qform__row .qform__field { margin-bottom: 16px; }

.qform__field label {
  display: block;
  margin-bottom: 7px;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--head-ink);
}
.qform__opt { font-weight: 500; color: #7A8195; }

.qform__field input,
.qform__field select,
.qform__field textarea {
  width: 100%;
  min-height: 48px;             /* clears the 44px touch minimum */
  padding: 12px 14px;
  border: 1px solid #D7DCE6;
  border-radius: 10px;
  background: #FFFFFF;
  font-family: var(--font);
  /* 16px AND NOT ONE PIXEL LESS. iOS Safari zooms the whole viewport
     when a focused input is under 16px, and it does not zoom back
     out. That is the single most common way a mobile form loses
     people. */
  font-size: 16px;
  font-weight: 500;
  color: var(--head-ink);
  transition: border-color .15s linear, box-shadow .15s linear;
  -webkit-appearance: none;
  appearance: none;
}

.qform__field textarea {
  min-height: 110px;
  line-height: 1.55;
  resize: vertical;
}

/* The native select arrow is stripped by appearance:none above, so
   it has to be drawn back. Inline SVG in the background rather than
   a file: it is 180 bytes and a request would not be. */
.qform__field select {
  padding-right: 40px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' fill='none' stroke='%234C5265' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}

.qform__field input::placeholder,
.qform__field textarea::placeholder { color: #9AA1B2; }

.qform__field input:focus,
.qform__field select:focus,
.qform__field textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(47, 111, 229, .18);
}
/* Keyboard users get a ring that does not depend on the brand blue
   being distinguishable from the resting border. */
.qform__field input:focus-visible,
.qform__field select:focus-visible,
.qform__field textarea:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
}

/* quote-form.js adds .err to a field it has rejected client-side,
   and moves focus there. The message in #formStatus says what is
   wrong; the border says which box. */
.qform__field input.err,
.qform__field select.err,
.qform__field textarea.err {
  border-color: #C4362F;
  box-shadow: 0 0 0 3px rgba(196, 54, 47, .14);
}

/* #formStatus is written by quote-form.js and starts empty. Collapsed
   to nothing until it has content, so it does not reserve a gap
   above the button on a form nobody has submitted yet. */
.qform__status {
  margin: 0;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--body-ink);
}
.qform__status:empty { display: none; }
.qform__status:not(:empty) { margin: 0 0 16px; }
.qform__status.is-ok  { color: #176928; }
.qform__status.is-err { color: #C4362F; }
.qform__status a { color: inherit; text-decoration: underline; }

.qform__submit {
  width: 100%;
  min-height: 54px;
  padding: 0 28px;
  border: 1px solid var(--blue);
  border-radius: 100px;
  background: var(--blue);
  color: #FFFFFF;
  font-family: var(--font);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .02em;
  cursor: pointer;
  transition: background-color .2s linear, border-color .2s linear;
}
.qform__submit:hover { background: var(--blue-hover); border-color: var(--blue-hover); }
.qform__submit:focus-visible { outline: 3px solid #0B1226; outline-offset: 3px; }
/* quote-form.js disables the button and swaps its value while the
   POST is in flight. */
.qform__submit:disabled { opacity: .62; cursor: default; }

.qform__small {
  margin: 14px 0 0;
  text-align: center;
  font-size: 13px;
  font-weight: 500;
  color: #7A8195;
}

.qform__noscript {
  margin: 18px 0 0;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.6;
  color: var(--body-ink);
}
.qform__noscript a { color: var(--blue-line); font-weight: 700; }


/* ============================================================
   .areas — service areas

   THE SPELLINGS HERE ARE CORRECT AND THE FOOTER'S ARE NOT, and that
   is deliberate. The footer is the live site's own markup carried
   over by the parity audit with its mistakes intact ("Belmark",
   "Bradly Beach", "Colt Neck", Matawan/Aberdeen twice, all 29 links
   pointing at "#"). Changing it is a site-wide decision and belongs
   to the client. But a service-area list is the one place on a local
   page where a misspelt town is actively expensive, because it is
   the text a crawler matches against the query — so this section
   carries the correct names and the counties they sit in.

   NOT LINKED. There are no town pages, and generating 29
   near-identical ones off this template is how a local site walks
   into Google's doorway-page policy.
   ============================================================ */
.areas { background: #FFFFFF; }

.areas__cols {
  display: grid;
  /* Weighted to the first column, which carries 19 of the 30 names. */
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 32px;
  margin-top: 44px;
}

.areas__head {
  margin: 0 0 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid #E4E7EE;
  font-family: var(--font);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--head-ink);
}

.areas__list {
  margin: 0;
  padding: 0;
  list-style: none;
  /* Two columns inside the wide first group, one inside the narrow
     ones — column-width does that on its own without a breakpoint
     per column. */
  columns: 160px auto;
  column-gap: 24px;
}

.areas__list li {
  break-inside: avoid;
  margin-bottom: 10px;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--body-ink);
}

.areas__note {
  max-width: 62rem;
  margin: 38px 0 0;
  padding-top: 26px;
  border-top: 1px solid #E4E7EE;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.65;
  color: var(--body-ink);
}
.areas__note a {
  color: var(--blue-line);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.areas__note a:hover { color: var(--blue-hover); }


/* ============================================================
   RHYTHM OVERRIDES — the only two on the page.

   Both are forced by THIS page's section order, not by anything
   wrong with the components. See the rhythm table at the top.
   ============================================================ */

/* .faq ships white in junk-removal.css. Here it follows .areas,
   which is white. */
.faq { background: var(--surface); }

/* .cta ships --surface. Here it follows .faq, which is now surface. */
.cta { background: #FFFFFF; }

/* .cta__panel inherits its ground from .cta on the junk page; state
   it explicitly so the panel does not go transparent over the new
   white and lose its edge against .cta__media. */
.cta__panel { background: #FFFFFF; }


/* ============================================================
   RESPONSIVE

   Checked at 375, 430, 768, 1024 and 1440. The rules below are in
   descending order so each one only has to undo the step above it.
   ============================================================ */

/* --- 1024: three-up grids go two-up, the form stacks ---------- */
@media (max-width: 1024px) {
  .tracks__grid,
  .choose__grid,
  .cost__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* An odd card left alone in a two-column grid reads as a mistake.
     The third one spans instead, which also gives the card that
     carries the most copy the most room. */
  .tracks__grid > :last-child:nth-child(odd),
  .choose__grid > :last-child:nth-child(odd),
  .cost__grid   > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
  /* A spanned .track would stretch three thumbnails across the full
     width and turn them into banners. Cap the strip instead. */
  .tracks__grid > :last-child:nth-child(odd) .track__shots {
    max-width: 420px;
  }

  .qform__inner { grid-template-columns: minmax(0, 1fr); }
  /* Copy above the form on a stacked layout: the reason to fill it
     in has to arrive before the fields do. */
  .qform__copy  { max-width: 62rem; }

  .incl__grid   { grid-template-columns: minmax(0, 1fr); }
  .areas__cols  { grid-template-columns: 1fr 1fr; gap: 28px 32px; }
}

/* --- 768: everything goes single column ---------------------- */
@media (max-width: 768px) {
  .tracks__grid,
  .choose__grid,
  .cost__grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .tracks__grid > :last-child:nth-child(odd),
  .choose__grid > :last-child:nth-child(odd),
  .cost__grid   > :last-child:nth-child(odd) {
    grid-column: auto;
  }
  .tracks__grid > :last-child:nth-child(odd) .track__shots { max-width: none; }

  .areas__cols { grid-template-columns: minmax(0, 1fr); gap: 30px; }
  .areas__list { columns: 150px auto; }

  /* Four chips on two lines rather than four cramped ones on one. */
  .shero__chips { gap: 12px 18px; }

  .incl__panel { padding: 28px 24px; }
  .choose__card,
  .cost__card  { padding: 28px 24px; }
}

/* --- 430 and 375: phone --------------------------------------- */
@media (max-width: 560px) {
  /* THE QUICKBAR SITS OVER THE BOTTOM OF THE PAGE. v2.js parks the
     two sticky pills above the footer, but between the hero and the
     footer they float over whatever is under them. The last section
     before the footer is .cta, so the page gets bottom padding
     rather than each section guessing. Measured against the pill
     row's height plus the safe-area inset on a notched phone. */
  body { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }

  .ctrust,
  .tracks,
  .incl,
  .choose,
  .cost,
  .qform,
  .areas {
    padding-left: 18px;
    padding-right: 18px;
  }

  /* THREE BUTTONS, FULL WIDTH, STACKED. At 375 three pills side by
     side are each ~105px, which is under the 44px-square target once
     the label is inside them, and the labels wrap to two lines. Full
     width is both bigger and easier to hit with a thumb. */
  .shero__actions { flex-direction: column; width: 100%; gap: 10px; }
  .shero__actions .btn { width: 100%; }

  .shero__chips {
    /* Ranged left in a two-column grid rather than centred and
       wrapped: centred chips of different lengths on four lines is a
       ragged block, and this is the last thing above the fold. */
    display: grid;
    grid-template-columns: 1fr 1fr;
    justify-content: stretch;
    gap: 12px 14px;
    text-align: left;
  }
  .shero__chip { font-size: 12.5px; }

  .tracks__foot { flex-direction: column; }
  .tracks__foot .btn { width: 100%; }

  /* One column of fields. Name and Phone side by side at 375 leaves
     each about 150px, which is not enough to see a typed phone
     number in. */
  .qform__row { grid-template-columns: minmax(0, 1fr); gap: 0; }

  .qform__card { padding: 22px 18px; border-radius: 18px; }

  /* "Rather not type? Call ... · Text us" wraps between the two links at
     this width and strands the separator at the end of the first line.
     Stack the pair instead and drop the dot, which only exists to
     separate them on one line. */
  .qform__or { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
  .qform__or span { display: none; }
  .qform__tel { padding: 9px 0; }

  .track { padding: 18px 18px 24px; }
  .track__shots { gap: 6px; }

  /* TWO COLUMNS, NOT ONE, and the reason is height rather than taste. The
     Monmouth group alone is 19 names; as a single column it runs about 630px,
     and the three groups together push a phone past 1000px of nothing but
     town names. Two columns halves that and still fits the longest name
     ("Atlantic Highlands", ~135px at this size) inside a ~170px column at
     375. The section stops being a wall and becomes a list you can scan. */
  .areas__list { columns: 2; column-gap: 16px; }
  .areas__list li { font-size: 15px; }

  .choose__card .btn { padding: 0 20px; }
}

/* A 320px viewport still exists on an SE in landscape-locked apps.
   Nothing here should ever produce a horizontal scrollbar, so the
   one element that can — the chip grid — drops to a single column. */
@media (max-width: 360px) {
  .shero__chips { grid-template-columns: minmax(0, 1fr); }
}


/* ============================================================
   MOTION

   Everything on this page that moves is a colour or shadow
   transition of 150-200ms, plus the .bax carousel's smooth scroll,
   which is v2.js's. Honour the system setting for all of it.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .qform__field input,
  .qform__field select,
  .qform__field textarea,
  .qform__submit {
    transition: none;
  }
}


/* ============================================================
   PRINT

   People print a quote page to take to an executor meeting or a
   landlord. Drop the chrome, keep the phone number visible, and stop
   the dark band burning a page of toner.
   ============================================================ */
@media print {
  .above-header,
  .site-header,
  .quickbar,
  .bax__nav,
  .shero__photo,
  .shero__scrim,
  .noise-overlay,
  .sfoot__noise { display: none !important; }

  .shero,
  .qform {
    background: #FFFFFF !important;
    color: #000000 !important;
  }
  .shero__title,
  .shero__eyebrow,
  .shero__chip,
  .qform__title,
  .qform__lede,
  .qform__pts li,
  .qform__tel { color: #000000 !important; }

  /* THE GHOST BUTTONS WOULD OTHERWISE PRINT BLANK. .btn--ghost-light and
     .cta__btn--ghost are white text inside a white-or-transparent border,
     because they were drawn to sit on a photograph and on a dark band. Both
     grounds have just been forced to white above, so without this the two
     hero call-to-actions and the closing "Text Us Now!" print as empty
     outlines — the phone number on a printed page disappears. */
  .btn--ghost-light,
  .cta__btn--ghost {
    color: #000000 !important;
    border-color: #000000 !important;
    background: transparent !important;
  }
  .cta__btn {
    color: #000000 !important;
    background: transparent !important;
    border: 1px solid #000000 !important;
  }

  .track,
  .choose__card,
  .cost__card,
  .incl__panel { break-inside: avoid; }

  /* A printed page has no tap targets, so every phone and email
     target has to survive as readable text. Scoped to tel:/mailto:
     so it does not print "(#quote)" after every in-page jump. */
  a[href^="tel:"]::after,
  a[href^="sms:"]::after,
  a[href^="mailto:"]::after {
    content: ' [' attr(href) ']';
    font-weight: 400;
  }
}
