/* Manny's Demolition & Junk Removal — design system.

   ELECTRIC BLUE AND BLACK, 2026-08-12. This was red for a day. It is now built
   on the blue sampled straight out of his own logo, #30A8DA, so the site and the
   decal on his trailer finally agree with each other.

   The organising idea is a heavy-equipment livery: solid black slabs, generous
   white space between them, and blue used structurally rather than decoratively.
   Blue does three jobs on this site and no others: it is the call-to-action, it
   is the marker on a section heading, and it is the frame of the badge. If it
   starts appearing anywhere else it stops meaning "act here".

   CONTRAST IS A SHIP GATE. Every foreground/background pair declared below is
   asserted in scripts/audit.mjs and the build fails under 4.5:1 (3:1 large).
   Measured values are in the comments. If you change a token, run npm run audit
   before you believe it. */

/* Self-hosted, latin subset only, no CDN.
   Before this block the CSS named Barlow Condensed and Inter but nothing loaded
   them, so the page silently rendered in whatever the visitor happened to have.
   On this box that meant Arial Narrow and it looked fine, which is exactly why
   it went unnoticed: on a machine without a narrow face installed the headings
   fall back to a normal-width system font and the design changes shape.
   font-display:swap so text is readable immediately rather than invisible. */
@font-face {
  font-family: 'Barlow Condensed';
  src: url('/assets/fonts/barlow-condensed-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- Electric blue ----------------------------------------------------
     Three blues, and which one you use depends entirely on what is behind it.

     HIS ACTUAL COLOUR IS --brand-blue-lift. #30A8DA was sampled from the badge
     on his own logo (research/assets/listingplanner-logo.jpg, weighted mean of
     the dominant blue cluster). It is a LIGHT blue: 7.25:1 on black and only
     2.71:1 on white, which is why it cannot be the whole palette. That is not a
     flaw in his mark, it is what his mark is for. The badge, the trailer decal
     and every black slab on this site are dark surfaces, so the colour he
     actually uses is the colour that carries the brand where it matters most.
     The other two are the same hue (197.6deg) walked darker so it can survive on
     white as well.

     --brand-blue        #0073A3  fills, CTA backgrounds, rules, the badge frame.
                                  5.27:1 on white, so white text on it is fine,
                                  but it is NOT a text colour on light grounds by
                                  house rule, and BLACK text on it fails at
                                  3.73:1. Never do that.
     --brand-blue-deep   #00567A  blue TEXT on white or #F4F4F5. 8.04:1 / 7.31:1.
     --brand-blue-lift   #30A8DA  HIS COLOUR. Blue TEXT and graphics on black.
                                  #0073A3 on #0B0B0C is only 3.73:1 and fails
                                  body text; this clears at 7.25:1 on --ink and
                                  6.55:1 on --ink-2.

     Every one of these meets or beats the red it replaced at the same job. */
  --brand-blue: #0073A3;
  --brand-blue-deep: #00567A;
  --brand-blue-lift: #30A8DA;

  /* --- Field ----------------------------------------------------------- */
  --ink: #0B0B0C;          /* the black slabs */
  --ink-2: #17181A;        /* raised panels sitting on a slab */
  --ink-3: #26282C;        /* hairlines on dark; never carries text */

  --paper: #FFFFFF;
  --paper-2: #F4F4F5;      /* section bands */
  --rule: #E3E3E5;         /* hairlines on light; never carries text */

  /* --- Text ------------------------------------------------------------ */
  --text: #17181A;         /* 17.77:1 on white, 16.16:1 on #F4F4F5 */
  --text-muted: #56585C;   /*  7.13:1 on white,  6.48:1 on #F4F4F5 */
  --on-dark: #FFFFFF;      /* 19.67:1 on --ink */
  --on-dark-soft: #C3C6CB; /* 11.49:1 on --ink, 10.37:1 on --ink-2 */
  --on-dark-muted: #A9ACB2;/*  8.65:1 on --ink,  7.81:1 on --ink-2 */

  /* --- Metrics ---------------------------------------------------------- */
  --wrap: 1120px;
  --gap: clamp(1rem, 3vw, 2rem);
  --measure: 68ch;
  --callbar-h: 62px;

  --font-display: 'Barlow Condensed', 'Oswald', 'Arial Narrow', system-ui, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* The four cyan-era aliases that lived here (--brand, --muted, --line,
     --ink-soft) were removed 2026-08-12. They existed only so form.njk kept
     rendering through a token rename; form.njk and service-areas.njk now use
     the real names, verified by grep across src/. */
}

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

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

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(1rem, .96rem + .2vw, 1.075rem);
  line-height: 1.7;
  color: var(--text);
  background: var(--paper);
  /* The fixed call bar sits over the bottom of the page on phones. Without
     this the footer's last line is unreachable. */
  padding-bottom: var(--callbar-h);
}
@media (min-width: 900px) { body { padding-bottom: 0; } }

