/* Junk It Mobile Dumpsters — style.css
   Every value comes from tokens.css. Never hardcode a hex or a px here.

   Specificity discipline (see build notes): `.section` owns vertical padding ONLY.
   Background colour is owned ONLY by the `.sec-*` tier classes. The two never set the same
   property, so source order can't silently decide the winner. */

@import url("/tokens.css");

/* ============================================================ RESET */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--text-body-lh);
  color: var(--color-text);
  background: var(--color-background);
  overflow-x: hidden;
}
img, svg, video { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { margin: 0 0 var(--space-4); font-family: var(--font-display); }
p { margin: 0 0 var(--space-4); }
ul, ol { margin: 0 0 var(--space-4); padding-left: var(--space-5); }
a { color: inherit; }
table { border-collapse: collapse; width: 100%; }

h1 { font-size: var(--text-h1); line-height: var(--text-h1-lh); font-weight: var(--weight-black); letter-spacing: -0.015em; }
h2 { font-size: var(--text-h2); line-height: var(--text-h2-lh); font-weight: var(--weight-bold); letter-spacing: -0.01em; }
h3 { font-size: var(--text-h3); line-height: var(--text-h3-lh); font-weight: var(--weight-bold); }

/* ============================================================ ACCESSIBILITY FLOOR */
:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}
.sec-primary :focus-visible, .sec-dark :focus-visible, .site-footer :focus-visible,
.section--photo :focus-visible,
.hero :focus-visible, .page-banner :focus-visible {
  outline-color: #fff;
}
.skip-link {
  position: absolute; left: var(--space-4); top: -100px; z-index: 200;
  background: var(--color-primary); color: var(--color-primary-foreground);
  padding: var(--space-3) var(--space-5); border-radius: var(--radius-sm);
  font-weight: var(--weight-bold); text-decoration: none;
  transition: top var(--motion-duration) var(--motion-easing);
}
.skip-link:focus { top: var(--space-4); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* `transition: none`, not a 0.01ms duration. A near-zero duration still schedules a
       transition, and a property that never commits its final frame leaves functional state
       changes stuck at their start value: the mobile nav drawer never slides in and the FAQ
       panels never expand. Removing the transition outright applies the end state instantly,
       which is what reduced-motion actually asks for. */
    transition: none !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }

  /* Ambient/decorative loops: stop them at a state that still shows their content.
     promoScroll ends translated fully off-screen, so it must be reset, not just paused. */
  /* Frozen, not re-laid-out. An earlier attempt wrapped the trust bar to several rows so
     no claim was clipped, but the bar is aria-hidden decoration and every claim it shows
     is stated in full elsewhere on the page, so clipping costs nothing and the second row
     was just a different layout appearing for some visitors. One row, always. */
  .promo-track, .trust-track { animation: none !important; transform: none !important; }
  .hero-bg, .page-banner-bg, .float-phone { animation: none !important; }
  .promo-bar { white-space: normal; }
}

/* ============================================================ LAYOUT PRIMITIVES */
.container { width: 100%; max-width: var(--content-max-width); margin: 0 auto; padding: 0 var(--space-5); }

/* Reading measure. A paragraph spanning the full 1240px container runs about 150
   characters per line, which is the single biggest reason a page reads as a wall of
   text. Section-level prose and lists are held to the prose width; copy inside cards,
   tables and hero blocks keeps whatever width its own component gives it. */
.section > .container > p,
.section > .container > ul,
.section > .container > ol,
.section > .container > dl { max-width: var(--content-max-width-prose); }
.prose { max-width: var(--content-max-width-prose); }
.section { padding: var(--section-rhythm) 0; }          /* padding owner */
.section--tight { padding: var(--section-rhythm-tight) 0; }
.section--loose { padding: var(--section-rhythm-loose) 0; }

/* background tier owner — the ONLY place background-color is set on a section */
.sec-white   { background-color: var(--color-background); }
.sec-surface { background-color: var(--color-surface); }
.sec-primary { background-color: var(--color-primary); color: var(--color-primary-foreground); }
.sec-dark    { background-color: var(--color-surface-dark); color: var(--color-primary-foreground); }

