:root {
  /* DJP Dumpsters brand palette. Both colors are sampled from real assets,
     not invented: navy #2D3891 off the logo file, orange #F26114 off his LIVE SITE (Ryan 2026-08-18: match the current site's blue+orange; his photos sample #F05A1E-#F86028 and his site uses #F26114 — we use the site's)
     of his own containers (#F86028 in full sun, #F04008 flat light, #B04030
     in shade). Navy logo on orange boxes — that's the fleet.

     NAVY is the primary (headers, dark bands, footer, headings) and ORANGE is
     the accent. Do not flip that: orange-dominant reads like a traffic cone.

     The orange needs TWO shades because the true container orange only hits
     3.39:1 against white — same trap as the Dumpster Fire build:
       --orange       #F05A1E  accents, rules, icons, big display type,
                               non-text fills. NEVER white body text on it.
       --orange-cta   #C8410E  filled CTAs with white labels (4.98:1), and
                               small orange text on white.
       --orange-hover #A8350A  hover for filled CTAs (6.61:1).

     Legacy --yellow* / --red* token names are kept so all 4,100 lines of
     donor CSS reskin from here in one edit. --yellow* = accent tier,
     --red* = action tier. */
  /* Accent scale. One brand orange in three shades plus a tint. These were
     six tokens under two colour names (--yellow and --red held the SAME
     #f26114; --yellow-deep and --red-deep the same #c8410e), which is how a
     palette drifts into two slightly different oranges: someone edits one name
     and not its twin. Role-named per the token test — a name that has to
     change to onboard a different client was named wrong, and "--yellow"
     holding an orange failed it outright. */
  --accent: #f26114;           /* brand orange off his live site. accents,
                                  rules, icons, display type. 3.4:1 on white,
                                  so never white text on it */
  --accent-cta: #c8410e;       /* AA-safe filled CTAs, white text 4.98:1 */
  --accent-cta-hover: #a8350a; /* hover for filled CTAs, white text 6.61:1 */
  --accent-tint: #fdeee4;      /* wash for active/selected states */
  --ok: #1e9e4a;          /* green — "accepted" checkmarks only */

  /* navy tier — the primary */
  --navy: #2d3891;        /* logo navy. white text on it 10.07:1 */
  --navy-deep: #1e2664;   /* headers, footer, dark bands */
  --navy-tint: #eef0fa;

  /* Dark-surface token (Ryan 2026-08-18: site must read BLUE+orange like his
     current site, not black+orange). #1A2150 is unmistakably navy: white on
     it 15.25:1, and the site orange #F26114 on it 4.72:1 — clears AA for
     normal text, so orange accents work on navy bands at body size. */
  --black: #1a2150;
  --ink: #14161c;
  --muted: #565b6b;
  --line: #e2e4ec;
  /* Interactive borders only. --line is 1.27:1 on white, fine for a decorative
     divider but failing WCAG 1.4.11 (3:1) for anything that IDENTIFIES a
     control: an input box, a size card, a date chip. 3.70:1 on white. */
  --line-control: #7c84a1;
  --logo-gray: #6d6e72;   /* the gray in the DJP wordmark */
  --bg: #ffffff;
  --bg-warm: #f4f5f8;     /* cool haze, sits under the navy */

  /* DJP pairing (2026-08-18, per local-service-design rotation rule — Inter is
     on the never-ship list and Oswald+X is the fleet's repeated default).
     Barlow Condensed: highway-signage/truck-plate provenance, which fits an
     owner who came out of trucking. Weights loaded: 500/600/700 display,
     Source Sans 3 400/600/700 body. */
  --font-display: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Source Sans 3", system-ui, sans-serif;

  /* Fleet button system (2026-08-17 ruling): action classes, role tokens.
     call = solid brand accent; book = solid dark; more = the only outline. */
  --btn-call-bg: #c8410e;      /* orange-cta tier: white text 4.98:1 */
  --btn-call-bg-hover: #a8350a;
  --btn-book-bg: #171d33;      /* dark navy-black: white text 16.67:1 */
  --btn-book-bg-hover: #2d3891;
  --btn-more-border: #2d3891;
  --btn-more-fg: #2d3891;
  --btn-focus: #2d3891;

  --max: 1280px;
  --pad: clamp(1.25rem, 4vw, 2.5rem);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  /* 18px, up from 17px (Ryan, 2026-08-21). Two reasons, and the second is the
     one that matters: 17px is small for the long prose on the town pages, and
     at 17px a 70ch measure came out only 591px — 46% of a 1280px block, with
     more empty space to its right than text. Headings and buttons are sized in
     rem off the ROOT, not em off body, so this raises body copy only and leaves
     the rest of the scale untouched. */
  font-size: 18px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  /* height:auto is NOT optional next to max-width. Without it the width shrinks
     to the container while the height stays pinned to the HTML height attribute,
     so the picture is squashed — measured on commercial-dumpster-rentals, a
     1600x1200 photo was rendering at 1195x1200 with object-fit:fill. It looked
     "a bit off" rather than obviously broken, which is why it survived.
     .hero-bg-media img and .size-card img set their own height and still win. */
  height: auto;
  display: block;
}
a {
  color: inherit;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  text-wrap: balance;
  margin: 0 0 0.5em;
  color: var(--black);
}

h1 {
  font-size: clamp(2.5rem, 6vw, 4.5rem);
  font-weight: 700;
}

.hero h1 {
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  line-height: 1.08;
  text-wrap: balance;
}

h2 {
  font-size: clamp(1.8rem, 3.5vw, 2.5rem);
}

h3 {
  font-size: clamp(1.2rem, 2vw, 1.4rem);
  letter-spacing: -0.015em;
}

p {
  margin: 0 0 1em;
}

.wrap {
  max-width: var(--max);
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

/* ===== Photo placeholder ===== */
.photo {
  background: var(--accent-tint);
  border: 2px dashed var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  border-radius: 4px;
}
.photo::before {
  content: "PHOTO";
  position: absolute;
  top: 12px;
  left: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  background: var(--black);
  color: var(--accent);
  padding: 4px 8px;
  border-radius: 2px;
}
.photo span {
  font-family: var(--font-body);
  color: #6a5a00;
  font-size: 0.85rem;
  max-width: 80%;
  text-align: center;
  line-height: 1.4;
  padding: 2rem 1rem 1rem;
}
.photo.ar-hero {
  aspect-ratio: 5 / 4;
}
.photo.ar-square {
  aspect-ratio: 1 / 1;
}
.photo.ar-wide {
  aspect-ratio: 16 / 9;
}
.photo.ar-tall {
  aspect-ratio: 4 / 5;
}
.photo.ar-card {
  aspect-ratio: 3 / 2;
}

/* When a real <img> is in the .photo container, hide placeholder styling */
.photo:has(img) {
  background: transparent;
  border: none;
}
.photo:has(img)::before {
  display: none;
}
.photo:has(img) span {
  display: none;
}
.photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ===== Nav ===== */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: #ffffff; /* white chrome (Ryan 2026-08-18, after trying navy and the
     orange livery): the navy logo was drawn for white and sits at 10:1 here.
     Orange is reserved for the phone CTA — the one loud element on the bar. */
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid #1f1f1f;
}

/* When users follow an #anchor link, scroll lands with breathing room
   above the section heading rather than slamming the section to the
   sticky topbar's edge. */
[id] {
  scroll-margin-top: 110px;
}
.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 92px;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  gap: 1.5rem;
}
.brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  letter-spacing: -0.02em;
  color: var(--black);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}
.brand-mark {
  height: 74px;
  width: auto;
  display: block;
  margin: 0.5rem 0;
  flex-shrink: 0;
}
.brand {
  flex-shrink: 0;
}
nav.primary {
  display: none;
  /* 1.15rem rather than 1.4: with six items the row has to share 1000px with the
     phone CTA and the logo, and the wider gap squeezed the logo at that width. */
  gap: 1.15rem;
  font-weight: 500;
  white-space: nowrap;
}
nav.primary a {
  color: #fff;
  text-decoration: none;
  font-size: 0.95rem;
  position: relative;
  padding-bottom: 4px;
  transition: color 180ms ease;
}
nav.primary a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1);
}
nav.primary a:hover {
  color: #fff;
}
nav.primary a:hover::after {
  transform: scaleX(1);
}
/* Current page: coral label + a persistent underline (same bar as the hover
   state, just locked open) so you can always tell where you are. */
nav.primary a[aria-current="page"] {
  color: #ff6a4d;
}
nav.primary a[aria-current="page"]::after {
  transform: scaleX(1);
  background: #ff6a4d;
}

.tel-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--accent);
  color: var(--black);
  text-decoration: none;
  padding: 0.7rem 1.15rem;
  border-radius: 3px;
  font-weight: 700;
  font-family: var(--font-display);
  font-size: 1.2rem;
  white-space: nowrap;
}
.tel-cta:hover {
  background: var(--accent-cta-hover);
}
.tel-cta::before {
  content: "";
  width: 14px;
  height: 14px;
  background: var(--black);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><path d='M6.6 10.8c1.4 2.8 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.4 21 3 13.6 3 4.5c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1l-2.3 1.7z'/></svg>")
    center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.6 10.8c1.4 2.8 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.4 21 3 13.6 3 4.5c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1l-2.3 1.7z'/></svg>")
    center/contain no-repeat;
}

/* Full horizontal nav appears at 1000px — covers iPad landscape (1024).
   Six items + phone CTA + logo fit comfortably at that width. */
@media (min-width: 1000px) {
  nav.primary {
    display: inline-flex;
  }
}

/* ===== Nav layout: phone on the LEFT, logo on the RIGHT (order-only swap) =====
   The two side zones share equal flex so the center nav stays optically
   centered even though the left cluster (hours + phone) is wider than the
   logo. flex-basis:0 + grow:1 gives each side (container - nav) / 2. */
@media (min-width: 1000px) {
  .topbar-inner > .brand { order: 1; flex: 1 1 0; }
  .topbar-inner > nav.primary { order: 2; flex: 0 0 auto; }
  .topbar-inner > div { order: 3; flex: 1 1 0; display: flex; justify-content: flex-end; }
}
/* Mobile put the phone first and the logo last, so with the header's duplicate
   phone button removed the wrapper div was left empty but still holding
   margin-right:auto -- which shoved the logo across to sit against the
   hamburger. Brand leads now and takes the auto margin itself: logo left,
   hamburger right, which is where a phone visitor looks for each. */
@media (max-width: 999px) {
  .topbar-inner > div:not(.brand) { display: none; }
  .topbar-inner > .brand { order: 1; margin-right: auto; }
}

/* ===== Mobile hamburger menu ===== */
.nav-toggle-cb {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.nav-toggle {
  display: none;
}
@media (max-width: 999px) {
  /* Push brand to the left, group call + hamburger on the right */
  .topbar-inner {
    justify-content: flex-start;
    gap: 0.5rem;
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    min-height: 76px;
  }
  .brand {
    margin-right: auto;
  }
  .brand-mark {
    height: 40px;
  }
  .tel-cta {
    padding: 0.5rem 0.7rem;
    font-size: 1rem;
    gap: 0.35rem;
  }
  .nav-toggle {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    cursor: pointer;
    background: transparent;
    border: 2px solid #fff;
    border-radius: 4px;
    flex-shrink: 0;
    order: 10;
  }
  .nav-toggle span {
    display: block;
    width: 22px;
    height: 2px;
    background: #fff;
    border-radius: 2px;
    transition:
      transform 180ms ease,
      opacity 180ms ease;
  }
  .topbar:has(.nav-toggle-cb:checked) .nav-toggle span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
  }
  .topbar:has(.nav-toggle-cb:checked) .nav-toggle span:nth-child(2) {
    opacity: 0;
  }
  .topbar:has(.nav-toggle-cb:checked) .nav-toggle span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
  }
  .topbar:has(.nav-toggle-cb:checked) nav.primary {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--black);
    padding: 0.5rem 1.5rem;
    border-top: 2px solid #1f1f1f;
    border-bottom: 2px solid var(--accent);
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4);
    z-index: 49;
    gap: 0;
  }
  .topbar:has(.nav-toggle-cb:checked) nav.primary a {
    padding: 0.95rem 0;
    font-size: 1.05rem;
    font-weight: 600;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }
  .topbar:has(.nav-toggle-cb:checked) nav.primary a:last-child {
    border-bottom: none;
  }
}

/* ===== Scroll-morphing topbar (mobile only) — slide transition ===== */
.topbar-inner {
  position: relative;
  overflow: hidden;
}
/* When hamburger nav is open, allow overflow so the drawer can drop below */
.topbar:has(.nav-toggle-cb:checked) .topbar-inner {
  overflow: visible;
}
/* The scroll-morphing topbar (brand swapped for Call + Text Us past 60px) is
   gone. It pinned a second phone CTA to the screen while the floating pill
   already held one, so a scrolled phone carried two identical tel: links and
   134px of permanent chrome -- 16.4% of a 375x812 viewport. It was also a
   full-width bar carrying Call AND Text, which is the pattern the pill replaced,
   and its container was aria-hidden="true" around two still-focusable links, so
   keyboard and screen-reader users tabbed into controls that were never
   announced (WCAG 4.1.2). Dropping it also gives the brand mark back as the
   tap-to-go-home affordance, which the swap removed exactly when a visitor was
   deepest in the page. The pill is now the single persistent action. */

/* ===== Hero ===== */
.hero {
  padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(3rem, 7vw, 6rem);
  border-bottom: 1px solid var(--line);
}
.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: center;
}
@media (min-width: 900px) {
  .hero-grid {
    grid-template-columns: 1.1fr 1fr;
    gap: clamp(2rem, 5vw, 4rem);
  }
}

.hero p.lead {
  font-size: clamp(1.1rem, 1.8vw, 1.3rem);
  color: var(--muted);
  max-width: 32em;
  margin-bottom: 2rem;
  line-height: 1.5;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 16px;
  align-items: center;
  margin-bottom: 1.25rem;
}
@media (max-width: 640px) {
  .cta-row { flex-direction: column; align-items: stretch; }
  .cta-row .btn { width: 100%; min-height: 48px; justify-content: center; }
}
.btn {
  display: inline-flex;
  align-items: center;
  /* justify-content is NOT optional here: .btn is a flex container and mobile
     rules stretch it to width:100%. Without it every stretched label slides to
     the left edge (local-service-design). */
  justify-content: center;
  /* An explicit colour is NOT optional either: a <button> with no colour falls
     back to the system link blue on iOS while desktop Chrome shows black, so
     the defect is invisible on the machine it is built on. */
  color: var(--black);
  gap: 0.5rem;
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  padding: 0.85rem 1.5rem;
  border-radius: 6px;
  border: 2px solid var(--black);
  white-space: nowrap;
  box-shadow: 4px 4px 0 var(--black);
  transition:
    transform 120ms ease,
    box-shadow 120ms ease,
    background 120ms ease;
}
.btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--black);
}
.btn:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--black);
}

.tel-big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.3vw, 1.65rem);
  color: var(--black);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--accent);
  padding: 0.85rem 1.5rem;
  border-radius: 6px;
  border: 2px solid var(--black);
  margin-bottom: 0.75rem;
  box-shadow: 4px 4px 0 var(--black);
  transition:
    transform 120ms ease,
    box-shadow 120ms ease,
    background 120ms ease;
}
.tel-big:hover {
  background: var(--accent-cta-hover);
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--black);
}
.tel-big:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--black);
}
.tel-big::before {
  content: "";
  width: 1.1em;
  height: 1.1em;
  background: var(--black);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.6 10.8c1.4 2.8 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.4 21 3 13.6 3 4.5c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1l-2.3 1.7z'/></svg>")
    center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.6 10.8c1.4 2.8 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.4 21 3 13.6 3 4.5c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1l-2.3 1.7z'/></svg>")
    center/contain no-repeat;
  flex-shrink: 0;
}