/* ---- Type --------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 .45em;
  font-weight: 700;
}
h1 { font-size: clamp(2.4rem, 1.5rem + 4vw, 4.4rem); line-height: .92; letter-spacing: -.015em; }
h2 { font-size: clamp(1.75rem, 1.25rem + 2.2vw, 2.7rem); line-height: .98; letter-spacing: -.012em; margin-top: 0; }
h3 { font-size: clamp(1.2rem, 1.05rem + .75vw, 1.5rem); line-height: 1.06; letter-spacing: -.005em; margin-top: 1.6em; }
h4 { font-size: 1.1rem; line-height: 1.15; letter-spacing: .01em; }

/* The blue tick above every section heading. This is the site's one repeating
   graphic device, and it is what keeps the accent meaningful without painting the
   page with it. Opt out with class="bare" where a heading is inside a panel
   that already carries a blue edge. */
h2:not(.bare)::before {
  content: '';
  display: block;
  width: 46px;
  height: 6px;
  background: var(--brand-blue);
  margin-bottom: .5em;
}
.section > .wrap > h2 { margin-top: 0; }
.wrap > h2 + p { margin-top: -.2em; }

p, ul, ol { margin: 0 0 1.15em; }
/* Generous measure in content areas. Grid children opt out below. */
.wrap > p, .wrap > ul, .wrap > ol, .wrap > .faq { max-width: var(--measure); }

a { color: var(--brand-blue-deep); text-underline-offset: .18em; text-decoration-thickness: from-font; }
a:hover { color: var(--text); }

strong, b { font-weight: 600; }
img { max-width: 100%; height: auto; display: block; }
hr { border: 0; border-top: 2px solid var(--ink); margin: 2.5rem 0; }

/* ---- Structure ---------------------------------------------------------- */
.wrap { width: min(100% - 2rem, var(--wrap)); margin-inline: auto; }
.section { padding: clamp(2.75rem, 6vw, 4.5rem) 0; }
.section--wash { background: var(--paper-2); }

.section--ink {
  background: var(--ink);
  color: var(--on-dark-soft);
  border-top: 6px solid var(--brand-blue);
}
.section--ink h2, .section--ink h3, .section--ink h4 { color: var(--on-dark); }
.section--ink strong { color: var(--on-dark); }
.section--ink a { color: var(--on-dark); text-decoration-color: var(--brand-blue); text-decoration-thickness: 2px; }
.section--ink a:hover { color: var(--brand-blue-lift); }

/* ---- Masthead ----------------------------------------------------------- */
/* Sticky on desktop only. On a phone the wrapped nav made this ~110px tall,
   which is a lot of a 660px viewport to spend on navigation when the thing the
   visitor actually wants is the phone number. That job belongs to the fixed
   call bar at the bottom, so up here the header simply scrolls away. */
/* LOCKED AT EVERY WIDTH, 2026-08-12. It used to stick only from 900px up,
   because the mobile nav needed three wrapped rows and pinning 110px of links
   to the top of a phone is indefensible. Moving the links behind a menu button
   removed that objection: the bar is now one compact row, so it stays put and
   the way back to any page is always one tap away.
   z-index sits above the fixed call bar, so an open menu covers it rather than
   fighting it. */
.masthead {
  background: var(--ink); color: var(--on-dark);
  position: sticky; top: 0; z-index: 60;
}
/* A sticky header eats the top of anything an in-page link jumps to. */
html { scroll-padding-top: 5.5rem; }

.masthead__inner {
  display: flex; align-items: center; gap: .75rem;
  padding: .6rem 0;
  /* No wrapping now. The row is logo, then everything else pushed right. */
  flex-wrap: nowrap;
}

.brandmark {
  display: flex; align-items: center; flex: none; gap: .7rem;
  text-decoration: none; color: var(--on-dark);
}

/* The business name beside the badge, desktop only.
   Hidden-but-present below 900px: the badge stands alone on a phone where the
   bar has no room, and the link keeps an accessible name at every width. This
   is the same declaration as .visually-hidden, written out rather than shared
   because the desktop query below has to undo every part of it. */