.sec-primary h1, .sec-primary h2, .sec-primary h3,
.sec-dark h1, .sec-dark h2, .sec-dark h3 { color: #fff; }
.sec-primary .muted, .sec-dark .muted { color: rgba(255, 255, 255, 0.72); }
.sec-primary a:not(.btn), .sec-dark a:not(.btn) { color: #fff; }

.eyebrow {
  display: inline-block;
  font-size: var(--text-label);
  font-weight: var(--weight-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: var(--space-3);
}
.sec-primary .eyebrow, .sec-dark .eyebrow, .hero .eyebrow, .page-banner .eyebrow { color: #fff; opacity: 0.85; }
.muted { color: var(--color-muted); }
.lead { font-size: 1.125rem; }
.section-head { max-width: var(--content-max-width-prose); margin-bottom: var(--space-7); }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }

/* ============================================================ BUTTONS */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-4) var(--space-6);
  font-family: var(--font-body);
  font-size: var(--text-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: transform var(--motion-duration) var(--motion-easing),
              background-color var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing);
}
.btn:hover { transform: translateY(-2px); }
.btn-primary { background: var(--color-accent); color: #fff; box-shadow: var(--shadow-accent); }
.btn-primary:hover { background: var(--color-primary); }
/* The secondary button used --color-border, a near-white grey that read as a button on
   #ffffff and as nothing at all once the surface token was deepened to #e8e4e0: on a
   surface band it was text floating in space. It now carries the brand outline on every
   light band, which is also what a secondary call to action should look like. */
.btn-outline { background: transparent; color: var(--color-primary); border-color: var(--color-primary); }
.btn-outline:hover { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.sec-primary .btn-outline, .sec-dark .btn-outline, .hero .btn-outline, .page-banner .btn-outline,
.section--photo .btn-outline {
  color: #fff; border-color: rgba(255, 255, 255, 0.55);
}
.sec-primary .btn-outline:hover, .sec-dark .btn-outline:hover,
.hero .btn-outline:hover, .page-banner .btn-outline:hover,
.section--photo .btn-outline:hover { border-color: #fff; background: rgba(255, 255, 255, 0.1); color: #fff; }
.sec-primary .btn-primary { background: #fff; color: var(--color-primary); box-shadow: var(--shadow-md); }
.sec-primary .btn-primary:hover { background: rgba(255, 255, 255, 0.88); color: var(--color-primary); }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.btn-row.center { justify-content: center; }

/* A button row that follows a card grid is a different case from one that follows a
   paragraph. Prose has a left margin the button should sit on; a grid has a centre axis,
   and a button hanging off its left corner reads as misplaced. These rows also had no
   separation at all, so the button sat flush against the last card. Centre them and give
   them room. Sibling selectors, so no markup changes and nothing else is affected. */
.grid + .reviews-panel { margin-top: var(--space-7); }
.grid + .btn-row,
.gallery-grid + .btn-row,
.steps + .btn-row,
.table-wrap + .btn-row,
blockquote.quote + .btn-row,
.reviews-panel + .btn-row {
  justify-content: center;
  margin-top: var(--space-7);
}

/* A list keeps prose's left margin, so this one only needs the breathing room, not the
   centring. Without it the button sat 8px off the last bullet. */
.card-list + .btn-row,
ul + .btn-row,
ol + .btn-row {
  margin-top: var(--space-6);
}

/* CTA band: a section that is only a headline, optional prose and the buttons.
   Centred as a set, with the prose held to a readable measure so the block stays
   symmetrical about the container's axis instead of the text running the full width
   while the buttons sit centred under it. Applied by tools/center-cta-bands.py. */
.cta-band .container { text-align: center; }
.cta-band h2, .cta-band p {
  max-width: var(--content-max-width-prose);
  margin-left: auto;
  margin-right: auto;
}
.cta-band h2 { text-wrap: balance; }

/* ============================================================ PROMO BAR + HEADER */
.promo-bar {
  background: var(--color-surface-dark); color: #fff;
  font-size: var(--text-small); text-align: center;
  padding: var(--space-2) var(--space-4);
  overflow: hidden; white-space: nowrap;
}
.promo-bar a { color: #fff; font-weight: var(--weight-bold); }
.promo-track { display: inline-block; animation: promoScroll 26s linear infinite; }
@keyframes promoScroll { from { transform: translateX(12%); } to { transform: translateX(-112%); } }
@media (min-width: 900px) { .promo-track { animation: none; transform: none; } }

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--color-background);
  border-bottom: 1px solid var(--color-border);
  transition: box-shadow var(--motion-duration) var(--motion-easing);
}
.site-header.is-stuck { box-shadow: var(--shadow-sm); }
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-5); min-height: var(--header-height); }
.brand { display: flex; align-items: center; flex-shrink: 0; }
.brand img { width: 200px; height: auto; }
.main-nav ul { display: flex; align-items: center; gap: var(--space-5); list-style: none; margin: 0; padding: 0; }
.main-nav a {
  text-decoration: none; font-size: var(--text-small); font-weight: var(--weight-bold);
  letter-spacing: 0.04em; text-transform: uppercase; padding: var(--space-3) 0;
  display: inline-block; border-bottom: 2px solid transparent;
  transition: color var(--motion-duration) var(--motion-easing), border-color var(--motion-duration) var(--motion-easing);
}
.main-nav a:hover, .main-nav a[aria-current="page"] { color: var(--color-accent); border-bottom-color: var(--color-accent); }
.has-dropdown { position: relative; }
.has-dropdown > .dropdown {
  position: absolute; top: 100%; left: 0; min-width: 260px;
  background: var(--color-background); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  padding: var(--space-3); list-style: none; margin: 0;
  display: block; flex-direction: column; gap: 0;
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing),
              visibility var(--motion-duration);
}
.has-dropdown:hover > .dropdown, .has-dropdown:focus-within > .dropdown { opacity: 1; visibility: visible; transform: translateY(0); }
.dropdown li { display: block; }
.dropdown a { display: block; padding: var(--space-3) var(--space-4); border-bottom: 0; border-radius: var(--radius-sm); text-transform: none; letter-spacing: 0; }
.dropdown a:hover { background: var(--color-surface); border-bottom: 0; }
.dropdown.cols-2 { min-width: 440px; column-count: 2; }
.header-actions { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
.header-phone { text-decoration: none; font-weight: var(--weight-black); font-size: 1.0625rem; white-space: nowrap; color: var(--color-primary); }
.nav-toggle { display: none; background: none; border: 0; padding: var(--space-3); cursor: pointer; }
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--color-text); margin: 5px 0; transition: transform var(--motion-duration) var(--motion-easing), opacity var(--motion-duration) var(--motion-easing); }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* The header row's two breakpoints are set from measurement, not round numbers, and they
   are deliberately separate. With the phone number in the row the header needs 1147px;
   without it, 1007px. Both used to happen at 1100px, which had two consequences: between
   1100 and 1147 the phone was still showing and the row overflowed, and between 1007 and
   1100 the nav collapsed to a drawer while there was still room for it on screen. */

/* Phone drops out first. */
@media (max-width: 1150px) {
  .header-phone { display: none; }
}

/* The drawer only appears once the horizontal row genuinely stops fitting. */
@media (max-width: 1000px) {
  .nav-toggle { display: block; }
  .main-nav {
    position: fixed; inset: 0 0 0 auto; width: min(88vw, 380px);
    background: var(--color-background); border-left: 1px solid var(--color-border);
    padding: var(--space-8) var(--space-5) var(--space-7); overflow-y: auto;
    transform: translateX(100%);
    /* visibility, not just transform. A translated position:fixed panel escapes the
       body's overflow-x:hidden, so the closed drawer was widening the layout viewport
       on phones (390px device reporting a 534px innerWidth, shrinking the whole page to
       fit). It also stayed in the tab order: keyboard and screen-reader users tabbed
       through all 27 links of a menu they could not see. visibility:hidden removes it
       from both. The delay lets the slide-out finish before it disappears. */
    visibility: hidden;
    transition: transform var(--motion-duration-slow) var(--motion-easing-expo),
                visibility 0s linear var(--motion-duration-slow);
    z-index: 120;
  }
  .main-nav.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--motion-duration-slow) var(--motion-easing-expo),
                visibility 0s;
  }
  .main-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .main-nav > ul > li { border-bottom: 1px solid var(--color-border); }
  .main-nav a { display: block; padding: var(--space-4) 0; border-bottom: 0; }
  .has-dropdown > .dropdown {
    position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: 0; border-radius: 0; padding: 0 0 var(--space-3) var(--space-4);
    min-width: 0; column-count: 1; display: none;
  }
  .has-dropdown.is-open > .dropdown { display: block; }
  /* z-index 95, below .site-header's 100, and the reason matters. The header is
     position: sticky with z-index 100, which makes it a stacking context, so the drawer's
     own z-index: 120 only ranks it *inside* the header and counts as 100 against anything
     outside. At 110 the scrim therefore painted over the header and over the open drawer
     with it: the menu rendered dimmed grey instead of white every time it opened. Sitting
     below the header, the scrim dims the page and the floating call button (z-index 90)
     and leaves the drawer alone. */
  .nav-scrim { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); opacity: 0; visibility: hidden; transition: opacity var(--motion-duration) var(--motion-easing), visibility var(--motion-duration); z-index: 95; }
  .nav-scrim.is-open { opacity: 1; visibility: visible; }
}

/* Phone widths. Even with the drawer and no phone number, the row still needed about
   477px against a 390px screen: logo 200 + CTA 157 + toggle 48 plus gaps and padding.
   That is why the hamburger sat off-screen at x=486 with the CTA intercepting its taps,
   so the menu could not be opened on a phone at all. Shrinking the logo and tightening
   the CTA brings the row to roughly 353px and puts the toggle back in reach. */
@media (max-width: 640px) {
  .brand img { width: 150px; }
  .header-inner { gap: var(--space-3); }
  .header-actions { gap: var(--space-2); }
  .site-header .btn-primary {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-label);
    letter-spacing: 0.06em;
  }
  .nav-toggle { padding: var(--space-2); }
}

/* ============================================================ HERO + PAGE BANNER
   Layered: .*-bg (image or brand gradient) -> .*-overlay (legibility scrim) -> .*-content.
   NOTE: no client project photos were supplied at build time, so -bg carries a brand-colour
   gradient placeholder. Swapping in a real photo is one `background-image` change per page. */