.text-big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.3vw, 1.65rem);
  color: var(--black);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--accent);
  padding: 0.85rem 1.5rem;
  border-radius: 6px;
  border: 2px solid var(--black);
  margin-bottom: 0.75rem;
  margin-left: 0.75rem;
  box-shadow: 4px 4px 0 var(--black);
  transition:
    transform 120ms ease,
    box-shadow 120ms ease,
    background 120ms ease;
}
.text-big:hover {
  background: var(--accent-cta-hover);
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--black);
}
.text-big:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--black);
}
.text-big::before {
  content: "";
  width: 1.1em;
  height: 1.1em;
  background: var(--black);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z'/></svg>")
    center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z'/></svg>")
    center/contain no-repeat;
  flex-shrink: 0;
}
@media (max-width: 500px) {
  .text-big {
    margin-left: 0;
    margin-top: 0.5rem;
  }
}

/* ===== Section base ===== */
section.block {
  padding: clamp(3.5rem, 8vw, 6rem) 0;
}
section.block.alt {
  background: var(--bg-warm);
}
@media (min-width: 800px) {
  .services-split {
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
  }
}
.svc-card {
  position: relative;
  background: var(--black);
  color: #fff;
  border-radius: 4px;
  overflow: hidden;
  min-height: 380px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.svc-card .photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 0;
  border-color: rgba(245, 184, 0, 0.6);
}
.svc-card .photo::before {
  background: var(--accent);
  color: var(--black);
}
.svc-card .svc-body {
  position: relative;
  z-index: 1;
  padding: 2rem;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.92) 30%,
    rgba(0, 0, 0, 0.55) 75%,
    transparent
  );
}
.svc-card h3 {
  font-size: 1.8rem;
  color: #fff;
  margin-bottom: 0.5rem;
}
.svc-card p {
  color: rgba(255, 255, 255, 0.85);
  margin-bottom: 1rem;
  font-size: 0.95rem;
  line-height: 1.5;
}
.svc-card a.svc-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--accent-cta);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
}
.svc-card a.svc-link:hover {
  color: #fff;
}

/* ===== Sizes row ===== */
/* minmax(0,1fr) not 1fr: a bare 1fr floors at the track's MIN-CONTENT, and the
   card's photo + padding make that ~217px. Two of those plus the gap is 450px,
   so at 375px the grid overflowed the viewport and the whole page scrolled
   sideways. Present since the initial build; caught 2026-08-21. Below 560px
   the cards go single-file, which is what they want on a phone anyway. */
.sizes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  margin-top: 2rem;
}
@media (max-width: 560px) {
  .sizes { grid-template-columns: 1fr; }
}
@media (min-width: 700px) {
  .sizes {
    /* 2x2, not 4-across (WOW 2026-08-10): four-up starves the photos */
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.size-card {
  border: 1px solid var(--line);
  padding: 1.5rem 1.25rem;
  background: #fff;
  border-radius: 4px;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s;
}
.size-card:hover {
  border-color: var(--accent);
}
.size-card .yd {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  letter-spacing: 0.2px;
  color: var(--black);
  line-height: 1.05;
  display: block;
}
.size-card .yd small {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--muted);
}
.size-card p {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--muted);
  line-height: 1.4;
}

/* ===== Story strip ===== */
.story-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: center;
}
@media (min-width: 900px) {
  .story-grid {
    grid-template-columns: 0.9fr 1.1fr;
    gap: clamp(2.5rem, 5vw, 4rem);
  }
}
.story-grid .photo.ar-tall {
  max-height: 540px;
}

/* ===== Reviews ===== */
/* Homepage strip: a curated, hand-picked "best of" showcase, frozen and
   undated on purpose -- scripts/fetch-reviews.mjs never writes index.html,
   so this list only changes by hand (Ryan, 2026-08-10: curated means
   undated; a hand-picked set must never silently refresh out from under a
   human's choice). It scrolls horizontally at every width, the way a real
   review widget does: one card and a peek of the next on a phone, three on
   desktop, the rest a swipe away.
   flex (not grid-auto-flow: column) on purpose: grid-auto-flow inside
   overflow-x: auto collapses to a single vertical column in iOS Safari.
   flex-shrink: 0 is what keeps cards from being squashed to fit the row. */
.reviews {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.reviews::-webkit-scrollbar {
  display: none;
}
.reviews > * {
  flex: 0 0 84%;
  flex-shrink: 0;
  scroll-snap-align: start;
}
@media (min-width: 800px) {
  .reviews > * {
    flex-basis: calc((100% - 2.5rem) / 3);
  }
}
/* JS-injected swipe indicator (built by the inline script at the foot of
   index.html, NOT by any file in assets/js): a little left-right bar that
   tracks the strip, visible on iOS where a styled native scrollbar never
   renders. Kept at top level, not inside a mobile query -- the review strip
   scrolls at every width. */
.hscroll-track {
  height: 5px;
  background: var(--line);
  border-radius: 3px;
  /* margin-bottom was 0 because this rule was written for a track sitting
     BELOW a carousel. The review strip puts it above the cards, so it needs
     real breathing room underneath or it crowds the first card. */
  margin: 0.75rem auto 1.4rem;
  max-width: 11rem;
  position: relative;
  overflow: hidden;
}

/* Review cards, styled to read as a real Google widget: initial avatar,
   name + date, the Google mark, gold stars over the quote. Gold (not brand
   red) is deliberate -- it's the strongest cue the rating is real Google
   data rather than a testimonial written in-house. Bordered + hard-shadow
   to match the site's chunky card system (DESIGN POLISH PASS, below). */
.review {
  background: #fff;
  border: 2px solid var(--black);
  border-radius: 8px;
  box-shadow: 5px 5px 0 var(--black);
  padding: 1.4rem 1.35rem;
  display: flex;
  flex-direction: column;
}
.rv-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.7rem;
}
/* Initial disc, cycled across the site's own 3 dark tokens only -- never a
   color outside the brand palette. White text clears AA against all three:
   --black and --red-deep are comfortably past 4.5:1; --ok lands near 3.5:1,
   which needs the "large text" 3:1 floor -- hence 1.2rem/700 here (body
   text is 17px, so 1.2rem = 20.4px, past the 18.66px-bold AA threshold). */
.rv-avatar {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: var(--black);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1;
}
/* nth-of-type, not nth-child: the cards are <article> and the reviews page now
   drops a <div> CTA into the middle of the same grid. nth-child counts every
   sibling, so that div would have shifted the three-colour avatar rotation for
   every card after it. nth-of-type counts only the articles. */
.reviews .review:nth-of-type(3n + 2) .rv-avatar,
.reviews-grid .review:nth-of-type(3n + 2) .rv-avatar {
  background: var(--accent-cta);
}
.reviews .review:nth-of-type(3n) .rv-avatar,
.reviews-grid .review:nth-of-type(3n) .rv-avatar {
  background: var(--ok);
}
.rv-who {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}
.rv-name {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--ink);
  font-size: 0.95rem;
}
.rv-date {
  font-size: 0.8rem;
  color: var(--muted);
}
.rv-g {
  margin-left: auto;
  flex-shrink: 0;
}
/* Google's own star gold. At ~1.7:1 on white it can't carry meaning alone,
   so the row carries an aria-label stating the rating in words rather than
   depending on the glyphs. */
.rv-stars {
  margin: 0 0 0.6rem;
  color: #fbbc04;
  font-size: 1rem;
  letter-spacing: 0.08em;
  line-height: 1;
}
.review blockquote {
  margin: 0;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink);
}
@media (min-width: 900px) {
  .area-grid {
    grid-template-columns: 0.85fr 1.15fr;
    gap: clamp(2.5rem, 5vw, 4rem);
  }
}
@media (min-width: 700px) {
  .town-list {
    columns: 3;
  }
}

/* ===== Final CTA ===== */
.cta-final {
  background: var(--black);
  color: #fff;
  text-align: center;
}
.cta-final h2 {
  color: #fff;
  max-width: 18em;
  margin-left: auto;
  margin-right: auto;
}
.cta-final h2 em {
  color: var(--accent);
  font-style: normal;
}
.cta-final p {
  color: rgba(255, 255, 255, 0.75);
  max-width: 34em;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 2rem;
  font-size: 1.1rem;
}
.cta-final .cta-row {
  justify-content: center;
}
.cta-final .tel-big,
.cta-final .text-big,
.cta-final .btn {
  border-color: var(--accent);
  box-shadow: 4px 4px 0 var(--accent);
}
.cta-final .tel-big:hover,
.cta-final .text-big:hover,
.cta-final .btn:hover {
  box-shadow: 6px 6px 0 var(--accent);
}
.cta-final .tel-big:active,
.cta-final .text-big:active,
.cta-final .btn:active {
  box-shadow: 0 0 0 var(--accent);
}
.cta-final .btn-primary {
  background: var(--accent);
  color: var(--black);
}
.cta-final .btn-primary:hover {
  background: var(--accent-cta-hover);
}
.cta-final .btn-secondary {
  background: transparent;
  color: var(--accent-cta);
}
.cta-final .btn-secondary:hover {
  background: var(--accent);
  color: var(--black);
}

/* ===== Footer ===== */
footer.site {
  background: #0a0a0a;
  color: rgba(255, 255, 255, 0.7);
  padding: 4rem 0 2rem;
  font-size: 0.9rem;
}
footer.site .foot-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
  margin-bottom: 3rem;
}
@media (min-width: 640px) {
  footer.site .foot-grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (min-width: 960px) {
  footer.site .foot-grid {
    grid-template-columns: 1.4fr 1fr 1fr 1fr;
  }
}
footer.site h4 {
  color: #fff;
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 1rem;
}
footer.site a {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
}
footer.site a:hover {
  color: var(--accent);
}
footer.site ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
footer.site ul li {
  margin-bottom: 0.5rem;
  line-height: 1.5;
}
footer.site .foot-brand {
  display: block;
  height: 110px;
  width: auto;
  margin-bottom: 0.85rem;
}
footer.site .nap p {
  margin-bottom: 0.5rem;
  color: rgba(255, 255, 255, 0.75);
}
footer.site .nap strong {
  color: #fff;
  font-weight: 600;
}
footer.site .legal {
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding-top: 1.5rem;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.5);
}

/* ===== Floating sticky Call button (mobile + desktop) ===== */
.mobile-cta {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  left: auto;
  z-index: 60;
  display: flex;
  gap: 0;
  background: transparent;
  border-top: none;
  padding: 0;
}
/* The donor's a.call / a.book pill rules lived here for the whole build,
   targeting classes this site's markup never carried. Dead weight, but armed:
   they held the stylesheet's only soft blur shadow and only 50px radius, so
   any future markup that copied the donor's class names would have restyled
   the pill against both house rules at once. Deleted 2026-08-28; the real
   pill is styled on .mobile-cta .btn further down. */

/* Add bottom padding on mobile so sticky CTA doesn't cover content */
@media (max-width: 899px) {
  footer.site {
    padding-bottom: 6rem;
  }
}

/* ===== Sub-page primitives ===== */

.crumb {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}
.crumb a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px dotted transparent;
}
.crumb a:hover {
  color: var(--black);
  border-bottom-color: var(--accent);
}
.crumb .sep {
  opacity: 0.4;
  margin: 0 0.35rem;
}
.crumb .current {
  color: var(--black);
}

.page-hero {
  padding: clamp(2.5rem, 6vw, 4.5rem) 0 clamp(2rem, 5vw, 3.5rem);
  border-bottom: 1px solid var(--line);
}
.page-hero h1 {
  font-size: clamp(2rem, 4.5vw, 3.5rem);
  max-width: 18em;
}
.page-hero .lead {
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  color: var(--muted);
  max-width: 36em;
  line-height: 1.55;
  margin-top: 1rem;
}

/* Compact chooser, used where full detail sections follow on the same page.
   With photos the three cards ran 2,124px on a phone, and the sections below
   repeat the very same three images at a larger size -- so every dumpster was
   shown twice before the visitor had decided anything. Three near-identical
   black boxes also discriminate nothing in a comparison; the prices and
   capacities do that work. Dropping the photo halves the chooser and lets the
   detail sections keep the photography they were built around. */
.sizes-compare .size-card img { display: none; }
.sizes-compare .size-card { padding-top: 1.7rem; }
.sizes-compare .size-card .yd { margin-top: 0; }

.page-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: start;
}
@media (min-width: 900px) {
  .page-grid {
    grid-template-columns: 1.1fr 1fr;
    gap: clamp(2rem, 5vw, 4rem);
  }
}
@media (min-width: 900px) {
  .page-grid .photo {
    min-height: 320px;
  }
}
@media (min-width: 700px) {
  .spec-grid {
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  }
}
.spec {
  background: #fff;
  border: 1px solid var(--line);
  padding: 1.1rem 1.25rem;
  border-radius: 4px;
}
.spec .label {
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-bottom: 0.35rem;
}
.spec .val {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  color: var(--black);
  line-height: 1.05;
}

.prose p {
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--ink);
}
.prose p + p {
  margin-top: 1em;
}
/* h3 carried a margin-top and h2 carried none, so a 42.5px heading sat 18px
   below the paragraph above it and 21px above its own — closer to the text it
   followed than the text it introduced. Blog posts and guides stack up to
   seven h2s in one .prose block, so the inversion ran their whole length.
   Space above a heading should beat space below by roughly 2:1.
   :not(:first-child) leaves the section's own padding alone. */
.prose h2:not(:first-child) {
  margin-top: 3.25rem;
}
.prose h3 {
  margin-top: 2rem;
}
.prose ul {
  padding-left: 1.25rem;
  line-height: 1.7;
}
.prose ul li {
  margin-bottom: 0.35rem;
}
.prose a {
  color: var(--black);
  border-bottom: 2px solid var(--accent);
  text-decoration: none;
}
.prose a:hover {
  color: var(--accent-cta);
}
/* Buttons inside prose keep button styling, not link styling */
.prose a.btn {
  border-bottom: 0;
}
.prose a.btn-primary {
  color: #fff;
}
.prose a.btn-primary:hover {
  color: #fff;
}
@media (min-width: 700px) {
  .related-sizes {
    grid-template-columns: repeat(4, 1fr);
  }
}
.related-sizes a.current {
  border-color: var(--black);
  background: #faf8f2;
}

/* Per-size "see details" link on the dumpsters size cards */
.size-card .size-more {
  display: inline-block;
  margin-top: 0.85rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--accent-cta);
  text-decoration: none;
}
.size-card .size-more:hover {
  text-decoration: underline;
}