.brandmark__name {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
@media (min-width: 900px) {
  .brandmark__name {
    position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip: auto; clip-path: none; white-space: normal;
    display: block;
    font-family: var(--font-display); text-transform: uppercase; font-weight: 700;
    /* His blue. #0073A3 on this near-black bar is 3.73:1 and would fail as
       text; --brand-blue-lift clears at 7.25:1. */
    color: var(--brand-blue-lift);
  }
  .brandmark__name-main {
    display: block; font-size: 1.5rem; line-height: .9; letter-spacing: .005em;
  }
  .brandmark__name-sub {
    display: block; font-size: .68rem; letter-spacing: .17em;
    line-height: 1.2; margin-top: .3em;
  }
}
/* His real badge. Height, not width, because the mark is close to square and
   the header is a fixed-height bar. 56px is the measured floor at which the
   DEMO & JUNK REMOVAL sub-line still holds together; below that the two lines
   of type close up. See docs/LOGO-RECREATION-SPEC.md §5. */
.brandmark__badge { height: 56px; width: auto; flex: none; }
@media (min-width: 900px) { .brandmark__badge { height: 62px; } }

/* Available to screen readers, gone from the page. The badge carries the name
   as artwork, so the accessible name has to come from somewhere. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- Menu button and panel ---------------------------------------------- */

/* NO-JS BASELINE, and the desktop layout. The links are an ordinary row. The
   button does not exist. Everything that turns this into a drop panel is scoped
   to `.js` AND to a max-width query, so a wide screen keeps the row it always
   had and a visitor without JavaScript keeps a working header rather than a
   button that does nothing. */
.menubtn { display: none; }

.nav { margin-left: auto; }
.nav__inner { display: flex; flex-wrap: wrap; gap: .35rem 1.35rem; align-items: center; }
.nav a {
  color: var(--on-dark-soft); text-decoration: none; white-space: nowrap;
  font-family: var(--font-display); text-transform: uppercase;
  font-size: 1.05rem; letter-spacing: .04em;
  padding: .3rem 0; border-bottom: 2px solid transparent;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--on-dark); border-bottom-color: var(--brand-blue); }
/* The number is already in the bar on a wide screen. */
.nav__call { display: none; }

/* GET A QUOTE, the one link in the bar we want tapped.
   DESKTOP ONLY, deliberately. Below 900px this same anchor sits inside the drop
   panel where every row is already full width at 1.2rem, and singling one out
   there reads as a rendering fault rather than emphasis. The mobile panel has
   its own blue call button at the bottom doing this job.
   --brand-blue-lift is the on-dark blue: #0073A3 on the near-black masthead is
   only 3.73:1 and would fail as text. This clears at 7.25:1. */
@media (min-width: 900px) {
  /* An outlined block, not just bolder text. Every link in this bar is already
     Barlow Condensed 700, so `font-weight: 700` changed precisely nothing; the
     box and the colour are what actually make it read as a block.
     Outlined rather than filled on purpose: the phone button beside it is a
     solid blue block, and two of those next to each other compete instead of
     ranking. Plain links, then this, then the phone. */
  /* `.nav a` is (0,1,1) and `.nav__quote` alone is (0,1,0), so the base link
     colour won and this rendered in the same grey as everything else. The
     border showed blue because nothing else set a border colour. Qualified to
     out-specify it rather than reaching for !important. */
  .nav a.nav__quote {
    color: var(--brand-blue-lift);
    letter-spacing: .07em;
    border: 2px solid var(--brand-blue-lift);
    /* display:flex + line-height:1 so the box is sized by the type rather than
       by the inline box's leading. The first attempt kept the inline default
       and added a negative margin-block to pull it back into the row, which
       made the border cut straight through the letterforms. */
    display: flex; align-items: center; line-height: 1;
    padding: .55rem .85rem;
  }
  .nav a.nav__quote:hover,
  .nav a.nav__quote[aria-current="page"] {
    color: var(--ink);
    background: var(--brand-blue-lift);
    border-color: var(--brand-blue-lift);
  }
}

/* Phone first, menu last. Rightmost is where a thumb expects the hamburger,
   and the number is the thing we would rather they tapped. */
.masthead__actions {
  flex: none; display: flex; align-items: center; gap: .55rem;
}