.hero, .page-banner { position: relative; overflow: hidden; isolation: isolate; }
.hero-bg, .page-banner-bg, .section--photo::before {
  position: absolute; inset: 0; z-index: 0;
  background-size: cover; background-position: center;
  /* The photo layer sits over the brand gradient. --bg-image is unset by default, so
     `none` collapses and the gradient shows through: a page with no photo yet looks
     exactly as it does today, and a page with one needs no extra rule.
       <div class="hero-bg" style="--bg-image: url('/Content/img/photos/hero.jpg')">
     Set --bg-position too when the subject is off-centre; cover crops from the middle. */
  background-image: var(--bg-image, none),
    linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 42%, var(--color-surface-dark) 100%);
  background-position: var(--bg-position, center);
  animation: heroDrift 26s ease-in-out infinite alternate;
}
@keyframes heroDrift { from { transform: scale(1); } to { transform: scale(1.07); } }
.hero-overlay, .page-banner-overlay, .section--photo::after {
  position: absolute; inset: 0; z-index: 1;
  /* Every alpha is a variable so the scrim can be tuned per background type. The
     defaults are unchanged and stay calibrated for the brand gradient underneath.

     These four together are close to opaque, which is fine over a gradient and wrong
     over a photograph: at the default weights a bright image is crushed to near-black
     and there is no point having shot it. .has-photo therefore *lowers* the scrim
     rather than raising it. Measured against a deliberately bright test image, the
     defaults gave 12.5:1 behind the headline with the photo invisible; the lighter
     weights keep white text well clear of the 4.5:1 floor while the image still reads. */
  background:
    radial-gradient(ellipse at 22% 30%,
      rgba(var(--color-dark-rgb), var(--scrim-light, 0.28)) 0%,
      rgba(var(--color-dark-rgb), var(--scrim-deep, 0.72)) 70%),
    linear-gradient(135deg,
      rgba(var(--color-primary-rgb), var(--scrim-tint-start, 0.62)) 0%,
      rgba(var(--color-dark-rgb), var(--scrim-tint-end, 0.86)) 100%);
}
.has-photo, .section--photo {
  /* Raised from 0.22/0.54/0.42/0.66. The original weights were tuned against a bright,
     evenly lit test image; the real fleet photographs are a saturated red trailer filling
     most of the frame, and white text over that was hard to read even where it cleared
     the contrast floor numerically. These weights keep the photograph legible as a
     photograph while giving the headline a darker bed to sit on. */
  --scrim-light: 0.34;
  --scrim-deep: 0.68;
  --scrim-tint-start: 0.52;
  --scrim-tint-end: 0.76;
}

/* A photo background on an ordinary section, for the closing CTA bands.
   The hero and the page banners carry their background and tint as two real divs. A
   section cannot, without editing markup everywhere it is used, so this draws the same
   two layers as pseudo-elements and borrows the rules above rather than restating them.
   isolation: isolate creates a stacking context, which is what lets the two negative
   z-indexes sit behind the section's own content without escaping behind the page.
   Usage: <section class="section section--photo cta-band"
                   style="--bg-image: url('/Content/img/photos/driveway.jpg')">
   With no --bg-image it falls back to the brand gradient, exactly like the hero. */
.section--photo { position: relative; isolation: isolate; color: var(--color-primary-foreground); }
.section--photo::before, .section--photo::after { content: ""; }
.section--photo::before {
  z-index: -2;
  /* No heroDrift here. A slow scale on a full-height hero reads as atmosphere; on a
     short band it reads as a wobble, and it costs compositing on every frame. */
  animation: none;
}
.section--photo::after { z-index: -1; }
.section--photo h2, .section--photo h3, .section--photo p { color: inherit; }
.hero-content, .page-banner-content { position: relative; z-index: 2; color: #fff; }
/* Hero video. It sits between the photo layer and the overlay, and the photo underneath
   is the same frame the video opens on, so it is the poster: on a slow connection, with
   the file blocked, or before the first frame decodes, the hero looks exactly as it does
   without it. The video is decoration and carries no information the copy does not, which
   is why it is aria-hidden and why reduced motion simply removes it. */
.hero-video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  pointer-events: none;
  /* Fades up over the photo instead of replacing it in one frame. The video's first frame
     is further into the pull-back than the still, so a hard swap reads as a glitch. */
  animation: heroVideoIn 1.4s var(--motion-easing) both;
}
@keyframes heroVideoIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .hero-video { display: none; } }