/* ===== Reviews: summary badge + card grid (dedicated reviews page) ===== */
/* Reviews trust badge: [stars / EXCELLENT] | [G  5.0 / Rated on Google] */
.reviews-summary {
  width: auto;
  margin: 0 auto;
  display: inline-flex;
  align-items: stretch;
  justify-content: center;
  gap: 1.4rem;
  background: #fff;
  border: 2px solid var(--black);
  border-radius: 10px;
  padding: 1rem 1.85rem;
  box-shadow: 6px 6px 0 var(--black);
}
.rs-rating {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}
.reviews-summary-stars {
  margin: 0;
  font-size: 1.7rem;
  line-height: 1;
  color: var(--accent);
  letter-spacing: 0.12em;
  text-shadow:
    -1px -1px 0 var(--black),
    1px -1px 0 var(--black),
    -1px 1px 0 var(--black),
    1px 1px 0 var(--black);
}
.reviews-summary-label {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--black);
  line-height: 1;
}
.rs-source {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding-left: 1.4rem;
  border-left: 2px solid var(--line);
}
.rs-source svg {
  flex-shrink: 0;
}
.rs-source-text {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}
.rs-source-text .rb-text {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}
.rs-source-text .rb-text strong {
  margin-bottom: 0.1rem;
}
.rs-source-text strong {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--black);
}
.rs-source-text span {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
/* Dedicated reviews-page grid: the same .review cards as the homepage
   strip, laid out as a grid instead of a horizontal scroller. Natural
   height (no stretch/align-items:stretch) so uneven review lengths read as
   real rather than padded to match. */
.reviews-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.25rem;
  margin-top: 2.25rem;
  align-items: start;
}
/* Reviews-page display cap (Ryan, 2026-08-10, tightened from 6): 3 cards on
   mobile, 12 on desktop by default; scripts/fetch-reviews.mjs assigns these
   classes to cards past that count when it (re)generates the grid, and the
   show-more script below removes them in matching batches. display:none
   only -- every rendered card stays in the HTML source, so it's still
   crawlable, and the render cap itself (currently 30 cards, not the full
   feed) is enforced in the generator, not here. */
@media (max-width: 640px) {
  .review.is-capped-mobile {
    display: none;
  }
}
.review.is-capped {
  display: none;
}
.reviews-show-more {
  min-height: 48px;
  justify-content: center;
}
@media (max-width: 640px) {
  .reviews-show-more {
    width: 100%;
  }
}
@media (max-width: 460px) {
  /* Stack the two halves so the badge can't overflow a narrow phone (it was
     a fixed ~379px wide and pushed past a 375px screen, causing side-scroll). */
  .reviews-summary {
    flex-direction: column;
    align-items: stretch;
    gap: 0.7rem;
    padding: 0.85rem 1.25rem;
    max-width: 100%;
    box-sizing: border-box;
  }
  .rs-rating {
    text-align: center;
  }
  .reviews-summary-stars {
    font-size: 1.5rem;
  }
  .rs-source {
    padding: 0.7rem 0 0;
    border-left: none;
    border-top: 2px solid var(--line);
    justify-content: center;
  }
  .rs-source-text strong {
    font-size: 1.3rem;
  }
}
.region-towns {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem 1.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (min-width: 700px) {
  .region-towns {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (min-width: 1000px) {
  .region-towns {
    grid-template-columns: repeat(4, 1fr);
  }
}
.region-towns li a {
  color: var(--ink);
  text-decoration: none;
  font-size: 0.95rem;
  border-bottom: 1px dotted transparent;
}
.region-towns li a:hover {
  border-bottom-color: var(--accent);
}
.region-towns .hq a {
  font-weight: 700;
}
.region-towns .hq a::after {
  content: " · HQ";
  color: var(--muted);
  font-weight: 400;
  font-size: 0.85rem;
}
@media (min-width: 800px) {
  .items-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Contact NAP card */
.nap-card {
  background: var(--bg-warm);
  border: 1px solid var(--line);
  padding: 2rem;
  border-radius: 4px;
}
.nap-card .label {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-bottom: 0.25rem;
}
.nap-card .val {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--black);
  margin-bottom: 1.25rem;
  display: block;
}
.nap-card .val a {
  color: var(--black);
  text-decoration: none;
  border-bottom: 2px solid var(--accent);
}

/* ===== Town search ===== */
.town-search {
  margin: 0 0 3rem;
  max-width: 640px;
  position: relative;
}
.town-search label {
  display: block;
  font-family: var(--font-display) !important;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--black);
  margin: 0 0 0.65rem;
}
.town-search-input-wrap {
  position: relative;
  background: #fff;
  border: 2px solid var(--ink);
  border-radius: 6px;
  transition:
    border-color 0.15s,
    box-shadow 0.15s;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04);
}
.town-search-input-wrap:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(245, 184, 0, 0.18);
}
.town-search input[type="search"] {
  width: 100%;
  padding: 1.1rem 3rem 1.1rem 3.25rem;
  font-family: var(--font-body) !important;
  font-size: 1.1rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  border-radius: 6px;
  -webkit-appearance: none;
  appearance: none;
  outline: none;
  font-weight: 500;
}
.town-search input:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.town-search input[type="search"]::-webkit-search-decoration,
.town-search input[type="search"]::-webkit-search-cancel-button,
.town-search input[type="search"]::-webkit-search-results-button,
.town-search input[type="search"]::-webkit-search-results-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.town-search input::placeholder {
  color: #9a9a9a;
  font-weight: 400;
}
.town-search-input-wrap:focus-within .town-search-icon {
  opacity: 1;
  color: var(--black);
}

.town-search .clear-btn {
  position: absolute;
  right: 0.65rem;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  background: var(--bg-warm);
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--ink);
  border-radius: 50%;
  display: none;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1;
  transition:
    background 0.15s,
    color 0.15s;
}
.town-search .clear-btn:hover {
  background: var(--accent);
  color: var(--black);
}
.town-search.has-query .clear-btn {
  display: flex;
}

/* Hidden state for filtered items */
.region-towns li.is-hidden,
.region.is-hidden {
  display: none;
}

/* ===== Blog featured image / category banner ===== */
.featured-image {
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  border-radius: 6px;
  border: 2px solid var(--ink);
  margin: 0;
  display: block;
}
.post-banner {
  background: var(--accent);
  border: 3px solid var(--black);
  border-radius: 6px;
  padding: 2.25rem 1.75rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  margin: 0;
}
.post-banner-mark {
  width: 72px;
  height: 72px;
  background: var(--black);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  flex-shrink: 0;
  line-height: 1;
}
.post-banner-text {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.8rem, 4.5vw, 3rem);
  letter-spacing: 0.04em;
  color: var(--black);
  text-transform: uppercase;
  line-height: 1.02;
}
.post-banner-text small {
  display: block;
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--ink);
  margin-top: 0.35rem;
}
@media (max-width: 600px) {
  .post-banner {
    padding: 1.5rem 1.25rem;
    gap: 1rem;
  }
  .post-banner-mark {
    width: 56px;
    height: 56px;
    font-size: 1.9rem;
  }
}

/* Stop region container from being too tight on mobile when search opens */
@media (max-width: 600px) {
  .town-suggestions {
    max-height: 280px;
  }
  .town-suggestion-region {
    display: none;
  }
}
#service-map {
  height: 640px;
  width: 100%;
  border: 2px solid var(--ink);
  border-radius: 6px;
  background: var(--bg-warm);
  z-index: 1;
}
@media (max-width: 600px) {
  #service-map {
    height: 420px;
  }
}

/* ===== Homepage size cards with image + price (3-up) ===== */
.sizes.cols-3 {
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .sizes.cols-3 {
    grid-template-columns: repeat(3, 1fr);
  }
}
.size-card.priced {
  display: flex;
  flex-direction: column;
  text-align: left;
}
.size-card.priced img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  height: auto;
  margin-bottom: 0.5rem;
}
.size-card.priced .price {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  color: var(--accent-cta);
  display: block;
  margin: 0.1rem 0 0.6rem;
}
.size-card.priced ul {
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.5;
}
.size-card.priced ul li::before {
  content: "✓ ";
  color: var(--accent-cta);
  font-weight: 700;
}
@media (min-width: 700px) {
  .item-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}
.steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-top: 1.5rem;
}
@media (min-width: 760px) {
  .steps {
    grid-template-columns: repeat(3, 1fr);
  }
}
.step {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1.25rem 1.25rem 1.4rem;
}
.step .n {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  color: var(--accent-cta);
  line-height: 1;
}
.step h3 {
  margin: 0.4rem 0 0.3rem;
}
.step p {
  margin: 0;
  font-size: 0.9rem;
  color: var(--muted);
}
@media (min-width: 760px) {
  .steps.steps-4 { grid-template-columns: repeat(4, 1fr); }
}
.price-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1.5rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 4px;
  overflow: hidden;
}
.price-table th,
.price-table td {
  text-align: left;
  padding: 0.9rem 1rem;
  border-bottom: 1px solid var(--line);
}
.price-table thead th {
  background: var(--black);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
}
.price-table tbody tr:last-child td {
  border-bottom: none;
}
.price-table .size {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--black);
}
.price-table .amt {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--accent-cta);
  white-space: nowrap;
}
@media (min-width: 600px) {
  .includes {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ===== Service menu (Residential / Commercial lists) ===== */
.svc-menu {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  margin-top: 1.5rem;
}
@media (min-width: 760px) {
  .svc-menu {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 600px) {
  .booking-modal-content {
    width: calc(100vw - 4.5rem);
    height: calc(100vh - 11rem);
    border-radius: 8px;
    border: 2px solid var(--black);
    box-shadow: 4px 4px 0 var(--accent);
  }
  .booking-modal-close {
    top: 10px;
    right: 10px;
  }
  .booking-modal-iframe {
    width: 100%;
    height: 100%;
    margin: 0;
  }
}

/* ===== Full-bleed hero (real-photo background) ===== */
.hero.hero-bg {
  position: relative;
  padding: clamp(2.25rem, 5.5vw, 4.5rem) 0 clamp(5rem, 10vw, 8rem);
  border-bottom: 0;
  overflow: hidden;
  isolation: isolate;
  min-height: 70vh;
  display: flex;
  align-items: center;
}
.hero.hero-bg .wrap.hero-grid { width: 100%; }
.hero-bg-media { position: absolute; inset: 0; z-index: -2; }
/* object-position nudged down from center to 40% so the sunset band sits below
   the nav instead of getting cut awkwardly right at the top edge. */
.hero-bg-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 40%; display: block; }
.hero-bg-overlay {
  position: absolute;
  inset: 0;
  /* Lighter over the sky (top) so the sunset shows, darker toward the bottom
     where the headline + buttons sit. The 0.42 top stop was measured, not
     guessed: sampling the actual photo pixels behind the H1 at 375px put the
     worst-case contrast at 2.99:1 against the 3:1 large-text floor — passing on
     average (5.21:1) but failing over the brightest patch of sky. 0.50 takes the
     worst case to ~3.4:1 and is not visible as a change. */
  background: linear-gradient(180deg, rgba(0,0,0,0.50) 0%, rgba(0,0,0,0.66) 46%, rgba(0,0,0,0.82) 100%);
  z-index: -1;
}
@media (min-width: 900px) {
  .hero-bg-overlay {
    /* Directional scrim: dark behind the left text column, clear over the
       right/top so the sunset comes through. */
    background:
      linear-gradient(90deg, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.5) 42%, rgba(0,0,0,0.08) 70%),
      linear-gradient(0deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.12) 38%, rgba(0,0,0,0) 62%);
  }
}
.hero.hero-bg .hero-grid { align-items: center; }
.hero.hero-bg .hero-content { color: #fff; }
.hero.hero-bg h1 { color: #fff; font-size: clamp(2.1rem, 5vw, 3.8rem); line-height: 1.05; margin-bottom: 1rem; text-shadow: 0 2px 4px rgba(0,0,0,0.92), 0 1px 22px rgba(0,0,0,0.55); }
.hero.hero-bg .hero-accent { color: #ff6a4d; display: inline-block; }
.hero.hero-bg .lead { color: rgba(255,255,255,0.85); max-width: 36em; margin-bottom: 2rem; font-size: clamp(1rem, 1.4vw, 1.15rem); }
@media (max-width: 640px) {
  .hero-ctas { flex-direction: column; align-items: stretch; }
  .hero-ctas .btn { width: 100%; min-height: 48px; justify-content: center; }
}
.btn.btn-on-dark, .btn-secondary.btn-on-dark {
  background: transparent; color: #fff; border-color: #fff; box-shadow: 4px 4px 0 var(--accent);
}
.btn.btn-on-dark:hover, .btn-secondary.btn-on-dark:hover { background: #fff; color: var(--black); }

/* ===== Full-bleed centered hero (major pages: dumpsters, services, service area) ===== */
.hero.hero-bg-centered {
  text-align: center;
  padding-bottom: clamp(7rem, 11vw, 9rem);
}
.hero.hero-bg-centered .hero-content-centered {
  max-width: 56rem;
  margin: 0 auto;
  color: #fff;
}
.hero.hero-bg-centered h1 {
  color: #fff;
  font-size: clamp(2rem, 4.8vw, 3.6rem);
  line-height: 1.05;
  margin-bottom: 1rem;
}
.hero.hero-bg-centered .hero-accent {
  color: var(--accent);
}
.hero.hero-bg-centered .lead {
  color: rgba(255, 255, 255, 0.88);
  max-width: 44rem;
  margin: 0 auto 2rem;
  font-size: clamp(1rem, 1.4vw, 1.15rem);
}
.hero.hero-bg-centered .hero-ctas {
  justify-content: center;
  margin-bottom: 0;
}
.hero.hero-bg-centered .eyebrow {
  margin-bottom: 1.25rem;
}
/* Topbar phone pill + scroll-morph Call/Text CTAs */
.tel-cta {
  background: var(--accent-cta);
  color: #fff;
}
.tel-cta:hover {
  background: var(--accent-cta-hover);
}
.tel-cta::before {
  background: #fff;
}
/* Big phone + text CTAs (hero/section/contact) */
.tel-big,
.text-big {
  background: var(--accent-cta);
  color: #fff;
}
.tel-big:hover,
.text-big:hover {
  background: var(--accent-cta-hover);
}
/* Prominent "Book Online" button — flame-orange so it stands out from the
   red Call/Text buttons. The primary action on dumpster pages. */
.book-big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.3vw, 1.65rem);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  background: var(--accent);
  color: var(--black);
  padding: 0.85rem 1.5rem;
  border-radius: 6px;
  border: 2px solid var(--black);
  box-shadow: 4px 4px 0 var(--black);
  transition:
    transform 120ms ease,
    box-shadow 120ms ease,
    background 120ms ease;
}
.book-big:hover {
  background: var(--accent-cta-hover);
  color: #fff;
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--black);
}
.book-big:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--black);
}
/* Unified CTA button group: one centered, wrapping row (fixes the
   split-row / off-center spacing). */
.cta-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin-top: 1.5rem;
}
.cta-buttons .tel-big,
.cta-buttons .text-big,
.cta-buttons .book-big {
  margin: 0;
}
.cta-final .cta-buttons {
  justify-content: center;
}
@media (max-width: 640px) {
  .cta-buttons { flex-direction: column; align-items: stretch; }
  .cta-buttons .tel-big,
  .cta-buttons .text-big,
  .cta-buttons .book-big {
    width: 100%;
    min-height: 48px;
    justify-content: center;
    box-sizing: border-box;
  }
}
.tel-big::before,
.text-big::before {
  background: #fff;
}

/* ============================================================
   Shared structural/UX components
   brought over from the reference site. Orange (--yellow) =
   accent, consistent with the red-primary hierarchy.
   ============================================================ */