@media (max-width: 899.98px) {
/* Push the menu button to the right edge.
   On desktop `.nav` carries margin-left:auto and shoves everything after it to
   the right, so the actions group needed nothing. Below 900px the nav is
   absolutely positioned and therefore out of flow, so nothing was pushing, and
   the button sat tucked against the badge in the middle of the bar. */
.masthead__actions { margin-left: auto; }

.js .menubtn {
  display: inline-flex; align-items: center; gap: .5rem;
  flex: none;
  background: none; border: 2px solid rgba(255,255,255,.22);
  color: var(--on-dark); cursor: pointer;
  font-family: var(--font-display); text-transform: uppercase; font-weight: 700;
  font-size: 1rem; letter-spacing: .06em;
  padding: .5rem .8rem;
  /* 44px minimum target. Everything in this header is thumb-operated. */
  min-height: 44px;
}
.js .menubtn:hover { border-color: var(--brand-blue); }
.js .menubtn[aria-expanded="true"] { background: var(--brand-blue); border-color: var(--brand-blue); }

.menubtn__bars { display: block; width: 20px; height: 14px; position: relative; }
.menubtn__bars i {
  position: absolute; left: 0; width: 100%; height: 2px;
  background: currentColor; transition: transform .18s ease, opacity .18s ease;
}
.menubtn__bars i:nth-child(1) { top: 0; }
.menubtn__bars i:nth-child(2) { top: 6px; }
.menubtn__bars i:nth-child(3) { top: 12px; }
/* Bars become an X. Purely decorative: aria-expanded carries the real state. */
.menubtn[aria-expanded="true"] .menubtn__bars i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menubtn[aria-expanded="true"] .menubtn__bars i:nth-child(2) { opacity: 0; }
.menubtn[aria-expanded="true"] .menubtn__bars i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* The panel. `hidden` does the hiding, so it is genuinely out of the
   accessibility tree and out of tab order when closed, rather than merely
   invisible. That is the difference between a menu and a trap.
   Absolute against .masthead (which is sticky, so it is a positioned ancestor)
   rather than against .masthead__inner, so the panel spans the full width of
   the bar instead of the width of the content column. */
.js .nav[hidden] { display: none; }
.js .nav {
  position: absolute; top: 100%; left: 0; right: 0;
  margin-left: 0;
  background: var(--ink-2);
  border-top: 1px solid rgba(255,255,255,.12);
  border-bottom: 3px solid var(--brand-blue);
  max-height: calc(100dvh - 4.5rem); overflow-y: auto;
}
.js .nav__inner {
  flex-direction: column; gap: 0; padding: .35rem 0 1rem;
  /* Matches .wrap so the panel's links line up with the logo above them. */
  width: min(100% - 2rem, var(--wrap)); margin-inline: auto;
  /* The base rule centers items for the desktop ROW. In a column that centers
     them horizontally, which left the links floating mid-panel with stubby
     dividers under each one. Stretch puts them back on the left edge, under
     the logo, with full-width rules between. */
  align-items: stretch;
}
.js .nav__call { display: block; }
.js .nav a {
  padding: .85rem .25rem; border-bottom: 1px solid rgba(255,255,255,.09);
  font-size: 1.2rem;
}
.js .nav a:hover, .js .nav a[aria-current="page"] {
  color: var(--on-dark); border-bottom-color: rgba(255,255,255,.09);
  padding-left: .6rem;
}
.js .nav a[aria-current="page"] { box-shadow: inset 3px 0 0 var(--brand-blue); padding-left: .6rem; }
.js .nav__call {
  margin-top: .9rem; background: var(--brand-blue); color: var(--on-dark);
  text-align: center; border-bottom: 0 !important; padding: .9rem 1rem !important;
}
.js .nav__call:hover { background: var(--brand-blue-deep); padding-left: 1rem !important; }
} /* end max-width: 899.98px */

.callbtn {
  background: var(--brand-blue); color: var(--on-dark);
  font-family: var(--font-display); text-transform: uppercase;
  font-weight: 700; font-size: 1.1rem; letter-spacing: .02em;
  padding: .55rem 1.1rem; text-decoration: none;
  border: 2px solid var(--brand-blue);
  display: none; /* redundant with the fixed call bar until desktop */
}
.callbtn:hover { background: var(--brand-blue-deep); border-color: var(--brand-blue-deep); color: var(--on-dark); }
@media (min-width: 900px) { .callbtn { display: inline-block; } }

/* ---- Hero --------------------------------------------------------------- */
.hero {
  position: relative; overflow: hidden;
  background: var(--ink); color: var(--on-dark-soft);
  padding: clamp(2.75rem, 7vw, 5.5rem) 0 clamp(2.5rem, 6vw, 4.5rem);
  border-bottom: 6px solid var(--brand-blue);
}
.hero .wrap { position: relative; z-index: 1; }
.hero h1 { color: var(--on-dark); }
.hero h1 em { color: var(--brand-blue-lift); font-style: normal; }
.hero__lede {
  font-size: clamp(1.08rem, 1rem + .55vw, 1.32rem);
  line-height: 1.55; max-width: 54ch; color: var(--on-dark-soft);
}
.hero__cta { display: flex; gap: .8rem; flex-wrap: wrap; margin-top: 1.7rem; }
.hero a { color: var(--on-dark); }