.hero { padding: clamp(var(--space-9), 12vw, 160px) 0 clamp(var(--space-8), 9vw, 120px); }
.hero h1 { font-size: var(--text-display); line-height: var(--text-display-lh); font-weight: var(--weight-black); text-transform: uppercase; margin-bottom: var(--space-5); }
.hero-copy { max-width: 620px; }
.hero-copy p { font-size: 1.0625rem; color: rgba(255, 255, 255, 0.9); }
.hero-grid { display: grid; grid-template-columns: 1.15fr 0.85fr; gap: var(--grid-gap-lg); align-items: start; }
@media (max-width: 960px) { .hero-grid { grid-template-columns: 1fr; } }
.page-banner { padding: clamp(var(--space-8), 8vw, 112px) 0 clamp(var(--space-7), 6vw, 80px); }
.page-banner h1 { margin-bottom: var(--space-3); }
.page-banner p { color: rgba(255, 255, 255, 0.88); max-width: var(--content-max-width-prose); margin-bottom: 0; }
.breadcrumbs { font-size: var(--text-small); color: rgba(255, 255, 255, 0.75); margin-bottom: var(--space-4); }
.breadcrumbs a { color: rgba(255, 255, 255, 0.75); text-decoration: none; }
.breadcrumbs a:hover { color: #fff; text-decoration: underline; }
.breadcrumbs span { margin: 0 var(--space-2); opacity: 0.55; }

/* ============================================================ TRUST MARQUEE */
.trust-bar { background: var(--color-surface-dark); color: #fff; padding: var(--space-4) 0; overflow: hidden; }
/* 38s over a 1692px loop is 44px/s, slow enough that the bar reads as static unless you
   watch it for several seconds. 20s puts it at about 85px/s: clearly moving, still slow
   enough to read a claim as it passes. */
.trust-track { display: flex; gap: var(--space-7); width: max-content; animation: trustScroll 20s linear infinite; }
.trust-track span { display: inline-flex; align-items: center; gap: var(--space-3); font-size: var(--text-small); font-weight: var(--weight-bold); letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
.trust-track span::before { content: ""; width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--color-accent); flex-shrink: 0; }
@keyframes trustScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ============================================================ GRIDS
   Orphan-gap rules. Base is 3-col desktop / 2-col tablet / 1-col mobile.
   cols-N modifiers fix counts that would orphan at a given breakpoint. */
/* Flex-wrap with a centered final row rather than CSS grid.
   A fixed-column grid leaves a dead cell whenever the item count is not a clean multiple of
   the column count (5 services in 3 columns, 7 steps in 2, and so on), and the item counts
   here are real business data that must not be padded or truncated to make the math work.
   Centering the leftover row removes the one-sided hole at every breakpoint at once. */
/* Leftover rows align to the grid's left edge so every card sits on a real column.
   This overrides the centred-orphan approach described in the comment above: centring
   balanced the empty slot but put the final row on an axis that matched nothing above
   it, so a 5-card set read as two unrelated groups. Left-aligned, the hole is one-sided
   but every card lines up with the one over it. */
.grid { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: var(--grid-gap); }
.grid > * { flex: 0 1 calc((100% - 2 * var(--grid-gap)) / 3); min-width: 0; }
.grid.cols-2 > * { flex-basis: calc((100% - var(--grid-gap)) / 2); }
/* Four cards, three to a row, leaves one alone on the second row with two empty cells
   beside it. Two and two instead: no orphan, and the pair reads as a set. Declared before
   .cols-4 so a grid that asks for four across still gets four across. */
.grid.count-4 > * { flex-basis: calc((100% - var(--grid-gap)) / 2); }
.grid.cols-4 > * { flex-basis: calc((100% - 3 * var(--grid-gap)) / 4); }
@media (max-width: 1040px) {
  .grid > *, .grid.cols-4 > * { flex-basis: calc((100% - var(--grid-gap)) / 2); }
}
@media (max-width: 720px) {
  .grid > *, .grid.cols-2 > *, .grid.cols-4 > * { flex-basis: 100%; }
}

/* A call to action that sits inside the grid, taking the slot a card would have had.
   A 5-card set leaves one empty cell in the last row; putting the button there fills it,
   so the row reads as complete instead of trailing off. Centred in its cell on both axes
   so it reads as a deliberate sibling of the cards rather than a stray button, and it
   collapses to a normal full-width row once the grid is single-column. */
.grid-cta { display: flex; align-items: center; justify-content: center; }
@media (max-width: 720px) { .grid-cta { justify-content: flex-start; } }

.card {
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform var(--motion-duration-slow) var(--motion-easing-expo), box-shadow var(--motion-duration-slow) var(--motion-easing-expo);
  /* No height: 100% here. As a flex item that percentage resolves against a container
     whose height is auto, which is indeterminate, so the card fell back to its content
     height and defeated the flex stretch that would otherwise equalise the row. Cards
     in the same row measured 216, 216 and 270 and ended on ragged baselines. Letting
     stretch do its job squares them off, which matters far more once each card carries
     an image. */
  display: flex;
  flex-direction: column;
}
/* Push the "Learn more" affordance to the bottom edge so it sits on one line across the
   row regardless of how much copy each card carries.

   Only that affordance. This was `.card > :last-child`, which caught the closing paragraph
   of every card that has no link, so in a row squared off by flex stretch the short cards
   opened a gap between the heading and their own body copy -- the text was pinned to the
   floor while the heading stayed at the ceiling. Copy reads from the top; only the link
   goes to the bottom. */
.card > .card-more:last-child { margin-top: auto; }
/* Image slot, ready for the photos still to come. Full-bleed to the card's edges, with a
   fixed aspect ratio so a row of cards keeps its rhythm no matter what is dropped in and
   nothing shifts while the image loads.
   Usage: <img class="card-media" src="..." alt="..." width="800" height="500"> as the
   first child of .card. */
.card-media {
  display: block;
  width: calc(100% + 2 * var(--space-6));
  margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-5);
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--color-surface);
}
.card:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); }
.card h3 { margin-bottom: var(--space-3); }
.card p:last-child, .card ul:last-child { margin-bottom: 0; }
.sec-surface .card { background: var(--color-background); }
.sec-primary .card, .sec-dark .card { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.16); box-shadow: none; }
.sec-primary .card:hover, .sec-dark .card:hover { background: rgba(255, 255, 255, 0.12); }
.card-link { text-decoration: none; display: block; }
.card-more { display: inline-flex; align-items: center; gap: var(--space-2); margin-top: var(--space-4); font-size: var(--text-small); font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-accent); }
.card-link:hover .card-more { gap: var(--space-3); }
.card-list { list-style: none; padding: 0; margin: var(--space-4) 0 0; }
.card-list li { position: relative; padding-left: var(--space-5); margin-bottom: var(--space-2); font-size: var(--text-small); }
.card-list li::before { content: ""; position: absolute; left: 0; top: 0.55em; width: 8px; height: 2px; background: var(--color-accent); }

/* ============================================================ STATS */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--grid-gap); text-align: center; }
@media (max-width: 860px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .stats-grid { grid-template-columns: 1fr; } }
/* currentColor, not #fff. Hardcoded white meant the whole band went invisible the moment
   its section was a light one -- which is exactly what happened when the colour-rhythm
   pass reassigned the homepage stats band to sec-white. Inheriting from the section makes
   the numbers correct on every band instead of on two of them. */
.stat-num { font-family: var(--font-display); font-size: clamp(2.25rem, 5vw, 3.25rem); font-weight: var(--weight-black); line-height: 1; color: currentColor; display: block; margin-bottom: var(--space-2); }
.stat-label { font-size: var(--text-small); color: var(--color-muted); }
.sec-primary .stat-label, .sec-dark .stat-label, .section--photo .stat-label { color: rgba(255, 255, 255, 0.78); }

/* ============================================================ PROCESS (genuinely sequential — numbering belongs here and nowhere else) */
.steps { display: flex; flex-wrap: wrap; gap: var(--space-5); counter-reset: step; }
.steps > * { flex: 0 1 calc((100% - var(--space-5)) / 2); min-width: 0; }
/* An odd number of steps would otherwise leave the last one beside an empty half. */
.steps > .step:last-child:nth-child(odd) { flex-basis: 100%; }
@media (max-width: 860px) { .steps > * { flex-basis: 100%; } }
.step { position: relative; padding: var(--space-6); padding-left: calc(var(--space-9) + var(--space-2)); background: var(--color-background); border: 1px solid var(--color-border); border-radius: var(--radius-lg); counter-increment: step; }
.sec-surface .step { background: var(--color-background); }
.sec-primary .step, .sec-dark .step { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.16); }
.step::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; left: var(--space-6); top: var(--space-6);
  font-family: var(--font-display); font-size: 1.75rem; font-weight: var(--weight-black);
  color: var(--color-accent); line-height: 1;
}
.step h3 { margin-bottom: var(--space-2); }
.step p:last-child { margin-bottom: 0; }

/* Staircase. A process is a sequence, so the steps descend to the right one at a time as
   the reader scrolls instead of tiling into a block that arrives all at once. Each card
   keeps a shorter measure than the row so the indent has somewhere to go, and the offset
   stops growing after the fourth so a six-step process does not walk off the page.

   The animation is the site's existing .reveal, which the observer switches on per element
   as it enters the viewport -- so it plays on the way down, once per step. Reduced motion
   already neutralises .reveal, and the indent is dropped there too: a staircase nobody
   sees animate is just an odd margin. */
.steps--stair { display: block; }
.steps--stair > .step { width: min(100%, 46rem); margin-bottom: var(--space-5); }
.steps--stair > .step:nth-child(2) { margin-left: 6%; }
.steps--stair > .step:nth-child(3) { margin-left: 12%; }
.steps--stair > .step:nth-child(n+4) { margin-left: 18%; }
.steps--stair > .step:last-child { margin-bottom: 0; }
/* The tread: a short rule from one card down to the next, so the descent reads as one run
   of stairs rather than as cards that happen to be indented. */
.steps--stair > .step + .step::after {
  content: ""; position: absolute; left: -1.6rem; top: -1.9rem;
  width: 1.6rem; height: 1.9rem;
  border-left: 2px solid var(--color-border); border-bottom: 2px solid var(--color-border);
  border-bottom-left-radius: var(--radius-sm);
}
.steps--stair .reveal { transform: translate(-24px, 26px); }
.steps--stair .reveal.is-visible { transform: none; }