/* --- Size-card polish (chunky border + top accent bar + hover lift) --- */
.size-card {
  border: 3px solid var(--black);
  border-radius: 8px;
  box-shadow: 6px 6px 0 var(--black);
  position: relative;
  /* No overflow:hidden — the availability badge straddles the top border and a
     clip slices it. Verified before/after: card sizes, image boxes and page
     height unchanged, so the clip was protecting nothing. */
}
.size-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: var(--accent);
}
.size-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: 9px 9px 0 var(--accent);
  border-color: var(--black);
}
.size-card.priced {
  padding-top: 1.75rem;
}
@media (min-width: 850px) {
  .pillar-grid {
    grid-template-columns: 1fr 1fr 1fr;
  }
}
@media (min-width: 600px) {
  .town-services-grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (min-width: 900px) {
  .town-services-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}
@media (min-width: 640px) {
  .quick-links {
    grid-template-columns: 1fr 1fr;
  }
}
@media (min-width: 900px) {
  .quick-links {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* ============================================================
   DESIGN POLISH PASS (2026-06-11): unify flat cards into the
   brand's chunky bordered-card family for visual consistency.
   ============================================================ */

/* Process steps — number becomes an orange circle badge */
.step {
  border: 2px solid var(--black);
  border-radius: 8px;
  padding: 1.6rem 1.4rem 1.45rem;
  box-shadow: 5px 5px 0 var(--black);
  position: relative;
  transition:
    transform 150ms ease,
    box-shadow 150ms ease;
}
.step:hover {
  transform: translate(-2px, -2px);
  box-shadow: 7px 7px 0 var(--accent);
}
.step .n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--black);
  border: 2px solid var(--black);
  font-size: 1.3rem;
  margin-bottom: 0.9rem;
}

/* Specialty-cleanout cards (services.html) — were an orphan dark
   image-card with no image (rendered as a black box). Restyle to the
   site's white chunky card with an orange top accent. */
.svc-card {
  background: #fff;
  border: 2px solid var(--black);
  border-radius: 8px;
  box-shadow: 6px 6px 0 var(--black);
  min-height: 0;
  overflow: hidden;
  position: relative;
  transition:
    transform 150ms ease,
    box-shadow 150ms ease;
}
.svc-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: var(--accent);
  z-index: 2;
}
.svc-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: 9px 9px 0 var(--accent);
}
.svc-card .svc-body {
  background: none;
  padding: 2.25rem 1.85rem 1.85rem;
}
.svc-card h3 {
  color: var(--black);
}
.svc-card p {
  color: var(--ink);
}
.svc-card a.svc-link {
  color: var(--accent-cta);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 1px;
}
.svc-card a.svc-link:hover {
  color: var(--black);
}
@media (min-width: 800px) {
  .allow-grid {
    grid-template-columns: 1fr 1fr;
    gap: 1.75rem;
  }
}
@media (min-width: 480px) {
  .allow-list {
    grid-template-columns: 1fr 1fr;
    column-gap: 1rem;
  }
}
@media (min-width: 850px) {
  .pillar-grid {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

/* ============================================================
   ACCESSIBILITY (audit fixes): keyboard focus, reduced motion,
   mobile tap targets.
   ============================================================ */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
summary:focus-visible,
input:focus-visible,
[tabindex]:focus-visible,
.town-service-card:focus-visible,
.size-card:focus-visible {
  outline: 3px solid var(--accent-cta);
  outline-offset: 2px;
  border-radius: 3px;
}
/* Light focus ring on red/dark surfaces for contrast */
.btn-primary:focus-visible,
.tel-cta:focus-visible,
.tel-big:focus-visible,
.text-big:focus-visible,
.btn-on-dark:focus-visible,
.hero-card-cta:focus-visible,
.cta-final a:focus-visible,
.mobile-cta a:focus-visible {
  outline-color: #fff;
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
/* Bigger mobile tap target on the topbar phone pill (was ~22px) */
@media (max-width: 999px) {
  .tel-cta {
    min-height: 44px;
    padding: 0.65rem 0.85rem;
    font-size: 0.9rem;
  }
}

/* Removing the scroll-morphing topbar left the header's own phone button on
   screen at every scroll position, and .mobile-cta is position:fixed with no
   media query -- so a phone showed two identical always-visible tel: links,
   one at y=15 and one at y=734. The pill is the persistent mobile action, so
   the header drops its duplicate and gets 145px back for the brand mark.
   Desktop keeps its header number: there is room, and it is conventional. */
@media (max-width: 999px) {
  .topbar .tel-cta { display: none; }
}
.acc-item {
  background: #fff;
  border: 2px solid var(--black);
  border-radius: 6px;
  overflow: hidden;
  transition: box-shadow 150ms;
}
.acc-item[open] {
  box-shadow: 4px 4px 0 var(--accent);
}
.acc-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem 1.25rem;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  color: var(--black);
  transition: background 120ms;
}
.acc-item summary::-webkit-details-marker {
  display: none;
}
.acc-item summary:hover {
  background: #fafaf7;
}
.acc-title {
  flex: 1;
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  color: inherit;
}
.acc-icon {
  flex-shrink: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.7rem;
  line-height: 0.8;
  color: var(--black);
  transition: transform 0.2s;
}
.acc-icon::before {
  content: "+";
}
.acc-item[open] .acc-icon {
  transform: rotate(45deg);
}
.acc-body {
  padding: 0 1.25rem 1.25rem;
  color: var(--ink);
  line-height: 1.6;
  border-top: 1px solid var(--line);
  padding-top: 1rem;
  margin-top: 0;
}
.acc-body p {
  margin: 0 0 0.85rem;
  color: var(--ink);
}
.acc-link {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--black);
  text-decoration: none;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 1px;
}
.acc-link:hover {
  color: var(--accent-cta);
  border-bottom-color: var(--black);
}
@media (min-width: 800px) { .why-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 800px) { .price-note-grid { grid-template-columns: repeat(3, 1fr); } }

/* ============================================================
   Homepage conversion additions (2026-07-12)
   Stats band · size-card actions · pull-quote · service-area map
   ============================================================ */

/* --- Stats band (real facts, sits under the hero) --- */
.stat-band { background: var(--black); }
.stat-band .wrap {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 1px; background: rgba(255, 255, 255, 0.12);
  border-radius: 8px; overflow: hidden; margin-top: 0; margin-bottom: 0;
}
.stat-band { padding: 0; }
.stat-item {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 0.25rem; padding: 1.4rem 1rem;
  background: var(--black); color: #fff; text-decoration: none;
  transition: background 0.15s;
}
.stat-item:hover { background: #232b66; /* navy hover, not the donor near-black */ }
.stat-item .si-val {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.25rem, 2.4vw, 1.7rem);
  line-height: 1; color: #fff;
}
.stat-item:hover .si-val { color: var(--accent); }
.stat-item .si-label {
  font-size: 0.8rem; line-height: 1.3; color: rgba(255, 255, 255, 0.72);
}
@media (max-width: 700px) {
  .stat-band .wrap { grid-template-columns: 1fr 1fr; }
}
/* override the feature-bullet checkmark that .size-card.priced ul li::before adds */
.size-card.priced .price-rows li::before { content: none; }

/* Genuine comparison table (the size guide) on a phone. Five columns cannot
   hold their meaning at 375px, and horizontal scroll hides the columns that
   decide the choice. Each row becomes a labelled block instead, carrying its
   own headers down from data-label so nothing depends on a thead the visitor
   can no longer see. Scoped to tables that opt in, so it cannot leak. */
@media (max-width: 700px) {
  .price-table-stack, .price-table-stack thead, .price-table-stack tbody,
  .price-table-stack tr, .price-table-stack td { display: block; width: 100%; }
  .price-table-stack thead { display: none; }
  .price-table-stack tbody tr {
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 0.85rem 1rem;
    margin-bottom: 0.7rem;
    background: #fff;
  }
  .price-table-stack tbody td {
    display: grid;
    grid-template-columns: 7.5rem 1fr;
    gap: 0.75rem;
    align-items: baseline;
    border: 0;
    padding: 0.3rem 0;
  }
  .price-table-stack tbody td::before {
    content: attr(data-label);
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .price-table-stack tbody td.price {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.3rem;
    color: var(--ink);
  }
}

/* ============================================================
   RATE CARDS — one panel per product, replacing the grouped rates table.

   The rates are not tabular data. A table asserts every row is the same kind
   of thing; these are three different products, each with its own set of
   rental periods. Forcing that two-level hierarchy into a flat table needs
   colspan "group" rows, and a colspan row still physically sits inside the
   column grid — which is why "20-yard roll-off" kept reading as a value under
   the "Rental period" header no matter how the band was styled. That was
   structural, not cosmetic, and three restyles could not fix it.

   Second tell: the "Includes" column said "Up to 1 ton" on five of six rows.
   A column that repeats is not a column — it is a property of the product,
   so it now sits once per panel in .rate-note.

   As panels, the size is a real heading, each price sits beside its own Book
   affordance, and the whole thing stacks on mobile with no layout hacks.
   ============================================================ */
/* stretch, not start: at 456/357/278px the three panels ended on three
   different baselines, which reads as unfinished rather than deliberate.
   Equal heights with the footnote pushed to the bottom squares them off. */
.rate-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  margin-top: 1.75rem;
  align-items: stretch;
}
/* Below the three-up breakpoint the panels stretched to the container -- 790px
   wide at 859px viewport, which stranded "3 days" 489px from its own price, the
   exact eye-travel problem this rebuild existed to remove, reintroduced at
   tablet width. Capping the measure is the fix.
   The breakpoint stays at 860 rather than the 760 used by .sizes.cols-3: a rate
   row carries a price and a Book button side by side and needs ~245px, so three
   up at 760 leaves 216px and the button overflows its own panel. Same-looking
   components, genuinely different minimum widths. */
.rate-cards { max-width: 560px; margin-inline: auto; }
@media (min-width: 860px) {
  .rate-cards {
    /* minmax(0,…) so the three columns stay exactly equal; plain 1fr floors at
       min-content and the panel holding "$649*" + Book grew wider than its
       neighbours. */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.5rem;
    max-width: none;
  }
}
.rate-card.featured { border-color: var(--accent-cta); box-shadow: 6px 6px 0 var(--accent-cta); }

.rate-rows { list-style: none; margin: 1.15rem 0 0; padding: 0; }
.rate-rows li + li { margin-top: 0.5rem; }

/* Whole row is the target: a 44px+ tap area on mobile, and the price sits
   next to the action instead of across a 1,180px table row. */
.rate-rows a {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: 0.9rem;
  min-height: 56px;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  transition: border-color 140ms ease, background 140ms ease;
}
.rate-rows a:hover,
.rate-rows a:focus-visible {
  border-color: var(--accent-cta);
  background: var(--accent-tint);
}
.rate-rows a:hover .rate-go,
.rate-rows a:focus-visible .rate-go { background: var(--accent-cta-hover); }
.price-rows li.featured .dur { color: var(--accent-cta); }

/* --- Size cards: info link + booking action footer --- */
.size-card-link { display: block; text-decoration: none; color: inherit; }
.size-card-actions {
  display: flex; gap: 0.6rem; margin-top: auto; padding-top: 1.1rem;
  /* When both buttons can't fit side by side (narrow phones ~360px and below),
     wrap them to full-width stacked rows instead of squishing the labels onto
     two cramped lines. */
  flex-wrap: wrap;
}

/* --- Service-area map (Leaflet, keyless) --- */
#area-map {
  min-height: 400px; height: 400px; margin: 0 0 2rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-warm); z-index: 0;
}
@media (max-width: 700px) { #area-map { min-height: 300px; height: 300px; } }

/* Availability badge on the dumpster cards. "Available today" is a STOCK
   statement (this size is in the yard), not a delivery promise, so it carries
   no turnaround wording — which matters here, where four towns need notice.
   Colours are this site's own tokens: near-black chip, and the palette's own
   --ok green for the status dot (never an imported green; on a red-brand site
   a red dot would read as an alert, not availability). */
.avail-badge {
  position: absolute;
  top: -0.75rem;
  right: 0.6rem;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0 0.72rem;
  height: 30px;
  border-radius: 999px;
  background: var(--black);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.16);
}
.avail-badge::before {
  content: "";
  width: 0.44rem;
  height: 0.44rem;
  border-radius: 50%;
  background: var(--accent); /* site orange, never an imported green */
}
@media (max-width: 640px) {
  .avail-badge { font-size: 0.7rem; right: 0.7rem; }
}

/* RANK vs STATUS — two different kinds of information, so two different visual
   registers rather than two competing pills.

   "Available Today" is a STATUS: it appears on every card, so it stays the
   filled pill on the border — consistent position, scannable down the row.
   "Most Popular" is a RANK: it appears on exactly ONE card, and a second filled
   pill of the same shape and weight just read as a mismatched twin of the first
   at every position tried (six of them). So rank becomes a typographic label
   inside the card, above the size name, where product labels live on real
   product cards.

   The slot is reserved on EVERY card, so the featured card's photo, title and
   buttons stay aligned with its neighbours — an in-flow label on one card only
   dropped its contents 42px below the rest of the row. */
.size-card .yd::before {
  content: "";
  display: block;
  height: 1.15rem;
  margin-bottom: 0.15rem;
}
.size-card.featured .yd::before {
  content: "★ Most Popular";
  color: var(--accent-cta);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.15rem;
}

/* The size cards each carried a "Details" button pointing at the same URL as the
   card link wrapping the photo and copy, so two of the card's links went one
   place. With the duplicate gone the booking button is alone and takes the full
   width, which is a bigger tap target than half a row. */
.size-card-actions .size-act:only-child { width: 100%; flex: 1 1 100%; }
.pr-actions .btn { flex: 1 1 auto; justify-content: center; }
@media (max-width: 640px) {
  .pr-actions { flex-direction: column; align-items: stretch; }
  .pr-actions .btn { width: 100%; }
}
@media (max-width: 640px) {
  .cta-inline-row { flex-direction: column; align-items: stretch; text-align: center; }
  .cta-inline-row .btn { width: 100%; }
}

/* ═══ DJP fleet button classes (see SITE-LOCKS.md §4) ═══════════════════════
   Six classes, two axes. Every tel: link carries .btn--call (greppable
   invariant). .btn--more is the only outline and never a conversion action. */