/* The badge, blown up and bled off the right edge. It is the mark used as
   architecture rather than as an ornament, which is why it is outline only and
   sits at 14%: any louder and it competes with the headline. Behind the text
   column it would eat contrast, so it is confined to the right and dropped
   entirely on phones. */
.hero::after {
  content: '';
  position: absolute; right: -7%; top: 50%; transform: translateY(-50%);
  width: min(44vw, 520px); aspect-ratio: 208 / 240;
  opacity: .14; pointer-events: none; z-index: 0;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 208 240'%3E%3Cpath fill='%230073A3' fill-rule='evenodd' d='M104 0L207.92 60V180L104 240L.08 180V60ZM104 6.7L202.12 63.35V176.65L104 233.3L5.88 176.65V63.35Z'/%3E%3Cpath fill='%230073A3' fill-rule='evenodd' d='M104 11.6L197.87 65.8V174.2L104 228.4L10.13 174.2V65.8ZM104 16.4L193.72 68.2V171.8L104 223.6L14.28 171.8V68.2Z'/%3E%3C/svg%3E");
}
@media (max-width: 899px) { .hero::after { display: none; } }

/* ---- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-block; font-family: var(--font-display);
  text-transform: uppercase; font-weight: 700; letter-spacing: .02em;
  font-size: 1.15rem; line-height: 1.25; padding: .78rem 1.5rem;
  text-decoration: none; border: 2px solid var(--brand-blue);
  background: var(--brand-blue); color: var(--on-dark);
  transition: background-color .15s ease, border-color .15s ease, transform .15s ease;
}
.btn:hover { background: var(--brand-blue-deep); border-color: var(--brand-blue-deep); color: var(--on-dark); }
.btn:active { transform: translateY(1px); }

/* Ghost buttons take their colour from context: blue-deep reads at 8.04:1 on
   white, white reads at 19.67:1 on the black slabs. currentColor then drives
   the border so the two can never drift apart. */
.btn--ghost { background: transparent; color: var(--brand-blue-deep); border-color: currentColor; }
.btn--ghost:hover { background: var(--brand-blue-deep); border-color: var(--brand-blue-deep); color: var(--on-dark); }
.hero .btn--ghost, .section--ink .btn--ghost, .foot .btn--ghost { color: var(--on-dark); }
.hero .btn--ghost:hover, .section--ink .btn--ghost:hover, .foot .btn--ghost:hover { background: var(--on-dark); color: var(--ink); border-color: var(--on-dark); }

/* ---- Fixed call bar ----------------------------------------------------- */
/* The audience is a homeowner standing in front of the pile, on a phone. The
   number is reachable from anywhere on any page without scrolling. */
.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: flex; align-items: stretch; gap: 0;
  height: var(--callbar-h);
  background: var(--ink); border-top: 3px solid var(--brand-blue);
  padding-bottom: env(safe-area-inset-bottom);
}
/* Was .callbar__note, a caption that did nothing when tapped. Now the second
   CTA. Styled as the quieter of the two on purpose: outlined rather than
   filled, so the phone button stays the obvious first move and this reads as
   the alternative rather than competing with it. */
/* Two words, centered, matching the call button's weight and size so the bar
   reads as one control split in half rather than a caption next to a button.
   The stacked strong + sub-line that lived here was doing the caption's job
   again in a smaller font. */
.callbar__quote {
  flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  padding: 0 .85rem; min-width: 0; text-decoration: none;
  border-right: 1px solid rgba(255,255,255,.16);
  font-family: var(--font-display); text-transform: uppercase; font-weight: 700;
  font-size: 1.28rem; letter-spacing: .02em; line-height: 1;
  color: var(--brand-blue-lift);
}
.callbar__quote:hover { color: var(--on-dark); }
.callbar__btn {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  padding: 0 1.15rem; background: var(--brand-blue); color: var(--on-dark);
  font-family: var(--font-display); text-transform: uppercase; font-weight: 700;
  font-size: 1.28rem; letter-spacing: .02em; text-decoration: none;
}
.callbar__btn:hover { background: var(--brand-blue-deep); color: var(--on-dark); }
@media (min-width: 900px) { .callbar { display: none; } }

/* ---- Review badge ------------------------------------------------------- */
.reviews-badge {
  display: inline-flex; align-items: center; gap: .55rem; flex-wrap: wrap;
  border: 1px solid var(--rule); background: var(--paper);
  padding: .5rem .85rem; font-size: .95rem; color: var(--text);
}
.reviews-badge strong { color: var(--brand-blue-deep); font-size: 1.15rem; font-weight: 600; }
.reviews-badge .stars { color: var(--brand-blue); letter-spacing: .08em; }
.hero .reviews-badge, .section--ink .reviews-badge {
  background: transparent; border-color: var(--ink-3); color: var(--on-dark-soft);
}
.hero .reviews-badge strong, .section--ink .reviews-badge strong { color: var(--brand-blue-lift); }
.hero .reviews-badge .stars, .section--ink .reviews-badge .stars { color: var(--brand-blue-lift); }