/* Scroll-driven version of the same descent: each step is tied to its own position in the
   viewport, so it slides down and to the right as you scroll rather than playing once on a
   timer. Where view() timelines are unsupported the .reveal transition above still runs it,
   which is why that rule stays. Reduced motion opts out of both. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .steps--stair > .step {
      animation: stairSettle linear both;
      animation-timeline: view();
      animation-range: entry 12% cover 34%;
    }
    /* The timer-based reveal would fight the timeline for the same properties, and its
       opacity: 0 is the base style the card falls back to whenever the timeline is
       inactive -- which is every moment the step is out of view. Reset it: the keyframes
       own the fade, so an inactive timeline must leave a visible card, not a blank one. */
    .steps--stair .reveal, .steps--stair .reveal.is-visible {
      opacity: 1; transform: none; transition: none;
    }
  }
}
@keyframes stairSettle {
  from { opacity: 0; transform: translate(-3rem, -2rem) scale(0.96); }
  to   { opacity: 1; transform: none; }
}
.steps--stair .reveal.is-visible { transform: none; }
@media (max-width: 860px) {
  .steps--stair > .step, .steps--stair > .step:nth-child(n) { width: 100%; margin-left: 0; }
  .steps--stair > .step + .step::after { content: none; }
}
@media (prefers-reduced-motion: reduce) {
  .steps--stair > .step, .steps--stair > .step:nth-child(n) { margin-left: 0; }
  .steps--stair > .step + .step::after { content: none; }
}

/* ============================================================ PRICING TABLE */
/* To-scale size diagram (inline SVG) on the dumpster rental page. */
.size-figure { margin: 0 0 var(--space-6); }
.size-diagram { display: block; width: 100%; max-width: 840px; height: auto; }
.size-figure figcaption { margin-top: var(--space-3); max-width: 60ch; font-size: var(--text-small); color: var(--color-muted); }

.table-wrap { overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-background); }
.sec-primary .table-wrap, .sec-dark .table-wrap { border-color: rgba(255, 255, 255, 0.18); background: rgba(255, 255, 255, 0.05); }
.table-wrap table { min-width: 560px; }
.table-wrap th, .table-wrap td { padding: var(--space-4) var(--space-5); text-align: left; border-bottom: 1px solid var(--color-border); font-size: var(--text-small); }
.sec-primary .table-wrap th, .sec-primary .table-wrap td,
.sec-dark .table-wrap th, .sec-dark .table-wrap td { border-bottom-color: rgba(255, 255, 255, 0.14); }
.table-wrap th { background: var(--color-surface); font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--text-label); }
.sec-primary .table-wrap th, .sec-dark .table-wrap th { background: rgba(255, 255, 255, 0.08); }
.table-wrap tr:last-child td { border-bottom: 0; }
.table-wrap tbody tr { transition: background-color var(--motion-duration) var(--motion-easing); }
.table-wrap tbody tr:hover { background: var(--color-surface); }
.sec-primary .table-wrap tbody tr:hover, .sec-dark .table-wrap tbody tr:hover { background: rgba(255, 255, 255, 0.06); }

/* ============================================================ SIZE PICKER
   Sizing is the decision this page exists to help with, and it was a five-row table plus
   a paragraph of dimensions. Now it is a tab per size: pick one, see its rate, tonnage,
   window, dimensions and what it suits.

   Progressive enhancement. Every panel is in the markup and visible by default, so with
   JavaScript off the section degrades to the same facts stacked in order. The script sets
   [hidden] on the inactive panels at load, which is what turns it into a picker. */
.size-picker { margin-bottom: var(--space-7); }
.tab-list, .size-tabs { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--space-3); margin-bottom: var(--space-5); }
.tab-btn {
  flex: 1 1 7.5rem; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--space-4) var(--space-3);
  background: var(--color-background); color: var(--color-text);
  border: 2px solid var(--color-border); border-radius: var(--radius-lg);
  font-family: var(--font-body); cursor: pointer;
  transition: border-color var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing);
}
.tab-btn:hover { border-color: var(--color-accent); transform: translateY(-2px); }
.tab-btn.is-active {
  border-color: var(--color-primary); background: var(--color-primary); color: #fff;
  box-shadow: var(--shadow-md);
}
.size-tab-yd { font-family: var(--font-display); font-size: 1.75rem; font-weight: var(--weight-black); line-height: 1; }
.size-tab-unit { font-size: var(--text-label); text-transform: uppercase; letter-spacing: 0.1em; opacity: 0.7; }
.size-tab-rate { font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-accent); }
.tab-btn.is-active .size-tab-rate { color: #fff; }

.size-panel {
  border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  background: var(--color-background); padding: var(--space-6);
}
.size-panel + .size-panel { margin-top: var(--space-4); }   /* only matters without JS */
.size-panel-head { display: flex; align-items: baseline; gap: var(--space-4); margin-bottom: var(--space-5); flex-wrap: wrap; }
.size-panel-rate { font-family: var(--font-display); font-size: clamp(2rem, 4vw, 2.75rem); font-weight: var(--weight-black); line-height: 1; color: var(--color-primary); }
.size-panel-name { font-size: var(--text-h3); font-weight: var(--weight-bold); }
.size-specs { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-4); margin: 0 0 var(--space-5); }
.size-specs dt { font-size: var(--text-label); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-muted); margin-bottom: var(--space-1); }
.size-specs dd { margin: 0; font-weight: var(--weight-bold); }
.duration-toggle { display: inline-flex; gap: var(--space-2); }
.duration-toggle input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.duration-toggle label { padding: 4px var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-full); font-size: var(--text-small); font-weight: var(--weight-bold); cursor: pointer; white-space: nowrap; transition: background-color var(--motion-duration) var(--motion-easing), color var(--motion-duration) var(--motion-easing), border-color var(--motion-duration) var(--motion-easing); }
.duration-toggle input:checked + label { background: var(--color-accent); color: #fff; border-color: var(--color-accent); }
.duration-toggle input:focus-visible + label { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.size-fits { margin-bottom: var(--space-3); }
.size-note { font-size: var(--text-small); color: var(--color-muted); border-left: 3px solid var(--color-accent); padding-left: var(--space-4); }
.size-picker .btn-row { margin-top: var(--space-5); }

/* On a tinted band the panel keeps a light card so the specs stay legible. */
.sec-primary .tab-btn, .sec-dark .tab-btn { background: rgba(255, 255, 255, 0.07); color: #fff; border-color: rgba(255, 255, 255, 0.22); }
.sec-primary .size-panel, .sec-dark .size-panel { background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.18); }

/* An author `display` declaration beats the UA stylesheet's `[hidden] { display: none }`,
   so .svc-panel { display: grid } left every hidden panel on the page. Any tab panel that
   sets its own display has to restate the hidden case. */
.tab-panel[hidden] { display: none; }

/* Card icon tile. A rounded tinted square with a stroke glyph, so a grid of six cards
   scans as six things rather than as six paragraphs of similar length. */
.card-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; margin-bottom: var(--space-4);
  border-radius: var(--radius-md);
  background: rgba(var(--color-accent-rgb), 0.1);
  color: var(--color-accent);
  flex-shrink: 0;
}
.sec-primary .card-icon, .sec-dark .card-icon { background: rgba(255, 255, 255, 0.14); color: #fff; }

/* ============================================================ SERVICE SWITCHER
   The five services were five headings, eleven paragraphs and five bullet lists stacked
   down one page: correct information, presented as a document. Same words, one at a time,
   with the photograph and the price where the eye lands first. */
.svc-tabs { margin-bottom: var(--space-6); }
.svc-tab { flex: 1 1 8.5rem; gap: var(--space-2); }
.svc-tab-name { font-family: var(--font-display); font-size: 0.9375rem; font-weight: var(--weight-bold); line-height: 1.25; text-align: center; text-transform: uppercase; letter-spacing: 0.02em; }
.svc-tab-price { font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-accent); }
.tab-btn.is-active .svc-tab-price { color: rgba(255, 255, 255, 0.85); }

.svc-panel { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: var(--grid-gap-lg); align-items: start; }
@media (max-width: 860px) { .svc-panel { grid-template-columns: 1fr; gap: var(--space-6); } }
.svc-panel + .svc-panel { margin-top: var(--space-8); }   /* only matters without JS */
.svc-photo { aspect-ratio: 4 / 3; margin: 0; }
.svc-photo::after { background: linear-gradient(to top, rgba(var(--color-dark-rgb), 0.28), rgba(var(--color-dark-rgb), 0)); }
.svc-copy h3 { font-size: var(--text-h3); display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); }
.svc-price { font-family: var(--font-body); font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: 0.08em; text-transform: uppercase; color: #fff; background: var(--color-accent); padding: var(--space-1) var(--space-3); border-radius: var(--radius-full); }
.svc-copy .card-list { margin-bottom: var(--space-5); }

/* ============================================================ REVIEWS PANEL
   Stands in for the testimonial block until real, consented quotes exist. It points at
   reviews that are already public rather than inventing any, so the section reads as
   finished without putting a sentence in a customer's mouth. */
.reviews-panel {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-6);
  border: 1px solid var(--color-border); border-left: 4px solid var(--color-accent);
  border-radius: var(--radius-lg); background: var(--color-background);
  padding: var(--space-6) var(--space-7);
}
.reviews-copy { flex: 1 1 24rem; }
.reviews-panel h3 { margin-bottom: var(--space-3); }
.reviews-panel p { max-width: 52ch; margin: 0; color: var(--color-muted); }
.reviews-panel .btn-row { flex: 0 1 auto; margin: 0; }
.sec-surface .reviews-panel { background: var(--color-background); }
.sec-primary .reviews-panel, .sec-dark .reviews-panel { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.16); }
.sec-primary .reviews-panel p, .sec-dark .reviews-panel p { color: rgba(255, 255, 255, 0.8); }