.btn--call { background: var(--btn-call-bg); color: #fff; border: 2px solid var(--btn-call-bg); }
.btn--call:hover { background: var(--btn-call-bg-hover); border-color: var(--btn-call-bg-hover); color: #fff; }
.btn--book { background: var(--btn-book-bg); color: #fff; border: 2px solid var(--btn-book-bg); }
.btn--book:hover { background: var(--btn-book-bg-hover); border-color: var(--btn-book-bg-hover); color: #fff; }
.btn--more { background: transparent; color: var(--btn-more-fg); border: 2px solid var(--btn-more-border); }
.btn--more:hover { background: var(--navy-tint); color: var(--btn-more-fg); }
.btn--on-dark.btn--more { color: #fff; border-color: rgba(255,255,255,.75); }
/* Booking button ON the dark hero card. --btn-book-bg is a dark navy-black
   built for white page bands; inside the navy hero card it measured 1.16:1
   against the card, so the button had no readable edge at all — only its label
   showed. It still has to be SOLID because it carries a conversion action
   (btn--more is the outlined variant and the doctrine bars it from those), so
   it inverts instead: white surface, navy label. Distinct from the orange
   btn--call beside it, and the viewport still reads one accent-filled CTA. */
.btn--book.btn--on-dark {
  background: #fff;
  color: var(--navy-deep);
  border-color: #fff;
}
.btn--book.btn--on-dark:hover { background: var(--navy-tint); border-color: var(--navy-tint); }
.btn--on-dark.btn--more:hover { background: rgba(255,255,255,.12); color: #fff; }
.btn--lg { font-size: 1.1rem; padding: 0.9rem 1.9rem; min-height: 52px; }

/* Press state: scale DOWN on :active (the craft move; scale-up hover is the
   generated-UI signature). 120ms ease-out. */
.btn:active, .btn--call:active, .btn--book:active, .btn--more:active,
.tel-cta:active, .mobile-cta a:active {
  transform: scale(0.97);
  transition: transform 120ms ease-out;
}
button { color: var(--ink); } /* unset <button> color renders BLUE on iOS */

/* Header action pair (SITE-LOCKS §1): Book Online outlined + phone filled.
   Book hides below 1000px; the floating pill carries mobile. */
.topbar-actions { display: flex; align-items: center; gap: 0.9rem; }
@media (max-width: 999px) { .topbar-actions .topbar-book { display: none; } }
.lede { font-size: 1.15rem; max-width: 62ch; }
.nearby { color: var(--muted); font-size: 0.95rem; margin-top: 1.2rem; }
/* DJP hero photo: truck+dumpster sit center-right; bias the crop right and up
   (subject band measured; text overlay sits lower-left). */
/* Hero crop, retuned for hero-truck-hauling-loaded-box (2026-08-21).
   Which axis crops flips with the viewport, so both values matter but never at
   the same time: at desktop the hero box is wide and short, so the photo scales
   to width and only the VERTICAL value does anything (measured overflowX 0,
   overflowY 358 at 1280). On a phone the box is tall, the photo scales to
   height, and only the HORIZONTAL value moves. 45% vertical keeps sky behind
   the headline without cutting the wheels; 52% horizontal keeps the cab and the
   loaded box together when the sides crop. Measured at 375px: only 44% of the
   photo's width survives, and 52% centred that window on the wheels and a pole,
   which is the least recognisable part of the rig. 26% puts the DJP-branded cab
   in frame with the box beside it. */
.hero.hero-bg .hero-bg-media img { object-position: 26% 45%; }

/* Size-card images. This rule was written when the cards held PHOTOGRAPHS of
   dumpsters on pavement, where cropping to 2:1 bought back ~35% of the frame.
   The cards now hold transparent CUT-OUTS, and the rule further down flips the
   fit to `contain` for exactly that reason. Two leftovers had to go with the
   change and did not: `object-fit: cover` was dead, overridden and misleading
   to read, and `object-position: 50% 55%` was NOT dead — it kept applying to
   the contained cut-outs and sat every dumpster low in its slot. Centred now. */
.size-card img { width: 100%; height: auto; aspect-ratio: 2 / 1; object-position: 50% 50%; }
@media (max-width: 640px) {
  .size-card { padding-bottom: 0.9rem; }
  .size-card h3 { margin: 0.5rem 0 0.15rem; }
  .size-card .spec { margin: 0.1rem 0; }
}

.vis-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ═══ White-chrome topbar (Ryan 2026-08-18): navy logo on white, navy links,
   the fleet-orange phone CTA as the single loud element. ═══ */
.topbar nav.primary a { color: #1a2150; font-weight: 600; }
.topbar nav.primary a:hover { color: #2d3891; }
.topbar nav.primary a[aria-current="page"] { color: #1a2150; border-bottom: 2px solid #f26114; }
.topbar .tel-cta, .topbar .tel-cta.btn--call {
  background: var(--btn-call-bg); border-color: var(--btn-call-bg); color: #fff;
}
.topbar .tel-cta:hover { background: var(--btn-call-bg-hover); border-color: var(--btn-call-bg-hover); color: #fff; }
.topbar .topbar-book { color: #1a2150; border-color: #1a2150; }
.topbar .topbar-book:hover { background: var(--navy-tint); color: #1a2150; }
@media (max-width: 999px) {
  .topbar .nav-toggle { border-color: #1a2150; }
  .topbar .nav-toggle span { background: #1a2150; }
  .topbar nav.primary { background: var(--black); }
  .topbar nav.primary a { color: #fff; }
  .topbar nav.primary a[aria-current="page"] { color: #fff; border-bottom-color: #f26114; }
}

.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--black); color: #fff; padding: 0.6rem 1rem; border-radius: 0 0 6px 0; }
.skip-link:focus { left: 0; }

.crumbs { font-size: 0.85rem; padding: 0.55rem 0; background: var(--bg-warm); border-bottom: 1px solid var(--line); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--navy); text-decoration: underline; }
.crumbs span[aria-current] { color: var(--ink); font-weight: 600; }
.crumb-sep { color: var(--muted); padding: 0 0.15rem; }

/* ═══ Hero card (fleet pattern; containment is the one direct-tested win).
   Dark glass so it holds over any photo; phone dominant; falsifiable list. ═══ */
.hero-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 2rem; align-items: center; }
.hero-card { background: rgba(11, 13, 30, 0.72); border: 1px solid rgba(255,255,255,0.22);
  border-radius: 10px; padding: 1.4rem 1.5rem; backdrop-filter: blur(2px); }
/* The kicker labels the most important element in the card, but shipped at
   0.95rem in the BODY font at 0.85 opacity — measured 16.15px against bullets
   at 15.64px, i.e. the same size and weight as body text but dimmed, so it read
   as a caption rather than a label. Now the display face at 1.25rem, weight 600,
   full opacity: clearly a label, still subordinate to the 2.3rem number under
   it. Sentence case, not uppercase — Barlow Condensed reads better that way. */
.hc-kicker { color: #fff; opacity: 1; margin: 0 0 0.1rem;
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 600;
  line-height: 1.15; }
.hc-phone { display: block; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.7rem, 3.4vw, 2.3rem); color: #fff; text-decoration: none; margin-bottom: 0.7rem; }
.hc-phone:hover { color: var(--accent); }
.hc-list { list-style: none; margin: 0 0 1.1rem; padding: 0; }
.hc-list li { color: #fff; padding-left: 1.5rem; position: relative; margin-bottom: 0.45rem; font-size: 0.95rem; }
.hc-list li::before { content: "✓"; position: absolute; left: 0; color: var(--accent); font-weight: 700; }
.hero-card .btn { width: 100%; justify-content: center; }
@media (max-width: 899px) {
  .hero-grid { grid-template-columns: 1fr; gap: 1.1rem; }
  .hero-card { padding: 1.1rem 1.2rem; }
}

/* ═══════════ Visual pass 2026-08-18 (Ryan's batch) ═══════════ */

/* Size cards v2: no top strip, no boxed specs, price loud, CTA pinned. */
.size-card::before { display: none; }
.size-card { display: flex; flex-direction: column; }
.size-card-link { display: flex; flex-direction: column; flex: 1; }
.size-card .card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; margin-top: 0.8rem; }
.size-card .card-head h3 { margin: 0; }
.size-card .price { font-family: var(--font-display); font-weight: 700; font-size: 1.7rem; color: var(--navy); margin: 0; }
.size-card .spec { background: transparent; border: 0; border-radius: 0; box-shadow: none;
  padding: 0.42rem 0; margin: 0; border-top: 1px solid var(--line); color: var(--muted); font-size: 0.9rem; }
.size-card .spec:first-of-type { margin-top: 0.65rem; }
.size-card .size-card-actions { margin-top: auto; padding-top: 0.9rem; }
/* cutout product shots: contain, never crop */
.size-card img { aspect-ratio: 2 / 1; object-fit: contain; background: #fff; padding: 0.4rem 0.6rem 0; }

/* Steps: locked 4-across on desktop, 2x2 mid, never 3+1 */
@media (min-width: 640px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .steps { grid-template-columns: repeat(4, 1fr); } }

/* Fleet accordion for every details FAQ (acc-item keeps its own component) */
.block details:not(.acc-item) { border: 2px solid var(--black); border-radius: 8px; background: #fff;
  margin: 0.65rem 0; box-shadow: 3px 3px 0 var(--black); }
.block details:not(.acc-item) summary { list-style: none; cursor: pointer; position: relative;
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; color: var(--navy);
  padding: 0.9rem 2.8rem 0.9rem 1.1rem; }
.block details:not(.acc-item) summary::-webkit-details-marker { display: none; }
.block details:not(.acc-item) summary::after { content: "+"; position: absolute; right: 1.05rem; top: 50%;
  transform: translateY(-50%); font-size: 1.5rem; line-height: 1; color: var(--accent); font-weight: 700; }
.block details:not(.acc-item)[open] summary::after { content: "\2212"; }
.block details:not(.acc-item) p { margin: 0; padding: 0 1.1rem 1rem; }

/* Price tables: the component the bare tables were missing */
.price-grid { width: 100%; border-collapse: separate; border-spacing: 0; background: #fff;
  border: 2px solid var(--black); border-radius: 8px; box-shadow: 4px 4px 0 var(--black);
  overflow: hidden; margin: 1.3rem 0; }
.price-grid th { background: var(--black); color: #fff; text-align: left; padding: 0.75rem 1rem;
  font-family: var(--font-display); font-weight: 600; letter-spacing: 0.03em; }
.price-grid td { padding: 0.75rem 1rem; border-top: 1px solid var(--line); vertical-align: top; }
.price-grid tbody tr:nth-child(even) td { background: var(--bg-warm); }
.price-grid td:first-child { font-weight: 600; color: var(--ink); }
/* .pg-price, NOT td:last-child. The old selector assumed every one of these
   tables ended in a short price. Two do not: pricing.html's "What Costs Extra"
   ends in full sentences and the size guide ends in "One room, a sorted garage,
   a small deck". `white-space: nowrap` forced those onto one unbreakable line,
   so at 375px the extras table measured 649px against a 326px slot — half of it
   off-screen, on the page whose whole job is telling people what things cost. */
.price-grid td.pg-price { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem;
  color: var(--navy); white-space: nowrap; }
@media (max-width: 640px) {
  /* Still scrolls, because the dimensions and tonnage columns cannot all fit —
     but price and size now sit inside the first screenful. tabindex makes the
     scroller reachable by keyboard (WCAG 2.1.1); without it the hidden columns
     are unreachable without a mouse or a finger. */
  .price-grid { display: block; overflow-x: auto; }
}
.price-grid:focus-visible { outline: 3px solid var(--btn-focus); outline-offset: 2px; }

/* Goes in / stays out visual (homepage) */
.inout-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; margin: 1.4rem 0; }
@media (max-width: 860px) { .inout-grid { grid-template-columns: 1fr; } }
.io-card { background: #fff; border: 2px solid var(--black); border-radius: 8px; box-shadow: 4px 4px 0 var(--black); padding: 1.15rem 1.2rem; }
.io-card h3 { display: flex; align-items: center; gap: 0.55rem; margin: 0 0 0.6rem; }
.io-card .io-ico { display: inline-flex; align-items: center; justify-content: center; width: 1.7rem; height: 1.7rem;
  border-radius: 50%; color: #fff; font-size: 1rem; font-weight: 700; flex: none; }
.io-yes .io-ico { background: var(--accent-cta); }  /* white glyph: 4.98:1, not 3.23:1 */
.io-fee .io-ico { background: var(--navy); }
.io-no .io-ico { background: var(--black); }
.io-card ul { list-style: none; margin: 0; padding: 0; }
.io-card li { padding: 0.3rem 0; border-top: 1px solid var(--line); font-size: 0.95rem; }
.io-card li:first-child { border-top: 0; }

/* County blocks on the hub: linked towns as chips, unlinked as muted pills */
.region-towns { margin: 1.1rem 0; }
.region-towns h3 { margin: 0 0 0.5rem; }
.town-links a { display: inline-block; background: #fff; border: 2px solid var(--navy); color: var(--navy);
  border-radius: 999px; padding: 0.32rem 0.85rem; margin: 0 0.4rem 0.5rem 0; font-weight: 600;
  font-size: 0.9rem; text-decoration: none; }
.town-links a:hover { background: var(--navy); color: #fff; }
.town-pill { display: inline-block; background: var(--bg-warm); border: 1px solid var(--line);
  color: var(--muted); border-radius: 999px; padding: 0.3rem 0.8rem; margin: 0 0.35rem 0.45rem 0;
  font-size: 0.9rem; cursor: default; }

/* Hub town cards: kill the underlined display-face links + double frame */
.svc-card::before { display: none; }
.svc-card h3 a { text-decoration: none; color: var(--navy); }
.svc-card h3 a:hover { text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 3px; }

/* Contact page: three action cards instead of a stacked column */
.contact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; align-items: stretch; }
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-card { background: #fff; border: 2px solid var(--black); border-radius: 8px;
  box-shadow: 4px 4px 0 var(--black); padding: 1.3rem 1.3rem 1.4rem; display: flex; flex-direction: column; }
.contact-card h2 { margin: 0 0 0.4rem; font-size: 1.25rem; }
.contact-card .tel-big { margin: 0 0 0.5rem; }
.contact-card .tel-big a { font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.5rem, 2.6vw, 1.9rem); color: var(--navy); text-decoration: none; }
.contact-card .tel-big a:hover { color: var(--accent-cta); }
.contact-card p { margin: 0 0 0.6rem; font-size: 0.95rem; }
.contact-card .btn { margin-top: auto; justify-content: center; }
.contact-photo { margin-top: 1.6rem; }
.contact-photo img { width: 100%; height: auto; border-radius: 8px; border: 2px solid var(--black); box-shadow: 4px 4px 0 var(--black); }

/* homepage map block */
.home-map #area-map { margin-top: 1.2rem; }

/* Contact fixes: the number is huge TEXT, not a second button.
   Donor styles .tel-big ITSELF as a filled chip — flatten the element, not the anchor. */
.contact-card .tel-big { background: none; border: 0; box-shadow: none; padding: 0;
  display: block; margin: 0.1rem 0 0.6rem; }
.contact-card .tel-big::before, .contact-card .tel-big a::before { content: none; }
.contact-card .tel-big a { background: none; border: 0; box-shadow: none; padding: 0; display: inline;
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.6rem, 2.8vw, 2.1rem);
  color: var(--navy); text-decoration: none; }
.contact-card .tel-big a:hover { color: var(--accent-cta); }
/* short utility pages: less dead air between hero and first band */
.page-hero { padding-bottom: 1.6rem; }
.page-hero + .block { padding-top: 1.8rem; }

/* inout v2: tinted header bands, differentiated icons, footnotes pinned */
.io-card { padding: 0 1.2rem 1.15rem; display: flex; flex-direction: column; }
.io-card h3 { margin: 0 -1.2rem 0.7rem; padding: 0.85rem 1.2rem; border-bottom: 2px solid var(--black);
  border-radius: 6px 6px 0 0; font-size: 1.1rem; }
.io-yes h3 { background: var(--accent-tint); }
.io-fee h3 { background: var(--navy-tint); }
.io-no h3 { background: var(--bg-warm); }
.io-no .io-ico { background: #fff; color: var(--black); border: 2px solid var(--black); }
.io-card p { margin: auto 0 0; padding-top: 0.7rem; font-size: 0.9rem; color: var(--muted); }

/* Hub town cards: real padding, one frame, no stray accents */
.page-grid .svc-card { padding: 1.35rem 1.5rem 1.45rem; border: 2px solid var(--black);
  border-radius: 8px; box-shadow: 4px 4px 0 var(--black); background: #fff; }
.page-grid .svc-card::before, .page-grid .svc-card::after { display: none; }
.page-grid .svc-card h3 { margin: 0 0 0.6rem; }
.page-grid .svc-card p { margin: 0 0 0.7rem; }
.page-grid .svc-card p:last-child { margin-bottom: 0; }
.page-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem; }
@media (max-width: 860px) { .page-grid { grid-template-columns: 1fr; } }
/* county rows: give the pills room */
.region-towns { display: grid; grid-template-columns: 220px 1fr; gap: 1rem; align-items: start;
  padding: 1.1rem 0; border-top: 1px solid var(--line); margin: 0; }
.region-towns:first-of-type { border-top: 0; }
@media (max-width: 720px) { .region-towns { grid-template-columns: 1fr; gap: 0.3rem; } }
.town-links { margin: 0; line-height: 2.2; }

/* Breathing room after the card grids: the offset shadows need clearance,
   and a follow-on CTA row was sitting flush against the last card. */
.sizes + .cta-row, .page-grid + .cta-row, .inout-grid + .cta-row { margin-top: 2.1rem; }
.sizes { margin-bottom: 0.5rem; }

.ts-result { margin-top: 0.7rem; font-size: 1rem; min-height: 1.4rem; }
.ts-result.ts-yes { color: var(--ink); }
.ts-result.ts-yes strong { color: var(--navy); }
.ts-result.ts-maybe { color: var(--muted); }
.town-search { margin-top: 2rem; }

/* Project picker (FT pattern, DJP frame) */
.picker { margin: 1rem 0 2rem; }
.picker-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.8rem; }
@media (max-width: 860px) { .picker-options { grid-template-columns: 1fr; } }
.picker-opt { font-family: var(--font-display); font-weight: 600; font-size: 1rem; text-align: left;
  line-height: 1.3; padding: 1.05rem 1.15rem; border: 2px solid var(--black); border-radius: 8px;
  background: #fff; color: var(--ink); cursor: pointer; box-shadow: 3px 3px 0 var(--black); }
.picker-opt:hover { border-color: var(--accent-cta); }
.picker-opt.active { background: var(--accent-tint); box-shadow: 3px 3px 0 var(--accent-cta); border-color: var(--accent-cta); }
.picker-result { margin: 1rem 0; grid-column: 1 / -1; }
.picker-rec { border: 2px solid var(--black); border-radius: 8px; background: #fff;
  box-shadow: 4px 4px 0 var(--black); padding: 1.1rem 1.25rem; }
.picker-rec-label { display: block; color: var(--muted); font-size: 0.9rem; }
.picker-rec-size { display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 1.5rem; color: var(--navy); margin: 0.1rem 0 0.4rem; }
@media (max-width: 860px) { .inout-2 { grid-template-columns: 1fr; } }

/* Every-rental-includes: check grid instead of a wall paragraph */
.includes-grid { list-style: none; margin: 1.3rem 0 0; padding: 0; display: grid;
  grid-template-columns: repeat(3, 1fr); gap: 1rem 1.6rem; }
@media (max-width: 860px) { .includes-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .includes-grid { grid-template-columns: 1fr; } }
.includes-grid li { display: grid; grid-template-columns: 1.8rem 1fr; grid-template-rows: auto auto;
  column-gap: 0.6rem; align-items: start; }
.includes-grid .inc-ico { grid-row: 1 / 3; display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem; border-radius: 50%; background: var(--accent-cta); color: #fff;
  font-weight: 700; margin-top: 0.1rem; }  /* AA: white on the bright accent is 3.23:1 */
.includes-grid strong { font-family: var(--font-display); font-size: 1.05rem; color: var(--navy); }
.includes-grid small { color: var(--muted); font-size: 0.9rem; line-height: 1.35; }
.home-town-links { margin-top: 1.2rem; }

@media (min-width: 1000px) { .includes-grid.grid-4 { grid-template-columns: repeat(4, 1fr); } }

.inline-photo { margin: 1.4rem 0; }
.inline-photo img { width: 100%; max-width: 760px; height: auto; display: block;
  border-radius: 8px; border: 2px solid var(--black); box-shadow: 4px 4px 0 var(--black); }
.inline-photo figcaption { color: var(--muted); font-size: 0.9rem; margin-top: 0.5rem; }

/* Footer column headings: navy was 1.81:1 on the dark footer (fail).
   Fleet orange reads at 5.63:1 and carries the brand. */
footer.site .foot-col h3 { color: var(--accent); }
@media (min-width: 900px) { .lineup-notes { grid-template-columns: repeat(4, 1fr); } }
/* size cards: tighten the letterbox around the cutouts */
.size-card img { aspect-ratio: 2.15 / 1; padding: 0.3rem 0.5rem 0; }

/* Lineup v3: per-column figures. flex-grow carries the feet ratio, so the
   shared scale is enforced by layout and captions align under their boxes
   by construction. The whole column is the tap target. */
.lineup-cols { display: flex; align-items: flex-end; gap: 1rem; margin: 1.3rem 0 0.4rem;
  background: #fff; border: 2px solid var(--black); border-radius: 8px;
  box-shadow: 4px 4px 0 var(--black); padding: 1.6rem 1.4rem 1.3rem; overflow-x: auto; }
/* basis = each SVG's viewBox width, so px-per-foot is IDENTICAL across
   columns (feet-proportional basis ignored the fixed dim-label margin and
   rendered the 30 about 7% larger than the 15 — visible as mismatched
   "5 ft" brackets). align-items on the row is flex-end so baselines sit
   level; captions get a fixed-height block so all four align. */
.lu-col { flex-grow: var(--vw); flex-shrink: 1; flex-basis: 0; min-width: 0; display: flex; flex-direction: column;
  text-decoration: none; color: inherit; }
.lu-col img { width: 100%; height: auto; display: block; }
.lu-col strong { font-family: var(--font-display); color: var(--navy); font-size: 1rem;
  margin-top: 0.5rem; }
.lu-col small { color: var(--muted); font-size: 0.85rem; line-height: 1.4; }
.lu-col:hover strong { text-decoration: underline; text-decoration-color: var(--accent);
  text-decoration-thickness: 3px; }
@media (max-width: 700px) {
  .lineup-cols { min-width: 640px; }
  .lineup-wrap-outer { overflow-x: auto; }
}

/* Mid-page call band (the .cta-band rename dropped these rules — the band
   was rendering unstyled and flush against the next section). */
.callband { background: var(--black); padding: 1.5rem 0; margin: 2.2rem 0 0; }
.callband-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.2rem; flex-wrap: wrap; }
.callband p { margin: 0; color: #fff; font-family: var(--font-display); font-weight: 600; font-size: 1.15rem; }
/* Two actions in the band. They sit side by side and wrap together rather than
   splitting across the flex row, so the pair never separates awkwardly. */
.callband-acts { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
@media (max-width: 560px) {
  .callband-inner { flex-direction: column; align-items: stretch; text-align: center; }
  .callband-acts { flex-direction: column; align-items: stretch; }
  .callband-acts .btn { width: 100%; min-height: 48px; }
}

/* stat band: every value the same color (no accent cell) */
.stat-band .stat-item .si-val { color: #fff; }

.step .n.n-ico { display: inline-flex; align-items: center; justify-content: center; }
.step .n.n-ico svg { display: block; }

/* ghost button for dark hero surfaces */

/* Lineup: centered captions; individual cards on mobile (Ryan 2026-08-19) */
.lu-col { text-align: center; }
@media (max-width: 700px) {
  .lineup-cols { display: grid; grid-template-columns: 1fr; gap: 1rem; min-width: 0;
    border: 0; box-shadow: none; background: transparent; padding: 0; }
  .lu-col { background: #fff; border: 2px solid var(--black); border-radius: 8px;
    box-shadow: 3px 3px 0 var(--black); padding: 1.1rem 1.1rem 1.2rem; }
}
/* Footer columns: one link per row, not an inline pile */
footer.site .foot-col a { display: block; padding: 0.17rem 0; }

/* Tips row: chip + heading inline, body hugs its heading (the reserved
   two-row inner grid left tips 1-3 with a floating gap while a wrapped
   heading crowded tip 4). */
/* The .grid-4 variant used to switch the item back to `display: block` with an
   inline-flex badge, which made the <strong> heading ordinary inline text
   flowing AROUND the badge. A heading short enough to fit one line looked fine;
   "Transfer station item charges" wrapped, and the second line started at the
   container's left edge, underneath the badge. The <small> faked its indent
   with padding-left, so the heading and the body did not line up either.
   The base rule already solves both: the item is a two-column grid, the badge
   spans both rows in column one, and the heading and body stack in column two,
   so every wrapped line stays in its own column. Only the column count needs
   overriding for the four-up layout, which happens above. */
.includes-grid.grid-4 strong { line-height: 1.3; }

/* Size cards: what the size is actually for. The cards carried dimensions,
   tonnage and price but never the buying question. */
.size-card .good-for { margin: 0.55rem 0 0; font-size: 0.9rem; color: var(--ink);
  line-height: 1.4; }
.size-card .good-for strong { color: var(--navy); }

/* ---- .two-col (2026-08-21) ----------------------------------------------
   NOTE: .two-col was already in the markup on the commercial and roofing pages
   (text beside a portrait photo) but had NO CSS, so it rendered as a plain
   .wrap. Adding homepage styling to the bare class silently restyled both of
   those pages. The generic rule below is the text+image layout they wanted;
   the homepage's panel treatment lives on the .two-col--panels modifier so it
   cannot leak again. */
.two-col { display: grid; gap: 2.4rem; align-items: start; }
@media (min-width: 900px) {
  .two-col { grid-template-columns: 1fr 1fr; gap: 3.2rem; }
}
.two-col > div > h2 { margin-top: 0; }
.two-col > img { max-width: 100%; height: auto; border-radius: 10px; justify-self: center; }

/* Homepage: two equal text panels, divider between, CTAs bottom-aligned. */
.two-col--panels > div { display: flex; flex-direction: column; height: 100%; }
.two-col--panels > div > p:first-of-type { font-weight: 600; color: var(--navy); }
.two-col--panels .cta-row { margin-top: auto; padding-top: 1.4rem; }
@media (min-width: 900px) {
  .two-col--panels { align-items: stretch; }
  .two-col--panels > div + div { border-left: 3px solid rgba(45,56,145,0.12); padding-left: 3.2rem; }
}

/* ═══════════ Design audit 2026-08-21 ═══════════ */

/* HERO MOBILE HEIGHT. Measured 862px at 375x812 = 106% of the viewport, so
   nothing below the hero peeked and the page read as if it had no second
   section. Target is 65-80% (local-service-design). Now 634px = 78%, with the
   next section peeking 101px.

   Cut duplication before anything real: the hero card repeated the SAME tel:
   link the CTA row already shows ~20px above it, so the kicker+phone pair goes
   on small screens. The number is still one tap away in the CTA row, the
   sticky pill and the footer. The card's bullets stay — they are the only
   place the sizes, rental length and licensing appear above the fold, and the
   skill's rule is never to fix height by deleting real content. Buttons
   measured 64px, well clear of the 48px tap floor. */
@media (max-width: 640px) {
  .hero.hero-bg { padding: 1.4rem 0 2rem; min-height: 0; }
  .hero.hero-bg .hc-kicker,
  .hero.hero-bg .hc-phone { display: none; }
  .hero.hero-bg .hero-card { padding: 0.95rem 1.05rem; }
  .hero.hero-bg .hc-list { margin-bottom: 0.8rem; }
  .hero.hero-bg .hc-list li { margin-bottom: 0.28rem; font-size: 0.9rem; line-height: 1.35; }
  .hero.hero-bg h1 { font-size: 2rem; line-height: 1.08; margin-bottom: 0.5rem; }
  .hero.hero-bg .lead { font-size: 1rem; line-height: 1.45; margin-bottom: 0.9rem; }
  .hero.hero-bg .hero-grid { gap: 0.8rem; }
  .hero.hero-bg .cta-row { gap: 10px; }
}

/* SIZE-CARD MOBILE HEIGHT. Measured 462px at 375x812 = 0.57 of the viewport
   against a 0.5 budget, and the card section ran 2.71 screens against 2.5, so
   two dumpsters could never be compared on one screen — on a page whose whole
   job is "pick a size". Now 412px (0.51) and 2.46 screens.

   Nothing was deleted: the specs and tonnage are what answer "will my stuff
   fit", which is the question blocking the booking. The height came out of the
   photo crop (2/1 -> 2.5/1 on a contained cutout), the spec-row padding and
   the action padding. Buttons measured 59px, clear of the 48px floor. */
@media (max-width: 640px) {
  .size-card { padding: 1rem 1.1rem 0.85rem; }
  .size-card img { aspect-ratio: 2.5 / 1; padding: 0.2rem 0.5rem 0; }
  .size-card .card-head { margin-top: 0.55rem; }
  .size-card .spec { padding: 0.3rem 0; font-size: 0.9rem; }
  .size-card .spec:first-of-type { margin-top: 0.45rem; }
  .size-card .good-for { margin: 0.45rem 0 0; line-height: 1.35; }
  .size-card .size-card-actions { padding-top: 0.65rem; }
}

/* DEPTH + RADIUS SCALE. The house technique is the hard offset shadow, which is
   right — but it was running at FIVE sizes for the same resting role (3, 4, 5,
   6 and 8px) across the rendered pages, which is sprawl rather than a system.
   Snapped to two steps: 4px for controls and small surfaces, 6px for large
   ones, +2px on hover, 0 on press. The orange shadow stays on exactly one
   element (the hero card) so it remains an accent that was earned, not a
   second depth language.

   Radius likewise had three one-off values (3px, 4px, 10px) alongside the real
   scale. The system is 6px controls / 8px surfaces / 50% icons / 999px pills. */
.picker-opt { box-shadow: 4px 4px 0 var(--black); }
.picker-opt.active { box-shadow: 4px 4px 0 var(--accent-cta); }
.block details:not(.acc-item) { box-shadow: 4px 4px 0 var(--black); }
.step { box-shadow: 6px 6px 0 var(--black); }
.tel-cta { border-radius: 6px; }
.price-table { border-radius: 8px; }
.hero-card { border-radius: 8px; }

/* The hero card keeps the orange shadow — it is the ONE element that wears the
   accent depth, which is what "earn each use" means — but at 6px so it sits on
   the same two-step scale as everything else, instead of a fifth size. */
.hero-card { box-shadow: 6px 6px 0 var(--accent); }

/* NOTE on coloured border strips (the most-cited generated-UI tell): the
   stylesheet carried EIGHT of them, and a render check across 19 pages found
   that none of them ever applied — every one was dead donor CSS. `.svc-menu
   .col` in particular targets a `.col` child that markup does not contain
   (book.html's .svc-menu holds <a> elements). Seven were removed with the rest
   of the dead rules; no override is needed for the eighth. Verified: zero
   elements site-wide render a >=3px accent-coloured top or left border. */
/* STICKY MOBILE PILL. The markup is <a class="btn btn--call">, so the pill was
   inheriting .btn's 6px radius and diagonal 4px 4px shadow. Two documented
   house rules apply to a FLOATING pill specifically:
     - full-pill radius (it is a floating pill, not an in-flow button)
     - vertical-offset shadow only. The diagonal stamp is right for buttons
       anchored in the page flow, but on a floating pill the sideways offset is
       the only thing making the left and right edges differ, so a perfectly
       centred label reads as off-centre (Ryan caught this on Proudfoot).
   (The stale donor `a.call` blocks that used to sit above are deleted.) */
.mobile-cta .btn {
  border-radius: 999px;
  box-shadow: 0 3px 0 var(--black);
}
.mobile-cta .btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--black);
}
.mobile-cta .btn:active {
  transform: translateY(1px);
  box-shadow: 0 0 0 var(--black);
}

/* ═══════════ Review badge + show-more (2026-08-21, feed wired) ═══════════ */

/* The aggregate badge. Pill shape is deliberate here and is NOT the banned
   decorative eyebrow: that tell is unfalsifiable self-praise as decoration
   ("Trusted by 10,000+"). This inverts every part of it — a live, falsifiable
   number from someone else's platform, carrying Google's own mark. No
   editorial label ("EXCELLENT"): the number and the mark already say it. */
.reviews-badge-row { margin: 1.1rem 0 1.4rem; }
.reviews-badge {
  display: inline-flex; align-items: center; gap: 0.7rem;
  background: #fff; border: 2px solid var(--black);
  border-radius: 999px; padding: 0.55rem 1.15rem;
  box-shadow: 4px 4px 0 var(--black);
}
.reviews-badge .rb-stars { color: #fbbc04; font-size: 1.05rem; letter-spacing: 0.06em; line-height: 1; }
.reviews-badge .rb-text { display: flex; align-items: baseline; gap: 0.45rem; line-height: 1.1; }
.reviews-badge .rb-text strong {
  font-family: var(--font-display); font-weight: 700; font-size: 1.4rem; color: var(--ink);
}
.reviews-badge .rb-text span { font-size: 0.9rem; color: var(--muted); }
.page-hero .reviews-badge { background: #fff; }

/* Show-more. The cap is a DISPLAY rule only — every card stays in the HTML so
   Google and the assistants read the full set. Both halves of the cap are
   assigned by the generator, not by CSS alone, so a rebuild cannot un-cap the
   page; these rules only hide what the generator already marked. */
.rev-more-wrap { margin: 1.6rem 0 0; }
.rev-more { width: 100%; justify-content: center; }
@media (min-width: 641px) { .rev-more { width: auto; } }

/* Review-strip scroll indicator. The native bar is hidden and this one is
   DRAWN, because on iOS and macOS the native scrollbar is an OVERLAY: it takes
   no layout space and fades a moment after the swipe, so ::-webkit-scrollbar
   height/colour rules are ignored on exactly the devices the strip exists for.
   Driven by assets/js/rev-strip.js. */
.hscroll-track > span {
  display: block; height: 100%; width: 30%; border-radius: 3px;
  background: var(--accent); transition: transform .12s linear;
}
.hscroll-track.is-off { display: none; }
@media (prefers-reduced-motion: reduce) { .hscroll-track > span { transition: none; } }

/* The See-All button sat flush against the card row: .reviews is a flex strip,
   so the cards' own margins do not create space beneath it. Give the following
   CTA row real separation, and the strip a little breathing room under the
   cards for the same reason. */
.reviews { padding-bottom: 0.25rem; }
.reviews + .cta-row { margin-top: 1.9rem; }

/* Very narrow phones (<=360px): the review quote is the only thing that makes
   a card tall, so trim its type rather than dropping reviews from the pool.
   Measured at 320px the tallest card went from 98% of the viewport to ~70%. */
@media (max-width: 360px) {
  .reviews .review blockquote { font-size: 0.95rem; line-height: 1.48; }
  .reviews .review { padding: 1rem 1.05rem; }
}

/* MEASURE. Body prose ran ~140 characters a line at 1440px — nearly double the
   60-80ch the eye tracks comfortably, and the reason long paragraphs on the
   homepage and town pages felt like a wall. Only .lede and the blog's .prose
   were capped; ordinary section paragraphs were not.
   Capped at 70ch on direct paragraph children of a section wrap. The :not()
   list is utility rows that are NOT prose and must stay full width — if you add
   another such class, add it here too, or it will silently narrow. */
.block > .wrap > p:not(.cta-row):not(.town-links):not(.home-town-links):not(.rev-more-wrap):not(.reviews-badge-row):not(.fine),
.block > .wrap > details > p {
  /* 80ch, the top of the readable band, paired with the 18px body above. That
     combination measures ~716px = 60% of the block, which stops the column
     reading as an accident next to a full-width heading. 70ch at 17px was
     technically readable and looked like a layout bug. */
  max-width: 80ch;
}

/* Mobile budgets, retuned for the 18px body (2026-08-21).
   Raising body copy 17->18px is a readability win and applies MORE on a phone,
   not less, so the height is taken back from CHROME rather than from the type:
   hero padding and gaps, card padding and the photo crop. Measured at 375x812
   after the bump: hero 86% (target 65-80), size card 0.53 (budget 0.5), card
   section 2.63 screens (budget 2.5). */
@media (max-width: 640px) {
  /* These were squeezed on 2026-08-28 to absorb a much longer hero lede. That
     lede was then trimmed and the hero card hidden on mobile, which dropped the
     hero to 50% — under the 65% floor, so the squeeze had become a readability
     tax paid for a problem that no longer existed. Restored to the values tuned
     for the 18px body. */
  .hero.hero-bg { padding: 1.1rem 0 1.5rem; }
  .hero.hero-bg h1 { font-size: 1.9rem; margin-bottom: 0.4rem; }
  .hero.hero-bg .lead { font-size: 0.95rem; line-height: 1.4; margin-bottom: 0.7rem; }
  .hero.hero-bg .hero-grid { gap: 0.65rem; }
  .hero.hero-bg .hero-card { padding: 0.85rem 0.95rem; }
  .hero.hero-bg .hc-list { margin-bottom: 0.65rem; }
  .hero.hero-bg .hc-list li { margin-bottom: 0.22rem; font-size: 0.9rem; line-height: 1.3; }

  .size-card { padding: 0.85rem 1rem 0.75rem; }
  .size-card img { aspect-ratio: 2.7 / 1; }
  .size-card .card-head { margin-top: 0.45rem; }
  .size-card .spec { padding: 0.26rem 0; font-size: 0.9rem; }
  .size-card .good-for { margin: 0.35rem 0 0; font-size: 0.9rem; line-height: 1.3; }
  .size-card .size-card-actions { padding-top: 0.55rem; }
}

/* BUTTON CLEARANCE (mobile sweep, 2026-08-21).
   Measured at 375px: zero overlaps, but a "See every town on the route" button
   sat FLUSH (0px) against the town-pill list above it, and several CTA rows
   cleared adjacent copy by only 10px. .cta-row carried margin-bottom but no
   margin-top, so it only ever got separation when the element above happened to
   have its own bottom margin — the pill list does not.
   Give every in-flow CTA row real clearance, and raise the stacked-button gap
   from 10-12px to 14px so two full-width buttons never read as one control. */
.cta-row { margin-top: 1.15rem; }
.cta-final .cta-row,
.two-col--panels .cta-row,
.hero.hero-bg .cta-row { margin-top: 0; }   /* these set their own spacing */
.town-links + .cta-row,
.home-town-links + .cta-row { margin-top: 1.6rem; }
@media (max-width: 640px) {
  .cta-row { gap: 14px; }
  .hero.hero-bg .cta-row { gap: 12px; }
}

/* ══ booking: page form + site-wide modal ═══════════════════════════════════
   Structure, proportions and interaction mirror 417's quote flow (Ryan,
   2026-08-27: "mimick 417 as close as possible"). The COLOUR does not: 417
   hangs off --yellow/--red-deep/--radius-ft, and pulling those in would put a
   second brand inside this one, which is donor residue by another name. So the
   shapes are theirs and the palette, the 2px borders and the hard offset
   shadows are DJP's.

   Progressive enhancement, which is what most of these rules encode: with JS
   off ALL THREE STAGES ARE VISIBLE and the form posts as one long page.
   booking-form.js adds `.js-stepped`, and only then does anything hide. Never
   write a bare `.bk-stage { display: none }`. */

.bk-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3rem);
  align-items: start;
}
@media (max-width: 860px) { .bk-wrap { grid-template-columns: minmax(0, 1fr); } }

.book-form {
  background: var(--bg);
  border: 2px solid var(--black);
  border-radius: 10px;
  box-shadow: 6px 6px 0 var(--black);
  padding: clamp(1.25rem, 3.5vw, 2rem);
}
.book-form h2 { margin-top: 0; }

/* Honeypot. Off-screen rather than display:none, so a bot reading computed
   styles still fills it; aria-hidden + tabindex="-1" in the markup keep it away
   from anybody using a screen reader or a keyboard. */
.bk-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ── progress ──
   Three plain bars, aria-hidden: the stage headings already say where you are,
   so this is decoration and a screen reader should skip it. */
.bk-prog { display: none; gap: 0.5rem; margin-bottom: 1.75rem; }
.js-stepped .bk-prog { display: flex; }
.bk-prog span { flex: 1 1 0; height: 5px; border-radius: 3px; background: var(--line-control); }
.bk-prog span.done { background: var(--accent); }

/* ── stages ── */
.bk-stage + .bk-stage { margin-top: 2.75rem; padding-top: 2.5rem; border-top: 1px solid var(--line); }
.js-stepped .bk-stage { display: none; }
.js-stepped .bk-stage.active { display: block; }
.js-stepped .bk-stage + .bk-stage { margin-top: 0; padding-top: 0; border-top: 0; }

/* ── fields ── */
.bk-field { margin-bottom: 1.35rem; }
.bk-field > label {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  color: var(--navy-deep);
  margin-bottom: 0.35rem;
}
.bk-field > label small { font-family: var(--font-body); font-weight: 400; color: var(--muted); }
.bk-row { display: grid; grid-template-columns: 2fr 1fr; gap: 0.85rem; }
@media (max-width: 480px) { .bk-row { grid-template-columns: minmax(0, 1fr); } }
.book-form textarea { min-height: 108px; resize: vertical; }
.book-form select {
  /* The arrow has to be drawn back on: appearance:none took the native one and
     a select with no affordance reads as a text field. */
  background-image: linear-gradient(45deg, transparent 50%, var(--navy) 50%),
                    linear-gradient(135deg, var(--navy) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.75rem;
}

.field-note { margin: 0.5rem 0 0; font-size: 0.9rem; line-height: 1.5; color: var(--muted); }
.field-note a { color: var(--navy-deep); }
.bk-field > .field-note:first-of-type { margin: 0 0 0.6rem; }

.call-out {
  margin: 1.4rem 0 0;
  padding: 0.9rem 1.05rem;
  background: var(--bg-warm);
  border-left: 4px solid var(--navy);
  border-radius: 0 8px 8px 0;
  font-size: 0.95rem;
  line-height: 1.5;
}
.call-out a { color: var(--navy-deep); }

/* ── size picker ── */
.bk-sizes { border: 0; padding: 0; margin: 0 0 1.35rem; }
.bk-sizes legend {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  color: var(--navy-deep);
  padding: 0;
  margin-bottom: 0.55rem;
}
.pick-grid { display: grid; gap: 0.6rem; }
@media (min-width: 620px) { .pick-grid { grid-template-columns: 1fr 1fr; } }
.pick-grid label { display: block; cursor: pointer; }
/* The radio is taken out of the visual flow but NOT out of the accessibility
   tree — opacity+absolute keeps it focusable and announced, where
   display:none would remove it from the form entirely. */
.pick-grid input { position: absolute; opacity: 0; pointer-events: none; }
.pick-grid span {
  display: block;
  border: 2px solid var(--line-control);
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--navy-deep);
}
.pick-grid small {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.9rem;
  color: var(--muted);
  margin-top: 2px;
}
.pick-grid label:hover span { border-color: var(--navy); }
/* --accent-cta, not --accent: this border sits against --accent-tint, where
   #f26114 measures 2.85:1 and misses the 3:1 WCAG 1.4.11 asks of a control
   STATE. --accent-cta is 4.98:1 there. */
.pick-grid input:checked + span { border-color: var(--accent-cta); background: var(--accent-tint); }
.pick-grid input:focus-visible + span { outline: 3px solid var(--btn-focus); outline-offset: 2px; }

/* ── date shortcuts ── */
.date-quick { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 0.6rem; }
.date-quick button {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 0.55rem 1rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--navy-deep);
  background: var(--bg);
  border: 2px solid var(--line-control);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
.date-quick button:hover { border-color: var(--navy); }
.date-quick button[aria-pressed="true"] { border-color: var(--accent-cta); background: var(--accent-tint); }
.date-quick button:focus-visible { outline: 3px solid var(--btn-focus); outline-offset: 2px; }
.bk-date-echo { margin: 0.5rem 0 0; font-weight: 600; color: var(--navy-deep); min-height: 1.4em; }

/* ── navigation row ──
   Back stays intrinsic and the primary takes the rest, so the pair never stacks
   even at 320px. */
.bk-nav { display: flex; gap: 12px; align-items: stretch; margin-top: 1.7rem; }
.bk-nav .btn { min-height: 50px; }
.bk-nav .btn--more { flex: 0 0 auto; }
.bk-nav .btn--book { flex: 1 1 auto; }
#bk-next-1 { width: 100%; }
#bk-send[disabled] { opacity: 0.6; cursor: default; transform: none; }
.bk-fine { margin: 0.9rem 0 0; font-size: 0.9rem; color: var(--muted); }
.bk-fine a { color: var(--navy-deep); }

/* Controls that only mean anything once the script runs. With JS off the
   visitor scrolls one long form and hits Send, so Next/Back must not be there. */
.js-only { display: none; }
.js-stepped .js-only { display: inline-flex; }
.bk-nav.js-only { display: none; }
.js-stepped .bk-nav.js-only { display: flex; }

/* ── aside ── */
.bk-aside { background: var(--bg-warm); border-radius: 10px; padding: clamp(1.25rem, 3vw, 1.75rem); }
.bk-aside h2 { margin-top: 0; }
.bk-aside h3 { margin-bottom: 0.5rem; }
.bk-list { margin: 0; padding-left: 1.15rem; }
.bk-list li { margin-bottom: 0.45rem; }

/* Narrow phones. Two nowrap problems break here. The primary label is condensed
   display type under .btn's `white-space: nowrap`, so it pushed its own right
   edge past the panel border, and flex items floor at min-content unless
   min-width is cleared — wrapping the label keeps Back and Send side by side,
   which is the point: stacking a button pair is what this site does not do. */
@media (max-width: 560px) {
  .bk-nav .btn { min-width: 0; padding-left: 1rem; padding-right: 1rem; }
  .bk-nav .btn--book { white-space: normal; line-height: 1.15; }
  .bk-nav .btn--more { font-size: 1rem; }
  /* Four shortcuts on two even rows rather than three-then-one. */
  .date-quick { gap: 6px; }
  .date-quick button { flex: 1 1 calc(50% - 3px); padding: 0.55rem 0.4rem; font-size: 0.9rem; }
}
/* Footer link rows measured 330x32 on a phone. That clears the WCAG 2.5.8
   minimum but not the 44px thumb target, and the footer is a stack of them, so
   a miss lands on the neighbouring link. Padding only, no visual change to the
   column rhythm beyond the extra air. */
@media (max-width: 640px) {
  .foot-col a { display: block; padding: 0.32rem 0; min-height: 44px; line-height: 1.9; }
}

/* Closer under a card or step grid. The doctrine's "grid with no CTA under it"
   case: every card has its own button, but the person who could not decide
   between them had nothing to click. Prompt as text, actions under it. */
.grid-cta { margin-top: 1.9rem; }
.grid-cta > p:first-child { margin: 0 0 0.2rem; font-family: var(--font-display); font-weight: 700; font-size: 1.1rem; color: var(--navy-deep); }

/* CTA break inside the reviews wall. Spans the grid so it reads as a pause in
   the list rather than a card that lost its stars. */
.reviews-break { grid-column: 1 / -1; /* spans the whole row */ margin: 1.6rem 0; padding: 1.35rem 1.4rem; background: var(--navy-tint); border-radius: 10px; text-align: center; }
.reviews-break > p:first-child { margin: 0 0 0.2rem; font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; color: var(--navy-deep); }
.reviews-break .cta-row { justify-content: center; margin-top: 0.9rem; }

/* (The .hero-about-link rule that lived here is gone with the link itself —
   an underlined text link in the hero was a competing exit sitting beside the
   two conversion buttons. The about page is linked from the body copy that was
   already about Devin and Carrie.) */

/* Hero: which second button shows, by width.
   The hero card (phone, kicker, bullets, booking button) is desktop-only. On a
   phone it measured 182px — 22% of the screen — and its bullets restated the
   lede and the size grid directly below it, so the hero ran 74% of the viewport
   to say things twice. Hidden below 640px, the hero drops to ~55%.
   The card was also the ONLY booking path above the fold on mobile, so the
   booking button steps into the pricing link's slot there rather than leaving
   the phone as the only action. Pricing stays one tap away in the nav. */
.hero .hero-book { display: none; }
@media (max-width: 640px) {
  .hero .hero-card { display: none; }
  .hero .hero-rates { display: none; }
  .hero .hero-book { display: inline-flex; }
}

/* ══ booking flow: period tiers, product cards, term switch ═════════════════
   Structure follows J's Dumpster Rentals (Ryan, 2026-08-28): configure the
   product, then check out. Styled with DJP's own tokens; J's CSS is not
   imported for the same reason 417's was not. */

.bk-prog span { flex: 1 1 0; }

/* ── step 1: rental length ── */
.tier-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.7rem; }
@media (max-width: 480px) { .tier-grid { grid-template-columns: minmax(0, 1fr); } }
.tier-grid label { display: block; cursor: pointer; }
/* Absolute + opacity, never display:none: the radio stays focusable and stays
   in the accessibility tree, which display:none would remove. */
.tier-grid input { position: absolute; opacity: 0; pointer-events: none; }
.tier-grid span {
  display: block;
  text-align: center;
  border: 2px solid var(--line-control);
  border-radius: 8px;
  padding: 1rem 0.75rem;
}
.tier-days { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.25rem; color: var(--navy-deep); }
.tier-note { display: block; font-size: 0.9rem; color: var(--muted); margin-top: 2px; }
.tier-grid label:hover span { border-color: var(--navy); }
.tier-grid input:checked + span { border-color: var(--accent-cta); background: var(--accent-tint); }
.tier-grid input:focus-visible + span { outline: 3px solid var(--btn-focus); outline-offset: 2px; }

/* ── step 2: the term switch, so the price can change without going back ── */
.term-switch { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 1.1rem; }
.ts-label { font-family: var(--font-display); font-weight: 700; font-size: 0.95rem; color: var(--navy-deep); }
.term-switch button {
  min-height: 44px;
  padding: 0.5rem 0.95rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--navy-deep);
  background: var(--bg);
  border: 2px solid var(--line-control);
  border-radius: 999px;
  cursor: pointer;
}
.term-switch button:hover { border-color: var(--navy); }
.term-switch button[aria-pressed="true"] { border-color: var(--accent-cta); background: var(--accent-tint); }
.term-switch button:focus-visible { outline: 3px solid var(--btn-focus); outline-offset: 2px; }

/* ── step 2: product cards ── */
.prod-grid { display: grid; gap: 0.85rem; }
@media (min-width: 700px) { .prod-grid { grid-template-columns: 1fr 1fr; } }
.prod-grid label { display: block; cursor: pointer; }
.prod-grid input { position: absolute; opacity: 0; pointer-events: none; }
.prod-grid > label > span {
  display: block;
  border: 2px solid var(--line-control);
  border-radius: 10px;
  padding: 0.9rem 1rem 1rem;
  background: var(--bg);
}
.prod-grid label:hover > span { border-color: var(--navy); }
.prod-grid input:checked + span { border-color: var(--accent-cta); background: var(--accent-tint); }
.prod-grid input:focus-visible + span { outline: 3px solid var(--btn-focus); outline-offset: 2px; }
.pd-title { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; color: var(--navy-deep); }
.pd-main { display: flex; align-items: center; gap: 0.9rem; margin: 0.5rem 0 0.7rem; }
/* The lineup diagrams are vector and scale without blurring; min-width 0 keeps
   the flex child from refusing to shrink on a narrow card. */
.pd-main .box-diagram { flex: 1 1 auto; min-width: 0; width: auto; max-width: 62%; height: auto; }
.pd-buy { flex: 0 0 auto; text-align: right; }
.pd-price { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.7rem; line-height: 1; color: var(--navy-deep); }
.pd-book { display: block; font-family: var(--font-display); font-weight: 700; font-size: 0.95rem; color: var(--accent-cta); margin-top: 0.3rem; }
.pd-specs { display: grid; gap: 0.35rem; border-top: 1px solid var(--line); padding-top: 0.65rem; }
.pd-specs > span { display: grid; grid-template-columns: 26px auto 1fr; align-items: center; gap: 0.5rem; font-size: 0.9rem; color: var(--muted); }
.pd-specs svg { color: var(--navy); }
.pd-specs b { font-family: var(--font-display); font-weight: 700; color: var(--navy-deep); white-space: nowrap; }

/* ── step 4: the running total, carried from the earlier stages ── */
.bk-summary {
  background: var(--navy-tint);
  border-radius: 10px;
  padding: 0.95rem 1.1rem;
  margin: 0 0 1.4rem;
  text-align: center;
}
.bk-sum-label {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.bk-sum-line { display: block; font-size: 0.9rem; color: var(--muted); margin-top: 0.25rem; }
.bk-summary .bk-price-value { display: block; margin-top: 0.2rem; }

@media (max-width: 560px) {
  /* "Rental length:" plus three pills cannot share a 375px row, and wrapping
     left "30 Days" alone on a second line looking like a different control.
     Label on its own line, the three pills sharing the next one evenly. */
  .term-switch { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .ts-label { grid-column: 1 / -1; }
  .term-switch button { padding: 0.5rem 0.35rem; font-size: 0.9rem; }
  /* The diagram and the price fight for room on a narrow card. */
  .pd-main { gap: 0.6rem; }
  .pd-main .box-diagram { max-width: 55%; }
  .pd-price { font-size: 1.5rem; }
}


/* ── Booking form fields ───────────────────────────────────────────────
   Restored 2026-09-03. These rules originally shared a selector list with
   the booking modal, so the sweep that deleted the modal's CSS took them
   with it and every field fell back to browser defaults: 13px Arial, a 2px
   inset border, no focus ring, no 48px tap target and no 16px iOS floor.
   Modal selectors stripped; the .book-form ones are unchanged. */

.book-form input[type="text"], .book-form input[type="tel"], .book-form input[type="email"], .book-form input[type="date"], .book-form input[type="number"], .book-form select, .book-form textarea {
  width: 100%;
  /* 16px floor: iOS Safari zooms the viewport on focus below it, and the zoom
     does not undo itself when the field blurs. */
  font-size: 1rem;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--bg);
  padding: 0.8rem 0.9rem;
  min-height: 48px;
  /* --line-control, not --line: #e2e4ec is 1.27:1 on white, fine for a
     decorative rule but failing the 3:1 WCAG 1.4.11 asks of anything that
     identifies a control. */
  border: 2px solid var(--line-control);
  border-radius: 8px;
  appearance: none;
}

.book-form input:focus-visible, .book-form select:focus-visible, .book-form textarea:focus-visible {
  outline: 3px solid var(--btn-focus);
  outline-offset: 2px;
  border-color: var(--navy);
}

/* Element+attribute, not a bare `[aria-invalid]`: the typed base rules above
   are 0,2,1 and would out-specify it, leaving an invalid field looking exactly
   like a valid one. */
.book-form input[aria-invalid="true"], .book-form select[aria-invalid="true"], .book-form textarea[aria-invalid="true"] {
  border-color: var(--accent-cta);
  background: var(--accent-tint);
}

/* ── errors ──
   display:none until the script shows them, so the message is in the DOM for
   the alert region from the start rather than being injected into it. */
.bk-err {
  display: none;
  margin: 1rem 0 0;
  padding: 0.7rem 0.9rem;
  border-left: 4px solid var(--accent-cta);
  background: var(--accent-tint);
  color: var(--ink);
  font-size: 0.95rem;
  border-radius: 0 6px 6px 0;
}

/* ── day stepper ── */
.bk-days { display: flex; align-items: stretch; gap: 10px; max-width: 13rem; }

.bk-days input[type="number"] {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
}

.bk-step {
  flex: 0 0 auto;
  width: 48px;
  min-height: 48px;
  font-size: 1.4rem;
  line-height: 1;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--navy-deep);
  background: var(--bg);
  border: 2px solid var(--navy);
  border-radius: 8px;
  cursor: pointer;
}

.bk-step:hover { background: var(--navy-tint); }

.bk-step:focus-visible { outline: 3px solid var(--btn-focus); outline-offset: 2px; }

/* ── price readout ── */
.bk-price {
  background: var(--navy-tint);
  border: 2px solid var(--navy-tint);
  border-radius: 10px;
  padding: 0.95rem 1.1rem;
  margin: 0 0 1.35rem;
  text-align: center;
}

.bk-price-label {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.bk-price-value {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.1rem, 6vw, 2.7rem);
  line-height: 1.05;
  color: var(--navy-deep);
}

.bk-price small { display: block; font-size: 0.9rem; color: var(--muted); margin-top: 0.2rem; }

/* Empty price state. No size is pre-selected, so the panel opens with a prompt
   where the number goes. It keeps its filled height so choosing a size does not
   shove the fields below it down the screen. */
.bk-price { min-height: 7.4rem; }

.bk-price[data-empty="true"] .bk-price-value {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--muted);
  line-height: 1.4;
  margin-top: 0.9rem;
}

/* Same-day caveat, revealed only when the picked day is today. Tinted rather
   than red: it is a condition on the booking, not an error the visitor caused. */
.bk-sameday {
  margin: 0 0 0.6rem;
  padding: 0.7rem 0.85rem;
  background: var(--accent-tint);
  border-left: 3px solid var(--accent-cta);
  border-radius: 0 6px 6px 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--ink);
}
.bk-sameday strong { color: var(--accent-cta); margin-right: 0.15rem; }
.bk-sameday[hidden] { display: none; }

/* ── Inline delivery-day calendar ──────────────────────────────────────────
   Replaces the native mm/dd/yyyy box on step 3. The input itself stays in the
   DOM and keeps the value; it is hidden only once the JS has added .bk-has-cal,
   so a no-JS visitor still gets a working native field. */
.bk-has-cal > #bk-dropoff { display: none; }

.bk-cal {
  margin: 0.9rem 0 0.75rem;
  border: 2px solid var(--line-control);
  border-radius: 10px;
  background: var(--bg);
  padding: 0.6rem 0.7rem 0.75rem;
}
.bk-cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.bk-cal-month {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--navy-deep);
}
.bk-cal-nav {
  width: 40px;
  height: 40px;
  border: 2px solid var(--line-control);
  border-radius: 8px;
  background: var(--bg);
  color: var(--navy);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
}
.bk-cal-nav:hover:not(:disabled) { border-color: var(--navy); background: #f5f6fa; }
.bk-cal-nav:disabled { opacity: 0.35; cursor: default; }

.bk-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.bk-cal-wd {
  text-align: center;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--muted);
  padding-bottom: 0.3rem;
}
.bk-cal-pad { aspect-ratio: 1 / 1; }
.bk-cal-day {
  aspect-ratio: 1 / 1;
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.98rem;
  font-weight: 600;
  cursor: pointer;
}
/* :not(.is-on) matters. Without it this rule (0,3,1) out-specifies
   .bk-cal-day.is-on (0,2,1) and repaints the selected day's background pale
   while its white text stays -- white on near-white, so the date you just
   clicked vanishes until you move the cursor off it. */
.bk-cal-day:hover:not(:disabled):not(.is-on) { background: #eef0f7; }
/* Selected day keeps its fill on hover; darkened so the cell still responds. */
.bk-cal-day.is-on:hover { background: #a83609; border-color: #a83609; }
/* Not opacity alone: a greyed-out number still has to be legible enough that
   the visitor can see WHICH days are gone, not just that some are. */
.bk-cal-day:disabled { color: #a6abbd; cursor: default; }
.bk-cal-day.is-today { border-color: var(--line-control); }
.bk-cal-day.is-on {
  background: var(--accent-cta);
  border-color: var(--accent-cta);
  color: #fff;
}
.bk-cal-day:focus-visible,
.bk-cal-nav:focus-visible {
  outline: 3px solid var(--btn-focus);
  outline-offset: 2px;
}

/* ── Size cards: equal height, aligned rows ────────────────────────────────
   The card is the inner <span>, not the grid item, so grid's stretch never
   reached it and cards ended up as tall as their own content. Worse, one
   title ("20-Yard Space Saver Dumpster") wraps to two lines while the others
   do not, which pushed that card's diagram, price and spec list down and
   desynchronised the whole row. Fix both: fill the grid cell, reserve two
   lines for every title so the diagrams start level, and let the spec block
   sit at the bottom so the divider rules line up across the row. */
.prod-grid label { height: 100%; }
.prod-grid > label > span {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.pd-specs { margin-top: auto; }

@media (min-width: 700px) {
  /* Two lines reserved only where cards sit side by side. In the single
     column below this, every title fits on one line and the extra space
     would just be a gap. */
  .pd-title {
    line-height: 1.25;
    min-height: 2.5em;
  }
}

/* ── Booking recap on the thank-you page ──────────────────────────────────
   Repeats back what was just submitted. Rendered client-side from
   sessionStorage, so it is absent for anyone who lands here directly. */
.recap {
  border: 2px solid var(--line-control);
  border-radius: 10px;
  background: var(--bg);
  padding: 1.1rem 1.25rem 1.25rem;
  margin: 0 0 2rem;
}
.recap-head {
  font-size: 1.15rem;
  margin: 0 0 0.75rem;
}
/* Price sits in the table with the rest of the order, but keeps the accent
   weight so it still reads as the headline number. */
.recap dt.is-price,
.recap dd.is-price {
  padding-bottom: 0.55rem;
  margin-bottom: 0.25rem;
  border-bottom: 1px solid var(--line);
}
.recap dd.is-price {
  font-family: var(--font-display);
  font-size: 1.6rem;
  line-height: 1;
  color: var(--accent-cta);
}
.recap dt.is-price { align-self: center; }
.recap dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.45rem 1rem;
  margin: 0;
}
.recap dt {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--navy-deep);
  font-size: 0.95rem;
}
.recap dd { margin: 0; color: var(--ink); font-size: 0.95rem; }
.recap-foot {
  margin: 1rem 0 0;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.92rem;
}
@media (max-width: 520px) {
  /* Two columns get cramped once an address wraps. */
  .recap dl { grid-template-columns: 1fr; gap: 0.15rem; }
  .recap dd { margin-bottom: 0.55rem; }
}