/* ---- Grids and cards ---------------------------------------------------- */
.grid { display: grid; gap: var(--gap); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); }

.card {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-top: 4px solid var(--ink);
  padding: 1.35rem 1.4rem 1.5rem;
  transition: border-top-color .15s ease, transform .15s ease, box-shadow .15s ease;
  /* Grid already makes cards in a row equal height, but the content inside was
     top-aligned, so a heading that wrapped to two lines pushed its link down and
     the row of links came out ragged. Push the trailing link paragraph to the
     bottom instead. Safe as a flex container here because nothing inside a card
     has a top margin, so no collapsed margins change. :has() is the whole point:
     where it is unsupported the rule is skipped and cards look exactly as before,
     and cards whose last child is ordinary prose are left alone either way. */
  display: flex;
  flex-direction: column;
}
.card > p:last-child:has(> a) { margin-top: auto; padding-top: .35rem; }
.card h3 { margin-top: 0; font-size: 1.28rem; }
.card p { color: var(--text-muted); }
.card p:last-child { margin-bottom: 0; }
.card a {
  font-family: var(--font-display); text-transform: uppercase;
  font-size: 1.02rem; letter-spacing: .03em; color: var(--brand-blue-deep);
  text-decoration: none; border-bottom: 2px solid var(--brand-blue);
  padding-bottom: 1px;
}
.card a::after { content: ' \2192'; }
.card a:hover { color: var(--text); border-bottom-color: var(--text); }
.card:hover { border-top-color: var(--brand-blue); transform: translateY(-3px); box-shadow: 0 10px 24px rgba(11, 11, 12, .1); }

.section--ink .card { background: var(--ink-2); border-color: var(--ink-3); border-top-color: var(--brand-blue); }
.section--ink .card p { color: var(--on-dark-muted); }
.section--ink .card a { color: var(--on-dark); border-bottom-color: var(--brand-blue); }
.section--ink .card:hover { box-shadow: none; }

/* ---- The county waste callout: the signature module of this site --------- */
/* Always white with a blue spine, including on the wash band, so it reads as a
   pulled-out fact rather than as more page. */
.county-rule {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-left: 6px solid var(--brand-blue);
  padding: 1.3rem 1.45rem;
  margin: 1.7rem 0;
}
/* In prose the callout needs air above and below. As a grid child that same
   margin fights the grid gap and staggers the row, which is what the home page
   was doing: four county boxes, three different vertical offsets. The column
   flex plus margin-top:auto lands the "county coverage" link on a common line
   across the row no matter how much rule text each county has. */
.grid > .county-rule { margin: 0; display: flex; flex-direction: column; }
.grid > .county-rule > p:last-child { margin-top: auto; padding-top: .8rem; }
.county-rule h3 { margin-top: 0; font-size: 1.25rem; }
.county-rule p:last-child { margin-bottom: 0; }
.county-rule__facility { font-size: .92rem; color: var(--text-muted); margin-bottom: 0; }
.county-rule__refuses { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; padding: 0; margin: .9rem 0 0; }
.county-rule__refuses li {
  background: var(--ink); color: var(--on-dark);
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: .04em;
  font-size: .88rem; padding: .22rem .6rem;
}
.section--ink .county-rule { background: var(--ink-2); border-color: var(--ink-3); border-left-color: var(--brand-blue); }
.section--ink .county-rule__facility { color: var(--on-dark-muted); }

/* ---- Photography -------------------------------------------------------- */
/* 31 real job photos land in this build, including seven before/after pairs.
   They are the only warm thing in a cold palette, so nothing here tints them:
   no filter, no overlay, no rounded corner. A hairline and a lot of air.

   Class names are NOT invented here. They are the ones already emitted by the
   macros in src/_includes/photo.njk, which carries zero-specificity :where()
   floor defaults so the markup stays usable if this file is missing. These
   rules supersede those; that file's photoStyles() call can now be dropped.

     figure.photo > img + figcaption.photo__cap
     .ba > figure.ba__item > span.ba__tag(.ba__tag--after) + img + .ba__cap
     ul.photo-grid > li > figure.photo

   background-color, never the background shorthand: the macros put the LQIP on
   the img as an inline background-image and the shorthand would fight it. */