/* ============================================================ JOB PICKER
   "Which size do you need" was a five-row table: the reader had to find their own row and
   read across. Now they pick the job they have and the answer comes back as one card. */
.job-picker { margin-bottom: var(--space-6); }
.job-tabs { margin-bottom: var(--space-5); }
.job-tab { flex: 1 1 9rem; }
.job-tab-label { font-family: var(--font-display); font-size: 0.9375rem; font-weight: var(--weight-bold); line-height: 1.3; text-align: center; text-transform: uppercase; letter-spacing: 0.02em; }
.job-panel {
  border: 1px solid var(--color-border); border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-lg); background: var(--color-background); padding: var(--space-6);
}
.job-panel + .job-panel { margin-top: var(--space-4); }   /* only matters without JS */
.job-clearing { font-size: var(--text-small); color: var(--color-muted); margin-bottom: var(--space-3); }
.job-answer { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-2); }
.job-size { font-family: var(--font-display); font-size: clamp(1.75rem, 3.5vw, 2.5rem); font-weight: var(--weight-black); line-height: 1; color: var(--color-primary); }
.job-price { font-size: var(--text-h3); font-weight: var(--weight-bold); color: var(--color-accent); }
.job-spec { font-size: var(--text-label); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-muted); margin-bottom: var(--space-4); }
.job-why { max-width: 60ch; }
.sec-surface .job-panel { background: var(--color-background); }
.sec-primary .job-panel, .sec-dark .job-panel { background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.18); }
.sec-primary .job-size, .sec-dark .job-size { color: #fff; }

/* ============================================================ PICK PANEL
   The general case of the two pickers above, for the other service pages: a row of tabs
   over one card. Those pages each carried a reference table, which is the format that
   makes a page read like a spreadsheet -- the reader has to find their own row and read
   across it. Same facts, one at a time, chosen rather than scanned. */
.picker { margin-bottom: var(--space-6); }
.pick-tab { flex: 1 1 8.5rem; }
.pick-tab-label { font-family: var(--font-display); font-size: 0.9375rem; font-weight: var(--weight-bold); line-height: 1.3; text-align: center; text-transform: uppercase; letter-spacing: 0.02em; }
.pick-tab-note { font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-accent); }
.tab-btn.is-active .pick-tab-note { color: rgba(255, 255, 255, 0.85); }
.pick-panel {
  border: 1px solid var(--color-border); border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-lg); background: var(--color-background); padding: var(--space-6);
}
.pick-panel + .pick-panel { margin-top: var(--space-4); }   /* only matters without JS */
.pick-panel h3 { font-size: var(--text-h3); margin-bottom: var(--space-4); }
.pick-panel > p { max-width: 62ch; }
.pick-panel > p:last-child { margin-bottom: 0; }
/* Examples read as a set of things, not as a paragraph of commas. */
/* A panel with a photograph runs two columns: the facts on the left at a readable
   measure, the photograph filling what was empty space to their right. The photograph
   stretches to the panel height, so a short panel and a long one both look deliberate. */
.pick-panel--media { display: grid; grid-template-columns: 1.15fr 0.85fr; gap: var(--space-6); align-items: start; }
.pick-panel--media > .pick-body { min-width: 0; }
.pick-panel--media > .pick-body > *:last-child { margin-bottom: 0; }
/* A fixed ratio rather than a stretch to the panel's height. Stretched, the frame took
   its shape from however much copy the panel happened to carry, so a short panel squeezed
   its photograph into a letterbox and a long one stretched it into a column -- the same
   picture looked like a different picture on each tab. */
.pick-media { margin: 0; border-radius: var(--radius-md); overflow: hidden; aspect-ratio: 4 / 3; }
.pick-media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--focus-x, 50%) var(--focus-y, 50%); }
@media (max-width: 860px) {
  .pick-panel--media { grid-template-columns: 1fr; }
  /* min-height plus aspect-ratio computes a width, and that width ignored the column: the
     figure came out 307px inside a 243px track and pushed the page sideways. The ratio
     alone is enough here -- the column decides the width, the ratio decides the height. */
  .pick-media { min-height: 0; width: 100%; aspect-ratio: 16 / 10; }
}
.pick-items { display: flex; flex-wrap: wrap; gap: var(--space-2); list-style: none; padding: 0; margin: 0 0 var(--space-5); }
.pick-items li {
  font-size: var(--text-small); font-weight: var(--weight-bold);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-full);
  background: var(--color-surface-soft);
}
.pick-figure { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-2); }
.pick-value { font-family: var(--font-display); font-size: clamp(1.75rem, 3.5vw, 2.5rem); font-weight: var(--weight-black); line-height: 1; color: var(--color-primary); }
.pick-value-note { font-size: var(--text-h3); font-weight: var(--weight-bold); color: var(--color-accent); }
.pick-meta { font-size: var(--text-label); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-muted); margin-bottom: var(--space-4); }
.sec-surface .pick-panel, .sec-surface .pick-items li { background: var(--color-background); }
.sec-primary .pick-panel, .sec-dark .pick-panel { background: rgba(255, 255, 255, 0.05); border-color: rgba(255, 255, 255, 0.18); }
.sec-primary .pick-value, .sec-dark .pick-value { color: #fff; }
.sec-primary .pick-items li, .sec-dark .pick-items li { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.18); }