.photo { margin: 0 0 1.6rem; }
.photo img,
.ba__item img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--rule); background-color: var(--paper-2);
}
.photo__cap, .ba__cap {
  font-size: .86rem; line-height: 1.5; color: var(--text-muted); padding-top: .55rem;
}
.section--ink .photo img,
.section--ink .ba__item img { border-color: var(--ink-3); background-color: var(--ink-2); }
.section--ink .photo__cap, .section--ink .ba__cap { color: var(--on-dark-muted); }

.photo-grid {
  display: grid; gap: var(--gap); grid-template-columns: 1fr;
  margin: 0 0 1.9rem; padding: 0; list-style: none;
}
.photo-grid > li { margin: 0; }
@media (min-width: 560px) { .photo-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .photo-grid { grid-template-columns: repeat(3, 1fr); } }

/* Before and after. The pair is the single strongest asset on this site, so it
   gets a black rule tying the two frames together and reads as one object. */
.ba {
  display: grid; gap: .85rem; grid-template-columns: 1fr;
  align-items: start; margin: 0 0 1.9rem;
  border-top: 4px solid var(--ink); padding-top: 1rem;
}
@media (min-width: 640px) { .ba { grid-template-columns: 1fr 1fr; gap: 1rem; } }
.ba__item { position: relative; margin: 0; }
.ba__tag {
  position: absolute; top: 0; left: 0; z-index: 1;
  font-family: var(--font-display); text-transform: uppercase; font-weight: 700;
  font-size: .95rem; line-height: 1; letter-spacing: .12em; padding: .42rem .7rem;
  background: var(--ink); color: var(--on-dark);
}
.ba__tag--after { background: var(--brand-blue); }
.section--ink .ba { border-top-color: var(--brand-blue); }

/* ---- Photo slots -------------------------------------------------------- */
/* Deliberately conspicuous. A placeholder that looks like a finished design is
   how unfinished builds get approved by accident. None of these may survive to
   launch; the spec forbids a photo slot on a page with no photo. */
.photo-slot {
  aspect-ratio: 4 / 3; display: grid; place-content: center; text-align: center;
  background: repeating-linear-gradient(45deg, var(--paper-2), var(--paper-2) 12px, #E9E9EB 12px, #E9E9EB 24px);
  border: 2px dashed var(--brand-blue); color: var(--text-muted); font-size: .85rem; padding: 1rem;
}
.photo-slot strong { display: block; color: var(--brand-blue-deep); font-family: var(--font-display); text-transform: uppercase; }

/* ---- FAQ ---------------------------------------------------------------- */
.faq { border-top: 2px solid var(--ink); }
.faq details { border-bottom: 1px solid var(--rule); padding: .95rem 0; }
.faq summary {
  font-family: var(--font-display); text-transform: uppercase; font-size: 1.18rem;
  letter-spacing: -.005em; cursor: pointer; color: var(--text);
  list-style: none; display: flex; gap: .8rem; align-items: baseline;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before { content: '+'; color: var(--brand-blue); font-size: 1.3rem; line-height: 1; flex: none; }
.faq details[open] summary::before { content: '\2013'; }
.faq details[open] summary { color: var(--brand-blue-deep); }
.faq p { margin: .7rem 0 0; max-width: var(--measure); }
.section--ink .faq { border-top-color: var(--brand-blue); }
.section--ink .faq details { border-bottom-color: var(--ink-3); }
.section--ink .faq summary { color: var(--on-dark); }
.section--ink .faq details[open] summary { color: var(--brand-blue-lift); }

/* ---- Tables and lists --------------------------------------------------- */
.tablewrap { overflow-x: auto; margin-bottom: 1.4rem; }
table { border-collapse: collapse; width: 100%; font-size: .95rem; }
th, td { text-align: left; padding: .6rem .75rem; border-bottom: 1px solid var(--rule); }
thead th {
  font-family: var(--font-display); text-transform: uppercase; font-size: 1rem;
  letter-spacing: .03em; border-bottom: 2px solid var(--ink); color: var(--text);
}
tbody tr:hover { background: var(--paper-2); }
/* The county column is a proper noun and reads badly broken over two lines.
   .tablewrap already scrolls, so widening it costs nothing on a phone. */
th:first-child, td:first-child { white-space: nowrap; }

.checklist { list-style: none; padding: 0; }
.checklist li { padding-left: 1.45rem; position: relative; margin-bottom: .5rem; }
.checklist li::before {
  content: ''; position: absolute; left: 0; top: .62em;
  width: .48rem; height: .48rem; background: var(--brand-blue); transform: rotate(45deg);
}

.townlist { list-style: none; padding: 0; columns: 3; column-gap: 2rem; max-width: none; }
.townlist li { break-inside: avoid; margin-bottom: .35rem; }
@media (max-width: 640px) { .townlist { columns: 2; } }

/* ---- Form controls ------------------------------------------------------ */
/* src/_includes/form.njk carries its own <style> block for the quote form.
   That block renders in the body and therefore wins the cascade at equal
   specificity, which is fine: these are the defaults for anything it does not
   cover, and what the site falls back to if that block is ever lifted out.
   Deliberately not applied to checkboxes, radios or buttons, which must keep
   their intrinsic sizing. */
input:not([type='checkbox']):not([type='radio']):not([type='submit']),
select, textarea {
  width: 100%; font: inherit; color: var(--text); background: var(--paper);
  border: 2px solid var(--rule); border-radius: 0; padding: .65rem .75rem;
}
textarea { resize: vertical; }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--brand-blue); outline-offset: 1px; border-color: var(--ink);
}
label {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: .02em; color: var(--text);
}