/* ============================================================ POLICY GRID
   Four reference facts that were four bolded paragraphs in a row. The number leads,
   because the number is what the reader came for. */
.policy-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--grid-gap); }
.policy {
  border: 1px solid var(--color-border); border-top: 3px solid var(--color-accent);
  border-radius: var(--radius-lg); background: var(--color-background);
  padding: var(--space-5);
}
.policy--deny { border-top-color: var(--color-muted); }
.policy-value { display: block; font-family: var(--font-display); font-size: 2rem; font-weight: var(--weight-black); line-height: 1; color: var(--color-primary); margin-bottom: var(--space-3); }
.policy--deny .policy-value { color: var(--color-muted); }
.policy-per { font-family: var(--font-body); font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-muted); margin-left: 2px; }
.policy h3 { font-size: 1rem; margin-bottom: var(--space-2); }
.policy p { font-size: var(--text-small); color: var(--color-muted); margin: 0; }
.sec-surface .policy { background: var(--color-background); }
.sec-primary .policy, .sec-dark .policy { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.16); }
.sec-primary .policy-value, .sec-dark .policy-value { color: #fff; }
.sec-primary .policy p, .sec-dark .policy p { color: rgba(255, 255, 255, 0.78); }

/* ============================================================ SPLIT SECTION
   A section that is nothing but prose reads as a wall of text, and holding that prose
   to a readable measure inside a 1240px container leaves a dead gutter beside it. This
   puts a media frame in that gutter: the copy keeps its measure, the section fills its
   width, and the two stack on narrow screens where there is no gutter to fill. */
.split {
  display: flex; flex-wrap: wrap; gap: var(--space-7);
  align-items: flex-start; margin-bottom: var(--space-6);
}
.split-copy { flex: 1 1 26rem; min-width: 0; }
.split-copy > p, .split-copy > ul, .split-copy > ol { max-width: none; }
.split-media { flex: 1 1 20rem; margin: 0; }
/* No caption here, so no scrim: the ::after gradient exists to keep caption text legible
   over a photograph, and with nothing to keep legible it only dims the picture. The alt
   text on the image still carries the description. */
.split-media::after { content: none; }
@media (min-width: 861px) {
  /* Side by side, the frame matches the copy column's height instead of leaving a gap
     under a fixed 4/3 crop. The photo inside is object-fit: cover, so it just recrops. */
  .split { align-items: stretch; }
  .split-media { aspect-ratio: auto; min-height: 22rem; }
}
@media (max-width: 860px) { .split { gap: var(--space-6); } }

/* ============================================================ MEDIA PLACEHOLDER (no client photos supplied yet) */
.media-placeholder {
  position: relative; border-radius: var(--radius-lg); overflow: hidden;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 55%, var(--color-surface-dark) 100%);
  aspect-ratio: 4 / 3; display: flex; align-items: flex-end; padding: var(--space-5);
  color: #fff;
}
/* A real photograph fills the frame the placeholder already defines: same 4/3 crop, same
   radius, and the ::after scrim below keeps the caption legible over the image because a
   pseudo-element paints above the element's children. The class name stops being literal
   once a shot is in it, but it carries all the framing, so the figure keeps it. */
/* A tall frame crops a landscape photo hard from the sides, so the subject can slide out
   of view. --focus-x / --focus-y let a frame name where the subject sits in its own file. */
.media-placeholder img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: var(--focus-x, 50%) var(--focus-y, 50%); }
.media-placeholder::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(var(--color-dark-rgb), 0.72), rgba(var(--color-dark-rgb), 0.08)); }
/* The caption rule must exclude the badge: `.media-placeholder span` outweighs the bare
   `.media-badge` class below, so without :not() the badge lost its absolute positioning,
   dropped into the flex flow at flex-end and printed on top of the caption. */
.media-placeholder span:not(.media-badge) { position: relative; z-index: 1; font-size: var(--text-small); font-weight: var(--weight-bold); line-height: 1.4; }
.media-placeholder .media-badge { position: absolute; z-index: 2; top: var(--space-4); left: var(--space-4); background: var(--color-accent); color: #fff; font-size: var(--text-label); font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: 0.08em; padding: var(--space-2) var(--space-3); border-radius: var(--radius-full); }
.gallery-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--grid-gap); }
.gallery-grid > * { flex: 0 1 calc((100% - 2 * var(--grid-gap)) / 3); min-width: 0; margin: 0; }
@media (max-width: 1040px) { .gallery-grid > * { flex-basis: calc((100% - var(--grid-gap)) / 2); } }
@media (max-width: 720px) { .gallery-grid > * { flex-basis: 100%; } }

/* ============================================================ TESTIMONIALS */
.rating-line { font-size: var(--text-small); color: var(--color-muted); margin-bottom: var(--space-5); }
.rating-line strong { color: #f5a623; letter-spacing: 0.05em; margin-right: var(--space-2); }
.quote {
  background: var(--color-background); border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-accent);
  border-radius: var(--radius-lg); padding: var(--space-6); height: 100%;
}
.sec-primary .quote, .sec-dark .quote { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.16); border-left-color: var(--color-accent); }
.quote p { font-style: italic; }
.quote cite { display: block; font-style: normal; font-size: var(--text-small); font-weight: var(--weight-bold); color: var(--color-muted); }
.sec-primary .quote cite, .sec-dark .quote cite { color: rgba(255, 255, 255, 0.7); }
.placeholder-note {
  display: inline-block; margin-bottom: var(--space-3);
  font-size: var(--text-label); font-weight: var(--weight-bold);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-accent); border: 1px dashed currentColor;
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-full);
}

/* ============================================================ FAQ ACCORDION */
.faq-list { max-width: 900px; }
.faq-item { border-bottom: 1px solid var(--color-border); }
.sec-primary .faq-item, .sec-dark .faq-item { border-bottom-color: rgba(255, 255, 255, 0.18); }
.faq-q {
  width: 100%; display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5);
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: var(--space-5) 0; font-family: var(--font-display);
  font-size: 1.0625rem; font-weight: var(--weight-bold); color: inherit; line-height: 1.4;
  transition: color var(--motion-duration) var(--motion-easing);
}
.faq-q:hover { color: var(--color-accent); }

/* On the brand and dark panels the question inherits white, so hovering it to the accent
   put #B32527 on #9E0F11: 1.27 to 1, which is the question disappearing into the panel.
   The same on the dark tier measured 2.72 to 1. Hover keeps the text white here and marks
   itself with an underline instead, which costs no contrast. Focus is included because the
   report was "hover or click", and a click leaves the button focused. */