/* ---- Breadcrumbs -------------------------------------------------------- */
/* The trail renders between the black masthead and the black hero. Left on
   paper it cut a white stripe across the middle of what should read as one
   black slab, which looked like a rendering fault rather than a decision.

   crumbs.njk emits <div class="wrap"><nav class="crumbs">, so the element that
   needs the background is already inside the measure and cannot bleed on its
   own. The fix releases the OUTER wrap to full width and re-applies the measure
   to the nav inside it. Where :has() is unsupported nothing matches and the
   trail simply stays on paper, which is legible either way.

   The first attempt used box-shadow: 0 0 0 100vmax with a clip-path to bleed
   the colour without touching layout. It worked, and it also made Chrome
   composite unrelated page content into the strip on a full-page capture. Not
   worth a paint trick to save one selector. */
.crumbs { font-size: .87rem; color: var(--on-dark-muted); padding: .85rem 0; }
.crumbs a { color: var(--on-dark-soft); }
.crumbs a:hover { color: var(--on-dark); }

main > .wrap:has(> .crumbs) { width: 100%; max-width: none; background: var(--ink); }
main > .wrap:has(> .crumbs) > .crumbs { width: min(100% - 2rem, var(--wrap)); margin-inline: auto; }

/* ---- Notice / gate banner ----------------------------------------------- */
.notice {
  background: #FFF3D6; border: 1px solid #E0C05C; border-left: 6px solid #8A6708;
  color: #4A3A05; padding: .85rem 1.1rem; font-size: .92rem; margin: 1.2rem 0;
}
.notice strong { color: #33280A; }

/* ---- Footer ------------------------------------------------------------- */
.foot {
  background: var(--ink); color: var(--on-dark-muted);
  border-top: 6px solid var(--brand-blue);
  padding: 3.2rem 0 2rem; font-size: .93rem; margin-top: 0;
}
.foot h4 { color: var(--on-dark); font-size: 1.1rem; letter-spacing: .04em; margin-bottom: .9rem; }
.foot a { color: var(--on-dark-soft); text-decoration: none; }
.foot a:hover { color: var(--on-dark); text-decoration: underline; text-decoration-color: var(--brand-blue); text-decoration-thickness: 2px; }
.foot strong { color: var(--on-dark); }
.foot .checklist li::before { background: var(--brand-blue); }
/* Nineteen counties in one column ran the footer to 600px and made every page
   end on a long thin list. Opt-in rather than blanket: the services list is
   seven items and splitting that one just breaks its labels in half. */
@media (min-width: 700px) { .foot__cols { columns: 2; column-gap: 1.6rem; } }
.foot__cols li { break-inside: avoid; }
.foot__bar {
  border-top: 1px solid var(--ink-3); margin-top: 2.2rem; padding-top: 1.2rem;
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; font-size: .85rem;
}
.foot__phone { font-family: var(--font-display); font-size: 1.6rem; letter-spacing: .01em; }

/* ---- Focus and skip link ------------------------------------------------ */
.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: .5rem; top: .5rem; background: var(--brand-blue); color: var(--on-dark);
  padding: .55rem 1rem; z-index: 100; text-decoration: none;
}

/* Blue at 5.27:1 on white and 3.73:1 on black clears the 3:1 that a focus
   indicator needs against both. Controls that are themselves blue get a white
   ring set inside them instead, because a blue ring on blue is nothing. */
:focus-visible { outline: 3px solid var(--brand-blue); outline-offset: 2px; }
.btn:focus-visible, .callbtn:focus-visible, .callbar__btn:focus-visible, .skip:focus-visible {
  outline: 3px solid var(--paper); outline-offset: -6px;
}
.btn--ghost:focus-visible { outline: 3px solid currentColor; outline-offset: 2px; }