.sec-primary .faq-q:hover, .sec-dark .faq-q:hover, .section--photo .faq-q:hover,
.sec-primary .faq-q:focus-visible, .sec-dark .faq-q:focus-visible {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The plus/minus icon had the same problem permanently rather than only on hover: it is
   drawn in the accent colour, which on these panels is very nearly the panel itself. */
.sec-primary .faq-icon::before, .sec-primary .faq-icon::after,
.sec-dark .faq-icon::before, .sec-dark .faq-icon::after,
.section--photo .faq-icon::before, .section--photo .faq-icon::after {
  background: #fff;
}

.faq-icon { flex-shrink: 0; width: 22px; height: 22px; position: relative; margin-top: 2px; }
.faq-icon::before, .faq-icon::after {
  content: ""; position: absolute; background: var(--color-accent);
  transition: transform var(--motion-duration) var(--motion-easing), opacity var(--motion-duration) var(--motion-easing);
}
.faq-icon::before { top: 10px; left: 0; width: 22px; height: 2px; }
.faq-icon::after { top: 0; left: 10px; width: 2px; height: 22px; }
.faq-q[aria-expanded="true"] .faq-icon::after { transform: rotate(90deg); opacity: 0; }
.faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--motion-duration-slow) var(--motion-easing-expo); }
.faq-a > div { overflow: hidden; }
.faq-item.is-open .faq-a { grid-template-rows: 1fr; }
.faq-a p { padding-top: var(--space-1); margin-bottom: var(--space-5); }
.faq-a p:last-child { margin-bottom: var(--space-5); }

/* ============================================================ FORMS */
.form-card { background: var(--color-background); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: var(--space-6); box-shadow: var(--shadow-md); }
.sec-primary .form-card, .sec-dark .form-card { color: var(--color-text); }
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field[hidden] { display: none; }
.field.full { grid-column: 1 / -1; }
.field label { font-size: var(--text-label); font-weight: var(--weight-bold); letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-muted); }
/* A required field is only obvious once the browser rejects the form, which is late.
   The asterisk says so up front; the `required` attribute is what actually enforces it. */
.field:has(:required) label::after {
  content: " *"; color: var(--color-primary); font-weight: var(--weight-bold);
}
.field input, .field select, .field textarea {
  font-family: var(--font-body); font-size: var(--text-body); color: var(--color-text);
  background: var(--color-background); border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4); width: 100%;
  transition: border-color var(--motion-duration) var(--motion-easing), box-shadow var(--motion-duration) var(--motion-easing);
}
.field textarea { min-height: 130px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px rgba(var(--color-accent-rgb), 0.18); }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: 3px solid var(--color-accent); outline-offset: 2px; }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-note { font-size: var(--text-small); color: var(--color-muted); margin-top: var(--space-4); }
.consent-block { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-5); }
.consent-check { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-small); cursor: pointer; }
.consent-check input[type="checkbox"] { margin-top: 3px; width: 18px; height: 18px; flex-shrink: 0; accent-color: var(--color-accent); cursor: pointer; }
.consent-check span { line-height: 1.5; }
.form-status { margin-top: var(--space-4); padding: var(--space-4); border-radius: var(--radius-sm); font-size: var(--text-small); display: none; }
.form-status.is-visible { display: block; }
.form-status.is-success { background: rgba(22, 101, 52, 0.1); border: 1px solid rgba(22, 101, 52, 0.35); color: #166534; }
.form-status.is-error { background: rgba(var(--color-primary-rgb), 0.08); border: 1px solid rgba(var(--color-primary-rgb), 0.35); color: var(--color-primary); }
.form-status a { font-weight: var(--weight-bold); }

/* ============================================================ PRE-FOOTER */
.prefooter-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--grid-gap-lg); align-items: center; }
@media (max-width: 960px) { .prefooter-grid { grid-template-columns: 1fr; } }
.prefooter-align-spacer { display: none; }
.area-cols { columns: 2; column-gap: var(--grid-gap); font-size: var(--text-small); }
@media (max-width: 520px) { .area-cols { columns: 1; } }
.area-cols ul { list-style: none; padding: 0; margin: 0; }
.area-cols li { margin-bottom: var(--space-2); break-inside: avoid; }
.area-cols a { text-decoration: none; }
.area-cols a:hover { text-decoration: underline; }
.contact-list { list-style: none; padding: 0; margin: 0 0 var(--space-5); }
.contact-list li { display: flex; gap: var(--space-3); margin-bottom: var(--space-3); font-size: var(--text-small); }
.contact-list strong { min-width: 96px; flex-shrink: 0; text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--text-label); opacity: 0.75; padding-top: 2px; }

/* ============================================================ FOOTER */
.site-footer { background: var(--color-surface-dark); color: rgba(255, 255, 255, 0.75); padding: var(--space-9) 0 var(--space-6); }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1.1fr; gap: var(--grid-gap-lg); }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr; gap: var(--space-7); } }
.site-footer h4 { color: #fff; font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: var(--space-4); }
.site-footer a { color: rgba(255, 255, 255, 0.75); text-decoration: none; transition: color var(--motion-duration) var(--motion-easing); }
.site-footer a:hover { color: #fff; }
.footer-logo { width: 230px; margin-bottom: var(--space-5); }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: var(--space-3); font-size: var(--text-small); }
.footer-social { display: flex; gap: var(--space-3); margin-top: var(--space-5); }
.footer-social a { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border: 1px solid rgba(255, 255, 255, 0.22); border-radius: var(--radius-full); transition: background-color var(--motion-duration) var(--motion-easing), border-color var(--motion-duration) var(--motion-easing); }
.footer-social a:hover { background: var(--color-accent); border-color: var(--color-accent); }
.footer-social svg { width: 18px; height: 18px; fill: currentColor; }
.footer-bottom { margin-top: var(--space-8); padding-top: var(--space-5); border-top: 1px solid rgba(255, 255, 255, 0.14); display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between; align-items: center; font-size: var(--text-small); }
.footer-bottom p { margin: 0; }
.footer-credit a { font-weight: var(--weight-bold); color: #fff; text-decoration: underline; text-underline-offset: 3px; }

/* ============================================================ FLOATING CALL BUTTON */
.float-phone {
  position: fixed; right: var(--space-5); bottom: var(--space-5); z-index: 90;
  display: inline-flex; align-items: center; justify-content: center;
  width: 58px; height: 58px; border-radius: var(--radius-full);
  background: var(--color-accent); color: #fff; box-shadow: var(--shadow-md);
  text-decoration: none; animation: phonePulse 2.6s ease-in-out infinite;
  transition: transform var(--motion-duration) var(--motion-easing), background-color var(--motion-duration) var(--motion-easing);
}
.float-phone:hover { transform: scale(1.07); background: var(--color-primary); }
.float-phone svg { width: 24px; height: 24px; fill: currentColor; }
@keyframes phonePulse {
  0%, 100% { box-shadow: var(--shadow-md), 0 0 0 0 rgba(var(--color-accent-rgb), 0.5); }
  70% { box-shadow: var(--shadow-md), 0 0 0 16px rgba(var(--color-accent-rgb), 0); }
}

/* ============================================================ SCROLL REVEAL */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity var(--reveal-duration) var(--motion-easing-expo), transform var(--reveal-duration) var(--motion-easing-expo); }
.reveal.is-visible { opacity: 1; transform: none; }

/* ============================================================ UTILITIES */
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.stack > * + * { margin-top: var(--space-5); }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.text-center { text-align: center; }
.note {
  border-left: 3px solid var(--color-accent);
  background: var(--color-surface);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm);
  font-size: var(--text-small); margin-bottom: var(--space-5);
}
.sec-primary .note, .sec-dark .note { background: rgba(255, 255, 255, 0.08); }
.note p:last-child { margin-bottom: 0; }

/* Deliberate, visible placeholder chip — content the client still has to supply. */
.tag {
  display: inline-block;
  font-family: var(--font-body); font-size: var(--text-label); font-weight: var(--weight-bold);
  letter-spacing: 0.06em; padding: 2px var(--space-2);
  border: 1px dashed currentColor; border-radius: var(--radius-sm);
  opacity: 0.85;
}
