/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */

/*
Document
========
*/

/**
Use a better box model (opinionated). Box sizing should not be inherited. (https://www.oddbird.net/2025/09/04/box-model/)
*/

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

/**
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
2. Correct the line height in all browsers.
3. Prevent adjustments of font size after orientation changes in iOS.
4. Use a more readable tab size (opinionated).
*/

html {
	font-family:
		system-ui,
		'Segoe UI',
		Roboto,
		Helvetica,
		Arial,
		sans-serif,
		'Apple Color Emoji',
		'Segoe UI Emoji'; /* 1 */
	line-height: 1.15; /* 2 */
	-webkit-text-size-adjust: 100%; /* 3 */
	tab-size: 4; /* 4 */
}

/*
Sections
========
*/

/**
Remove the margin in all browsers.
*/

body {
	margin: 0;
}

/*
Text-level semantics
====================
*/

/**
Add the correct font weight in Chrome and Safari.
*/

b,
strong {
	font-weight: bolder;
}

/**
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
2. Correct the odd 'em' font sizing in all browsers.
*/

code,
kbd,
samp,
pre {
	font-family:
		ui-monospace,
		SFMono-Regular,
		Consolas,
		'Liberation Mono',
		Menlo,
		monospace; /* 1 */
	font-size: 1em; /* 2 */
}

/**
Add the correct font size in all browsers.
*/

small {
	font-size: 80%;
}

/**
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
*/

sub,
sup {
	font-size: 75%;
	line-height: 0;
	position: relative;
	vertical-align: baseline;
}

sub {
	bottom: -0.25em;
}

sup {
	top: -0.5em;
}

/*
Tabular data
============
*/

/**
Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
*/

table {
	border-color: currentcolor;
}

/*
Forms
=====
*/

/**
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
*/

button,
input,
optgroup,
select,
textarea {
	font-family: inherit; /* 1 */
	font-size: 100%; /* 1 */
	line-height: 1.15; /* 1 */
	margin: 0; /* 2 */
}

/**
Correct the inability to style clickable types in iOS and Safari.
*/

button,
[type='button'],
[type='reset'],
[type='submit'] {
	-webkit-appearance: button;
}

/**
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
*/

legend {
	padding: 0;
}

/**
Add the correct vertical alignment in Chrome and Firefox.
*/

progress {
	vertical-align: baseline;
}

/**
Correct the cursor style of increment and decrement buttons in Safari.
*/

::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
	height: auto;
}

/**
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/

[type='search'] {
	-webkit-appearance: textfield; /* 1 */
	outline-offset: -2px; /* 2 */
}

/**
Remove the inner padding in Chrome and Safari on macOS.
*/

::-webkit-search-decoration {
	-webkit-appearance: none;
}

/**
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to 'inherit' in Safari.
*/

::-webkit-file-upload-button {
	-webkit-appearance: button; /* 1 */
	font: inherit; /* 2 */
}

/*
Interactive
===========
*/

/*
Add the correct display in Chrome and Safari.
*/

summary {
	display: list-item;
}


/* ==========================================================================
   Wisconsin RV Detailing — site.css
   modern-normalize is pasted above (MIT). Everything below is ours.

   1. Tokens        design decisions live here; tune the whole site from :root
   2. Base          element defaults
   3. Utilities     container, bands, eyebrow, visually-hidden, icon
   4. Components    buttons, links, placeholder figures, pills, fields
   5. Header / footer
   6. Home
   ========================================================================== */


/* 1. Tokens
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light;      /* the brief is explicit: clean and bright, no dark mode */

  /* Color — from the brief, section 3 */
  --surface:      #F8F7F5;  /* warm white — dominant background */
  --surface-alt:  #EDEAE6;  /* light warm gray — alternating section bands */
  --ink:          #1B1C1E;  /* near-black — headings and body. never #000 */
  --muted:        #615F5C;  /* warm gray — secondary text. 5.3:1 on --surface-alt */
  --line:         #D8D4CF;  /* borders and dividers */
  --red:          #A82B26;  /* brick red — CTAs, accents, active states */
  --red-dark:     #7F1F1B;  /* hover and pressed */

  /* Color — additions */
  --card:          #FFFFFF; /* the floating hero card: pure white so it lifts off the warm page */
  --on-red:        #FFFFFF; /* text on red buttons */
  /* Hero overlay. Strong on the copy side, weak on the far side. The hero
     photographs run from near-black (a soaped panel in shade) to near-white
     (gelcoat in full sun), so no single text colour clears 3:1 on all of them
     — measured 1.33:1 worst case in ink, 1.47:1 worst case in white. The scrim
     is what makes the ground predictable; the copy is then white against it.
     Both the headline and the tagline are large text (the tagline is sized to
     clear 18.66px bold for exactly this reason), so the floor is 3:1 rather
     than 4.5:1. Do not lighten without re-measuring against the brightest
     frame, and do not shrink .hero__tag without darkening this to match. */
  --scrim:         rgba(27, 28, 30, 0.55);  /* hero overlay, headline side */
  --scrim-mid:     rgba(27, 28, 30, 0.26);  /* hero overlay, midpoint */
  --scrim-fade:    rgba(27, 28, 30, 0.06);  /* hero overlay, far side */
  --focus:         var(--ink);              /* keyboard focus ring on light surfaces */
  --focus-inverse: #FFFFFF;                 /* focus ring on red and dark surfaces */
  --on-dark-ink:   #FFFFFF;                 /* text over the hero photograph */
  --shadow-card:   0 12px 32px rgba(27, 28, 30, 0.14);

  /* Type — one family, two weights (400, 700), to protect the 300KB page budget */
  --font-display: "Archivo", "Archivo Fallback", Arial, sans-serif;
  --font-body:    "Archivo", "Archivo Fallback", Arial, sans-serif;

  --text-sm:      1rem;        /* 16px — eyebrows, labels, captions only. never paragraphs */
  --text-base:    1.0625rem;   /* 17px — body minimum per brief */
  --text-lg:      1.25rem;     /* 20px — lead paragraphs, card titles */
  --text-xl:      1.5rem;      /* 24px — h3 */
  --text-2xl:     clamp(1.75rem, 1.2rem + 2vw, 2.5rem);    /* h2, 28→40px */
  --text-3xl:     clamp(2.25rem, 1.5rem + 3vw, 3.5rem);    /* inner-page h1, 36→56px */
  --text-display: clamp(2.75rem, 1rem + 5.5vw, 5.5rem);    /* hero h1, 44→88px; sized so DETAILING fits its column */

  --leading-body:    1.6;
  --leading-heading: 1.15;
  --leading-display: 1.0;
  --tracking-caps:   0.08em;   /* "We come to you", section eyebrows */

  /* Space */
  --space-1: 0.25rem;   /*  4px */
  --space-2: 0.5rem;    /*  8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-5: 1.5rem;    /* 24px */
  --space-6: 2rem;      /* 32px */
  --space-7: 3rem;      /* 48px */
  --space-8: 4rem;      /* 64px */
  --space-9: 6rem;      /* 96px */
  --section-y: clamp(3rem, 6vw, 6rem);   /* vertical padding of each page band */

  /* Layout */
  --container: 72rem;                     /* 1152px max content width */
  --header-h:  3.75rem;                   /* 60px — the sticky header. anything offsetting against it reads this */
  --gutter:    clamp(1rem, 4vw, 2.5rem);  /* side padding, 16→40px */
  --tap-min:   2.75rem;                   /* 44px minimum tap target */

  /* Shape */
  --radius-sm:   0.375rem;   /*  6px — inputs */
  --radius:      0.75rem;    /* 12px — cards */
  --radius-lg:   1.5rem;     /* 24px — the hero card, "generous" */
  --radius-pill: 999px;      /* buttons, RV-type pills */

  /* Motion — hover and focus only */
  --ease: 150ms ease;
}


/* Archivo, self-hosted (brief section 2 — no render-blocking third-party
   request against a 95+ Lighthouse target). SIL Open Font License 1.1.
   Archivo v25 is a variable font, so one file covers the whole weight axis
   and both the weights we use; latin-ext is a separate subset the browser
   only fetches if the page needs those codepoints. */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 100%;
  font-display: swap;
  src: url("/fonts/archivo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 100%;
  font-display: swap;
  src: url("/fonts/archivo-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Metric-matched fallback so the Archivo swap doesn't shift the layout.
   Measured in Chrome against Arial: Archivo runs ~98.5% of Arial's advance
   width, with ascent 88% and descent 21% of the em. Overrides are divided by
   size-adjust, per spec, so the fallback line boxes land where Archivo's do. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial"), local("Liberation Sans");
  size-adjust: 98.5%;
  ascent-override: 89.3%;
  descent-override: 21.3%;
  line-gap-override: 0%;
}


/* 2. Base
   -------------------------------------------------------------------------- */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: var(--leading-heading);
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-4);
  text-wrap: balance;
  overflow-wrap: break-word;
}
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }

p {
  margin: 0 0 var(--space-4);
  max-width: 65ch;
}
p:last-child { margin-bottom: 0; }

a {
  color: var(--red);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}
a:hover { color: var(--red-dark); }

img, svg { display: block; max-width: 100%; }
button, input { font: inherit; color: inherit; }

/* Focus: 3px ring, drawn outside the element, in ink on light ground.
   Inside anything red or dark, .on-dark switches the ring to white (3:1 floor). */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}
.on-dark :focus-visible,
.on-dark:focus-visible {
  outline-color: var(--focus-inverse);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
}


/* 3. Utilities
   -------------------------------------------------------------------------- */
.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band { padding-block: var(--section-y); }
.band--alt { background: var(--surface-alt); }
.band--slim { padding-block: var(--space-7); }

.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
}

.lead {
  font-size: var(--text-lg);
  color: var(--muted);
}

/* Keeps a hyphenated compound ("south-central") on one line. Archivo has no
   U+2011 non-breaking hyphen, so that character would fall back to Arial. */
.nowrap { white-space: nowrap; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--gutter);
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--ink);
  color: var(--surface);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  text-decoration: none;
  font-weight: 700;
}
.skip-link:focus { top: 0; color: var(--surface); }

/* Inline Lucide icons: stroke follows currentColor, size follows font-size */
.icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
}


/* 4. Components
   -------------------------------------------------------------------------- */

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0.75rem 1.5rem;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--ease), color var(--ease), border-color var(--ease);
}
.btn--primary {
  background: var(--red);
  border-color: var(--red);
  color: var(--on-red);
}
.btn--primary:hover {
  background: var(--red-dark);
  border-color: var(--red-dark);
  color: var(--on-red);
}
.btn--ghost {
  background: transparent;
  border-color: var(--ink);
  color: var(--ink);
}
.btn--ghost:hover {
  background: var(--ink);
  color: var(--surface);
}
/* Over the hero photograph the ghost button's ink border and label vanish. */
.on-dark .btn--ghost {
  border-color: var(--on-dark-ink);
  color: var(--on-dark-ink);
}
.on-dark .btn--ghost:hover {
  background: var(--on-dark-ink);
  color: var(--ink);
}
.btn--compact {
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
}
.btn--compact .icon { display: none; }   /* below 60em the header is too tight for icon + number + menu */
@media (min-width: 60em) {
  .btn--compact { padding: 0.5rem 1.125rem; font-size: 1rem; }
  .btn--compact .icon { display: block; }
}

/* Arrow link: red, bold, chevron follows */
.link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  font-weight: 700;
  color: var(--red);
  text-decoration: none;
}
.link:hover { text-decoration: underline; }

/* Placeholder figures.
   Every image is a real <img> with width/height. Until the photo exists the
   figure shows a --surface-alt block with the filename, so gaps are obvious. */
.ph {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--surface-alt);
}
.ph::after {
  content: attr(data-file);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  text-align: center;
  color: var(--muted);
  font-family: ui-monospace, Consolas, monospace;
  font-size: var(--text-sm);
}
.ph img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  color: transparent;   /* hides a broken image's alt text visually; AT still reads it */
}
.ph--16x9 { aspect-ratio: 16 / 9; }
.ph--4x3  { aspect-ratio: 4 / 3; }
.ph--3x2  { aspect-ratio: 3 / 2; }
.ph--3x4  { aspect-ratio: 3 / 4; }   /* portrait phone shots, shown uncropped */

/* Pill radio group — native radios, styled labels, no JS */
.pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.pills legend {
  padding: 0;
  margin-bottom: var(--space-3);
  font-size: var(--text-lg);
  font-weight: 700;
}
.pill { position: relative; }
.pill input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.pill span {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0.5rem 1rem;
  border: 2px solid var(--line);
  border-radius: var(--radius-pill);
  font-weight: 700;
  transition: background-color var(--ease), border-color var(--ease), color var(--ease);
}
.pill:hover span { border-color: var(--ink); }
.pill input:checked + span {
  background: var(--red);
  border-color: var(--red);
  color: var(--on-red);
}
/* RV-type drawing, to the right of the label, on the home card's pills and on
   /quote's type cards. Its strokes are currentColor, so it turns white with the
   label when the pill or card is selected. At 24px tall it fits inside the
   pill without making it taller. */
.pill__icon,
.choice__icon {
  flex: none;
  width: 3rem;
  height: 1.5rem;
}
.pill input:focus-visible + span {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* Text fields */
.field label {
  display: block;
  font-weight: 700;
  margin-bottom: var(--space-2);
}
.field input {
  width: 100%;
  min-height: var(--tap-min);
  padding: 0.625rem 0.875rem;
  border: 2px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  font-size: var(--text-lg);
  text-overflow: ellipsis;
  transition: border-color var(--ease);
}
.field input:hover { border-color: var(--muted); }
.field input:focus { border-color: var(--ink); }
/* Below 40em the hint drops to body size, so the longer ones ("Town,
   campground or address") fit a phone-width field instead of losing their
   last word. What the visitor types stays at --text-lg. The ellipsis covers
   anything still too long on a 320px screen. */
@media (max-width: 39.99em) {
  .field input::placeholder { font-size: var(--text-base); }
}
.field__hint {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--muted);
}


/* 5. Header / footer
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--header-h);
}

/* Wordmark: two stacked lines on mobile. From 64em it runs on one line once its
   column is wide enough (about 72em with five nav links), and stays stacked until then.
   Sized so brand + phone + menu fit a 360px screen inside the gutter. */
.brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8125rem;
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
}
.brand span { display: block; white-space: nowrap; }
.brand:hover { color: var(--red); }

.site-header__actions {
  margin-left: auto;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Desktop nav: centered between brand and actions */
.nav-desktop { display: none; }

/* Mobile nav: a native <details> disclosure, so it works with JS off */
.nav-mobile summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--radius-pill);
  list-style: none;
  cursor: pointer;
  font-size: 1.25rem;
}
.nav-mobile summary::-webkit-details-marker { display: none; }
.nav-mobile summary:hover { background: var(--surface-alt); }
.nav-mobile[open] .icon-menu,
.nav-mobile:not([open]) .icon-x { display: none; }
.nav-mobile__panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  padding: var(--space-2) var(--gutter) var(--space-5);
  background: var(--card);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-card);
}
.nav-mobile__panel ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-mobile__panel a {
  display: flex;
  align-items: center;
  min-height: 3.25rem;
  border-bottom: 1px solid var(--line);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
}
.nav-mobile__panel li:last-child a { border-bottom: 0; }
.nav-mobile__panel a:hover { color: var(--red); }

@media (min-width: 64em) {
  .site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    min-height: 4.25rem;
  }
  .brand { font-size: 1.0625rem; }
  .brand span { display: inline; }
  .nav-desktop { display: block; }
  .nav-desktop ul {
    display: flex;
    gap: clamp(var(--space-5), 2.5vw, var(--space-6));   /* 24px at the 64em switch, the full 32px from 80em */
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .nav-desktop a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-min);
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: border-color var(--ease), color var(--ease);
  }
  .nav-desktop a:hover,
  .nav-desktop a[aria-current="page"] { border-bottom-color: var(--red); }
  .site-header__actions { justify-self: end; margin-left: 0; }
  .nav-mobile { display: none; }
}

/* Footer */
.site-footer {
  border-top: 1px solid var(--line);
  padding-block: var(--space-8) var(--space-6);
}
.site-footer__grid {
  display: grid;
  gap: var(--space-6);
}
.site-footer h2 {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-3);
}
.site-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-footer li { padding-block: 0.125rem; }

/* 22 towns in one column made the footer taller than most of the pages above
   it. Two columns halves that; the names are short enough to sit comfortably.
   break-inside keeps a town from splitting across the column boundary. */
.site-footer__towns {
  columns: 2;
  column-gap: var(--space-5);
}
.site-footer__towns li { break-inside: avoid; }
.site-footer li a,
.site-footer address a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.5rem;
  color: var(--ink);
  text-decoration: none;
}
.site-footer li a:hover,
.site-footer address a:hover { color: var(--red); }
.site-footer address { font-style: normal; }
.site-footer .brand {
  display: inline-block;
  margin-bottom: var(--space-3);
  font-size: 1.0625rem;
}
/* Social row — sits under the phone and email in the first footer column.
   The links are icon-only, so they get a fixed 44px tap target rather than the
   text-link treatment .site-footer li a gives everything else. */
.site-footer__social {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.site-footer__social li { padding-block: 0; }
.site-footer .site-footer__social a {
  justify-content: center;
  gap: 0;
  width: var(--tap-min);
  height: var(--tap-min);
  min-height: var(--tap-min);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: color var(--ease), border-color var(--ease);
}
.site-footer .site-footer__social a:hover {
  color: var(--red);
  border-color: var(--red);
}

.site-footer__legal {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
  color: var(--muted);
}
.site-footer__legal p { margin-bottom: var(--space-2); }
@media (min-width: 48em) {
  .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr; gap: var(--space-8); }
}


/* 6. Home
   -------------------------------------------------------------------------- */

/* Hero. On phones the photo is a band of its own with the copy over it, and
   the quote-starter card sits under it, overlapping only its bottom edge.
   From 48em the photo goes full-bleed behind both, copy left and card right,
   and the card carries a negative bottom margin so it breaks the hero's bottom
   edge; the following band pads down to make room (the 48em block below).

   Phones used to get that desktop layout stacked, with the photo stretched
   behind the copy and the whole card. That cropped each portrait frame to
   about half its width, and the card covered most of what was left. */
.hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.hero__media {
  grid-area: 1 / 1;   /* shares the first row with the copy, so it is never shorter than the copy */
  /* At least the frames' own 3:4 shape, so a phone sees the whole photo.
     Capped so a small tablet's band stays shorter than its screen; past the
     cap the crop is top and bottom, as on desktop. */
  min-height: min(100vw * 4 / 3, 36rem);
}
/* Rotating hero. The frames are stacked by .ph img already; all this adds is
   which one is opaque. Driven by the inline script on index.html rather than a
   CSS animation, so the frame count is not baked into keyframe percentages —
   adding a before/after pair is one more <img>, nothing else.

   .is-rotating is only added once every frame has decoded, so the first fade
   never lands on an image that has not arrived. Without it there is no
   transition at all, which is also what a visitor with JavaScript off gets. */
.hero__slides img {
  opacity: 0;
}
.hero__slides img.is-current {
  opacity: 1;
}
.hero__slides.is-rotating img {
  transition: opacity 1.2s ease-in-out;
}

/* Phones place the copy and the card straight onto .hero's grid. */
.hero__inner { display: contents; }
.hero__copy {
  grid-area: 1 / 1;
  align-self: start;
  position: relative;
  z-index: 3;
  padding: clamp(2.5rem, 8vw, 6rem) var(--gutter) var(--space-8);
  /* The scrim, solid down to the foot of the buttons and faded out by the foot
     of this padding, so the rest of the photo shows as shot. It is drawn on the
     copy rather than the photo so it ends where the copy does at any text size.
     Desktop draws it across the photo instead, see the 48em block below. */
  background: linear-gradient(180deg,
    var(--scrim) calc(100% - var(--space-8)), var(--scrim-mid) calc(100% - var(--space-6)), transparent);
}
.hero__title {
  font-size: var(--text-display);
  line-height: var(--leading-display);
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--on-dark-ink);
  margin: 0 0 var(--space-4);
}
.hero__title span { display: block; }
.hero__tag {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--on-dark-ink);
  /* 1.1875rem = 19px. Deliberately above the 18.66px where the large-text
     exemption starts: bold and over that line, this is held to 3:1 rather than
     4.5:1, which is what lets --scrim sit as light as it does. Do not reduce
     this without darkening the scrim to match. */
  font-size: 1.1875rem;
  margin: 0 0 var(--space-6);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
/* On phones the pair stacks full-width. Around 400px the two buttons sit on a
   wrap boundary that the web-font swap flips (Archivo's digits run wider than
   Arial's), which moved the hero card by a full row. Stacking removes the
   boundary and gives bigger tap targets. */
@media (max-width: 35.99em) {
  .hero__actions .btn { flex: 1 1 100%; }
}

/* Quote starter card — shared by the homepage hero and /services.
   .form-card is the same shell for the standalone contact and campground forms. */
.starter,
.form-card {
  display: grid;
  gap: var(--space-5);
  padding: clamp(var(--space-5), 4vw, var(--space-6));
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.hero .starter {
  grid-area: 2 / 1;
  position: relative;
  z-index: 3;
  margin: calc(-1 * var(--space-6)) var(--gutter) 0;   /* overlaps the photo's bottom edge */
}
.starter .btn,
.form-card .btn { width: 100%; }
.form-card h2 {
  margin: 0;
  font-size: var(--text-xl);
}
.form-card .step-nav { margin-top: 0; }
.form-card[hidden] { display: none; }
.starter { container: starter / inline-size; }
.starter .pills {
  display: grid;
  grid-template-columns: 1fr;   /* one per row until the card can hold two */
}
/* Five types, two across and three down (the last one on a row of its own),
   once a pill has room for the longest label ("Pull-behind") and its drawing
   without wrapping. A single row of five never fits. The home hero's card is
   390px inside at every width from 1200px up, so this only drops to one column
   on phones and in the narrower tablet hero (roughly 768-1190px). */
@container starter (min-width: 24rem) {
  .starter .pills { grid-template-columns: repeat(2, 1fr); }
}
.starter .pill span {
  width: 100%;
  justify-content: space-between;
  gap: var(--space-2);
}
@media (min-width: 48em) {
  .hero { display: block; }
  .hero__media {
    position: absolute;
    inset: 0;
    min-height: 0;
  }
  .hero__media::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(90deg,
      var(--scrim) 0%, var(--scrim) 26%, var(--scrim-mid) 52%, var(--scrim-fade) 100%);
  }
  .hero__inner {
    position: relative;
    z-index: 3;
    display: grid;
    grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
    align-items: end;
    gap: var(--space-8);
    min-height: min(78vh, 46rem);   /* room for the photograph; the card still breaks the bottom edge */
    padding-top: clamp(2.5rem, 8vw, 6rem);
  }
  .hero__copy {
    grid-area: auto;
    align-self: center;
    padding: 0 0 var(--space-9);   /* keeps the copy centred on the visible photo, clear of the card overlap */
    background: none;
  }
  .hero .starter {
    grid-area: auto;
    margin: 0 0 calc(-1 * var(--space-9));
  }
  .hero + .band { padding-top: calc(var(--section-y) + var(--space-9)); }
}

/* Service cards */
.services {
  display: grid;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.service-card {
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.service-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
}
.service-card h3 {
  margin: 0;
  font-size: var(--text-lg);
}
.service-card p {
  flex: 1;
  margin: 0;
  color: var(--muted);
}
@media (min-width: 40em) { .services { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64em) { .services { grid-template-columns: repeat(4, 1fr); } }
/* Three cards (/fleets "Who we work with"): stacked, then three across. Two
   across would leave the third alone on its own row. */
.services.services--3 { grid-template-columns: 1fr; }
@media (min-width: 48em) { .services.services--3 { grid-template-columns: repeat(3, 1fr); } }
/* Six cards (/rv-detailing "What each service does"): stacked, then two across
   (the .services default), then three across from 64em, so they fill three rows
   of two or two rows of three. The default four across at 64em would strand two
   on a second row, and their text is longer than the /fleets cards', so three
   across waits for 64em rather than starting at 48em like .services--3. */
@media (min-width: 64em) { .services.services--6 { grid-template-columns: repeat(3, 1fr); } }
/* A card that leads with an icon instead of a photo (/fleets "On your property") */
.service-card__icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: var(--space-1);
  color: var(--red);
}

/* How it works */
.steps {
  display: grid;
  gap: var(--space-6);
  counter-reset: step;
  list-style: none;
  margin: 0;
  padding: 0;
}
.steps li {
  counter-increment: step;
  padding-top: var(--space-4);
  border-top: 2px solid var(--line);
}
.steps li::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  margin-bottom: var(--space-3);
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1;
  color: var(--red);
}
.steps h3 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}
.steps p { margin: 0; color: var(--muted); }
/* A pointer link under a list: the "typical visit" link under the steps, and
   the "Service details" and "RV & camper detailing" links under the home
   page's package cards */
.steps__more,
.services__more { margin: var(--space-6) 0 0; }
.services__more + .services__more { margin-top: var(--space-2); }   /* a second pointer sits close under the first */
@media (min-width: 48em) { .steps { grid-template-columns: repeat(3, 1fr); gap: var(--space-8); } }

/* Trust row */
.trust {
  display: grid;
  gap: var(--space-4);
  list-style: none;
  margin: 0;
  padding: 0;
}
.trust li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-weight: 700;
}
.trust .icon {
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.1em;
  color: var(--red);
}
@media (min-width: 40em) { .trust { grid-template-columns: repeat(2, 1fr); } }
/* auto-fit rather than a fixed count: the homepage row carries four items,
   the service pages five (they keep "no checks" alongside the tax line). */
@media (min-width: 64em) {
  .trust { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }
}

/* Service area — reads as prose, not a list, on the home and rv-detailing
   pages alike (brief section 1) */
.area__prose {
  max-width: 46ch;
  font-size: var(--text-lg);
}
.area__prose + .area__prose { margin-top: var(--space-4); }

/* Map beside the prose. The map's ground (#E5E0D9) sits a shade under
   --surface-alt, so with rounded corners and a soft shadow it reads as a panel
   set into the band rather than a pasted-in rectangle. The map gets the wider
   column: it is 1200px square with ~16px town labels, and every pixel of width
   is label size. Side by side only from 64em — below that the headline column
   gets too narrow and the map too small, so they stack, the map capped at
   about its desktop size. Hover enlarges it 25% in place — no lightbox,
   nothing to close — and only where there is a real hover, so a tap on a phone
   does nothing. At 25% there's no room to grow from the centre: the page
   gutter is 16–40px and the map would gain 60–75px a side. So it grows from
   the edge with open space beside it — out to the right while stacked (it is
   left-aligned, with room past its 37.5rem cap), and back over the gap and the
   text while side by side, so it never pushes past the page edge. The clip is
   a backstop for narrow windows with a mouse; it only cuts sideways. */
.area {
  display: grid;
  gap: var(--space-6);
  align-items: center;
  overflow-x: clip;
}
.area__map {
  width: 100%;
  max-width: 37.5rem;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 2px 6px rgba(27, 28, 30, 0.06), 0 10px 28px rgba(27, 28, 30, 0.08);
  transform-origin: left center;
  transition: transform 300ms ease, box-shadow 300ms ease;
}
@media (hover: hover) {
  .area__map:hover {
    transform: scale(1.25);
    box-shadow: var(--shadow-card);
  }
}
@media (min-width: 64em) {
  .area { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-8); }
  .area__map { transform-origin: right center; }
}

/* Fleets & facilities callout */
.callout {
  display: grid;
  gap: var(--space-5);
  padding: clamp(var(--space-5), 4vw, var(--space-7));
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.callout h2 { margin-bottom: var(--space-2); }
@media (min-width: 48em) {
  .callout { grid-template-columns: 1fr auto; align-items: center; gap: var(--space-8); }
}

/* Final CTA */
.cta-final { text-align: center; }
.cta-final p { margin-inline: auto; }
.cta-final .hero__actions {
  justify-content: center;
  margin-top: var(--space-6);
}


/* 7. Inner pages
   -------------------------------------------------------------------------- */

/* Page header: eyebrow, h1, lead */
.page-head { padding-block: var(--space-8) var(--space-4); }
.page-head h1 {
  font-size: var(--text-3xl);
  margin-bottom: var(--space-4);
}
.page-head .lead { max-width: 60ch; }
.page-head + h2 { margin-top: var(--space-6); }   /* a section heading straight after the page head (/fleets) */
.page-head--roomy { padding-bottom: var(--space-6); }   /* text follows with no photo between (/oxidation-removal, /interior-detailing) */

/* /services — package cards with a "what's included" list */
.packages {
  display: grid;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.service-card--detail .service-card__body { gap: var(--space-3); }
.service-card--detail h2 {
  margin: 0;
  font-size: var(--text-xl);
}
.service-card--detail .service-card__intro {
  flex: none;
  margin: 0;
}
.included {
  flex: 1;
  display: grid;
  align-content: start;   /* the list absorbs spare card height; its rows must not spread to fill it */
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.included li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
.included li::before {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.6em;
  border-radius: 50%;
  background: var(--red);
}
.service-card__note {
  flex: none;
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--red);
  background: var(--surface);
  color: var(--ink);
}
@media (min-width: 48em) {
  .packages { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
}

/* /services — the single worked example. One rig, three finished prices. */
.example {
  max-width: 36rem;
  padding: clamp(var(--space-5), 4vw, var(--space-7));
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.example h2 {
  font-size: var(--text-xl);
  margin-bottom: var(--space-5);
}
.price-list {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--space-5);
  margin: 0 0 var(--space-5);
}
.price-list dt,
.price-list dd {
  margin: 0;
  padding-block: var(--space-3);
  border-top: 1px solid var(--line);
  font-weight: 700;
}
.price-list dd {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.price-list dd.is-quoted {
  font-weight: 400;
  color: var(--muted);
}
/* Phones: "Requires an in-person estimate" drops under its package name.
   Beside it, even wrapped to two lines, it took 186px of a 295px card at
   375px and pushed "Basic Wash" and "Wash & Wax" onto two lines each. The dt
   spans too, so that row's rule still runs the full width. */
@media (max-width: 39.99em) {
  .price-list .is-quoted { grid-column: 1 / -1; }
  .price-list dd.is-quoted {
    padding-top: 0;
    border-top: 0;
    text-align: left;
  }
}
.example__note { margin: 0; color: var(--muted); }

/* Quote starter band: copy beside the shared card */
.quote-band__inner {
  display: grid;
  gap: var(--space-6);
}
@media (min-width: 48em) {
  .quote-band__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 30rem);
    align-items: center;
    gap: var(--space-8);
  }
}

/* Prose: the reading column on SEO pages */
.prose { max-width: 65ch; }
.prose h2 {
  font-size: var(--text-xl);
  margin: var(--space-7) 0 var(--space-3);
}
.prose h2:first-child { margin-top: 0; }
.prose ul {
  display: grid;
  gap: var(--space-2);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
}
.prose li {
  position: relative;
  padding-left: 1.25rem;   /* not flex: items hold inline children like <strong>, which must stay in flow */
}
.prose li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--red);
}

/* Reading column with a call-to-action beside it — /oxidation-removal.
   The prose caps at 65ch, which leaves most of a 1152px container empty. Below
   68em there is no honest room for two columns, so the aside stacks under the
   prose, which is where a closing CTA sat before. */
.with-aside {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}
.with-aside > * { min-width: 0; }

.cta-aside {
  padding: clamp(var(--space-5), 3vw, var(--space-6));
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.cta-aside h2 {
  font-size: var(--text-xl);
  margin: 0 0 var(--space-3);
}
.cta-aside p { margin: 0 0 var(--space-5); }
.cta-aside__actions {
  display: grid;
  gap: var(--space-3);
}
/* The column is narrow enough that "Request an in-person estimate" cannot hold
   one line. .btn is nowrap by default; here it has to be allowed to break, and
   centred, or the wrapped lines sit flush left above the centred phone button. */
.cta-aside .btn {
  width: 100%;
  white-space: normal;
  text-align: center;
}

@media (min-width: 68em) {
  .with-aside {
    grid-template-columns: minmax(0, 1fr) minmax(17rem, 21rem);
    gap: var(--space-8);
  }
  .cta-aside {
    position: sticky;
    top: calc(var(--header-h) + var(--space-5));
  }
}

/* Callout note: one red rule, white ground */
.note {
  margin: var(--space-5) 0;
  padding: var(--space-4) var(--space-5);
  border-left: 3px solid var(--red);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--card);
}
.note p { margin: 0; }
.note p + p { margin-top: var(--space-3); }

/* Image beside text */
.split {
  display: grid;
  gap: var(--space-6);
  align-items: center;
}
.split .ph { border-radius: var(--radius); }
@media (min-width: 48em) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-8); }
  .split--media-right .split__media { order: 2; }
}

/* Before / after pair */
.pair {
  display: grid;
  gap: var(--space-5);
  margin: var(--space-6) 0;
}
.pair figure { margin: 0; }
.pair .ph { border-radius: var(--radius); }
.pair figcaption {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
}
@media (min-width: 40em) { .pair { grid-template-columns: 1fr 1fr; } }


/* 8. Quote flow
   The form works with JavaScript off: every step visible, native validation,
   plain POST. js/quote.js adds .is-wizard and shows one step at a time.
   -------------------------------------------------------------------------- */
.page-head--tight { padding-block: var(--space-6) var(--space-2); }

/* Step indicator */
.stepper {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  counter-reset: s;
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--muted);
}
.stepper li {
  counter-increment: s;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.stepper li::before {
  content: counter(s);
  display: inline-grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 2px solid var(--line);
  border-radius: 50%;
  font-size: 0.875rem;
}
.stepper li[aria-current] { color: var(--ink); }
.stepper li[aria-current]::before {
  border-color: var(--red);
  background: var(--red);
  color: var(--on-red);
}
.stepper li.is-done::before {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--surface);
}

/* Two columns from 60em: steps left, summary right and sticky */
/* Grid and flex items default to min-width:auto, which refuses to shrink below
   their content — one long unbreakable string then widens the whole track. The
   explicit template plus min-width:0 on the children makes that impossible,
   whatever string turns up later. */
.quote-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.quote-layout > * { min-width: 0; }
.quote-form,
.step,
.extras,
.subscription { min-width: 0; }
@media (min-width: 60em) {
  .quote-layout { grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--space-8); }
  .summary { position: sticky; top: 5.5rem; }
}

/* Steps */
.step {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.quote-form:not(.is-wizard) .step + .step {   /* JS off: steps stack with a rule between */
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--line);
}
.step[hidden] { display: none; }
.step > legend {
  padding: 0;
  margin-bottom: var(--space-5);
}
.step__num {
  display: block;
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.step > legend h2 {
  margin: 0;
  font-size: var(--text-2xl);
}
.step > legend h2:focus { outline: none; }   /* focus is programmatic, on step change */
.step__optional { font-weight: 400; color: var(--muted); }
.step__intro { margin-bottom: var(--space-5); color: var(--muted); }

/* Unit type: large tappable cards, each with its rig's drawing to the right of
   the label. The longest label plus a drawing needs about 176px a card, so the
   columns follow the width of the form, not of the screen: from 60em the form
   shares its row with the summary, and a screen width says little about how
   much room it has. One per row until the form can hold two (24rem, the same
   threshold as the home card), three across from 36rem. */
.quote-form { container: quote / inline-size; }
.choices {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
@container quote (min-width: 24rem) { .choices { grid-template-columns: repeat(2, 1fr); } }
@container quote (min-width: 36rem) { .choices { grid-template-columns: repeat(3, 1fr); } }
.choice { position: relative; }
.choice input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.choice span {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 4rem;
  padding: var(--space-3);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  font-weight: 700;
  text-align: left;
  transition: background-color var(--ease), border-color var(--ease), color var(--ease);
}
.choice:hover span { border-color: var(--ink); }
.choice input:checked + span {
  border-color: var(--red);
  background: var(--red);
  color: var(--on-red);
}
.choice input:focus-visible + span {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* Package cards with the calculated price */
.pkgs {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
@media (min-width: 40em) { .pkgs { grid-template-columns: repeat(2, 1fr); } }
.pkg {
  position: relative;
  display: block;
  color: inherit;
  text-decoration: none;
}
.pkg[hidden] { display: none; }
.pkg--wide { grid-column: 1 / -1; }   /* Interior detail only, above the washes when asked for */
.pkg input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.pkg__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-4) var(--space-5);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  transition: border-color var(--ease), box-shadow var(--ease);
}
.pkg:hover .pkg__body { border-color: var(--ink); }
.pkg input:checked + .pkg__body {
  border-color: var(--red);
  box-shadow: inset 0 0 0 1px var(--red);
}
.pkg input:focus-visible + .pkg__body {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}
.pkg__name {
  font-size: var(--text-lg);
  font-weight: 700;
}
.pkg__desc {
  flex: 1;
  color: var(--muted);
}
.pkg__price {
  font-size: var(--text-xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.pkg__price.is-pending {
  font-size: var(--text-base);
  font-weight: 400;
  color: var(--muted);
}
.pkg--link .pkg__price--quoted {
  font-size: var(--text-base);
  color: var(--red);
}
.pkg--link:hover .pkg__price--quoted { text-decoration: underline; }

/* Priced elements only mean anything once the calculator has run. They stay
   hidden until js/quote.js sets data-ready on <html>, so nothing ever flashes
   an empty price and the JS-off form is a clean lead form. (Brief section 2.) */
html:not([data-ready]) .js-priced { display: none !important; }

/* Subscription panel — Touchless only, above the extras.
   One bordered block, a thin red left rule, no badge or banner. */
.subscription {
  margin-bottom: var(--space-5);
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-left: 3px solid var(--red);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--surface-alt);
}
.subscription[hidden] { display: none; }
.subscription__label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.subscription h3 {
  margin-bottom: var(--space-3);
  font-size: var(--text-lg);
}
.subscription p {
  margin-bottom: var(--space-3);
  color: var(--muted);
}
.subscription .extra {
  margin-top: var(--space-4);
  background: var(--card);
}
/* "$300 (save $100)" is long. On narrow screens let the toggle stack so the
   price sits under the label rather than squeezing it to one word a line. */
@media (max-width: 26em) {
  .subscription .extra { flex-wrap: wrap; }
  .subscription .extra .extra__price {
    width: 100%;
    padding-left: 2.125rem;   /* clears the checkbox */
  }
}

/* Extras */
.extras {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
.extra-group {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  border: 0;
}
.extra-group legend {
  padding: 0;
  margin-bottom: var(--space-2);
  font-weight: 700;
}
.extra {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  cursor: pointer;
  transition: border-color var(--ease);
}
.extra[hidden] { display: none; }
.extra input {
  flex: none;
  width: 1.375rem;
  height: 1.375rem;
  margin: 0.15em 0 0;
  accent-color: var(--red);
}
.extra__body {
  flex: 1;
  min-width: 0;   /* flex items default to min-width:auto and refuse to shrink */
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.extra__name { font-weight: 700; }
.extra__desc { color: var(--muted); }
/* "Recommended" over the Spray-On Sealant's name: eyebrow type in red, plain
   text, not a badge. */
.extra__tag {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--red);
}
/* No nowrap: a figure has no break opportunity inside it anyway, and forcing
   it made "$300 (save $100)" an unbreakable 140px block that pushed its grid
   track past the viewport. Let the parenthetical wrap. */
.extra__price {
  flex: none;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* The saving under a subscriber price: "+ $50 a wash" over "$25 off". Plain
   red text, not a badge; it exists only while the subscription is ticked. */
.extra__save {
  display: block;
  text-align: right;
  font-size: var(--text-sm);
  color: var(--red);
}
/* On narrow screens the longer subscriber price drops under the description,
   as the subscription toggle's does, instead of squeezing it to a few letters
   a line. */
@media (max-width: 26em) {
  .extra:has(.extra__save) { flex-wrap: wrap; }
  .extra:has(.extra__save) .extra__price {
    width: 100%;
    padding-left: 2.125rem;   /* clears the checkbox */
  }
  .extra__save { text-align: left; }
}
.extra:has(input:checked) { border-color: var(--red); }

/* Contact fields */
.quote-form .field { margin-bottom: var(--space-5); }
.field textarea {
  width: 100%;
  min-height: 6rem;
  padding: 0.625rem 0.875rem;
  border: 2px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  font-size: var(--text-lg);
  resize: vertical;
  transition: border-color var(--ease);
}
.field textarea:hover { border-color: var(--muted); }
.field textarea:focus { border-color: var(--ink); }
.field__opt { font-weight: 400; color: var(--muted); }
.grid-2 { display: grid; gap: 0 var(--space-5); }
@media (min-width: 40em) { .grid-2 { grid-template-columns: 1fr 1fr; } }

/* City / ZIP. ZIP is narrower — it only ever holds five digits. */
.grid-city-zip { display: grid; gap: 0 var(--space-5); }
@media (min-width: 40em) { .grid-city-zip { grid-template-columns: 1fr 7rem; } }

/* Make / Model / Year. Year is narrower — it only ever holds four digits. */
.grid-3 { display: grid; gap: 0 var(--space-5); }
@media (min-width: 40em) { .grid-3 { grid-template-columns: 1fr 1fr 7rem; } }

.rig-details {
  margin: 0 0 var(--space-5);
  padding: 0;
  border: 0;
}
.rig-details legend {
  padding: 0;
  margin-bottom: var(--space-3);
  font-weight: 700;
}
.windows { margin: 0 0 var(--space-5); padding: 0; border: 0; }
.windows legend {
  padding: 0;
  margin-bottom: var(--space-2);
  font-weight: 700;
}
.windows .pills { gap: var(--space-2); }

.consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin: var(--space-6) 0 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
}
.consent input {
  flex: none;
  width: 1.375rem;
  height: 1.375rem;
  margin-top: 0.15em;
  accent-color: var(--red);
}

.step-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
.step-nav[hidden] { display: none; }

/* Review */
.review dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-2) var(--space-5);
  margin: 0 0 var(--space-5);
}
.review dt { color: var(--muted); }
.review dd {
  margin: 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}
@media (max-width: 39.99em) {
  .review dl { grid-template-columns: 1fr; gap: 0; }
  .review dt { margin-top: var(--space-3); }
}

/* Summary panel */
.summary {
  padding: var(--space-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.summary h2 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-4);
}
.summary__list {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-4);
}
.summary__list dt { color: var(--muted); }
.summary__list dd {
  margin: 0;
  font-weight: 700;
  text-align: right;
  overflow-wrap: anywhere;
}
/* Itemised rows: package, then each extra. Names wrap, amounts never do. */
.summary__items {
  display: grid;
  gap: var(--space-2);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: var(--space-4) 0 0;
  border-top: 1px solid var(--line);
}
.summary__items li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
/* The label shrinks and wraps; the amount keeps its width. */
.summary__items li > span:first-child { min-width: 0; }
.summary__amt {
  flex: none;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.summary__empty { color: var(--muted); }

.summary__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding-top: var(--space-4);
  border-top: 2px solid var(--ink);
  font-weight: 700;
}
.summary__total output {
  font-size: var(--text-2xl);
  font-variant-numeric: tabular-nums;
}
.summary__note {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  color: var(--muted);
}
.summary__note[hidden] { display: none; }

/* Mobile total bar (JS only; hidden on desktop where the panel is visible) */
.summary-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--gutter);
  background: var(--card);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 24px rgba(27, 28, 30, 0.10);
  font-weight: 700;
}
.summary-bar output {
  margin-left: auto;
  font-size: var(--text-xl);
  font-variant-numeric: tabular-nums;
}
.summary-bar[hidden] { display: none; }
@media (max-width: 59.99em) { .quote-page main { padding-bottom: 4.5rem; } }
@media (min-width: 60em) { .summary-bar { display: none; } }

/* Honeypot: off-screen, never display:none (some bots skip those) */
.hp {
  position: absolute !important;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* Thank-you view */
.thanks { max-width: 40rem; }
.thanks h1, .thanks h2 { font-size: var(--text-3xl); }
.thanks h2:focus { outline: none; }
.thanks[hidden] { display: none; }


/* 9. Contact, campgrounds, gallery
   -------------------------------------------------------------------------- */
.field select {
  width: 100%;
  min-height: var(--tap-min);
  padding: 0.625rem 0.875rem;
  border: 2px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  font-size: var(--text-lg);
  transition: border-color var(--ease);
}
.field select:hover { border-color: var(--muted); }
.field select:focus { border-color: var(--ink); }

/* Info beside a form */
.contact-grid {
  display: grid;
  gap: var(--space-6);
  align-items: start;
}
@media (min-width: 48em) {
  .contact-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 32rem); gap: var(--space-8); }
}
.contact-info {
  display: grid;
  gap: var(--space-4);
  list-style: none;
  margin: var(--space-5) 0;
  padding: 0;
}
.contact-info li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
.contact-info .icon {
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.1em;
  color: var(--red);
}
.contact-info a {
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
}
.contact-info a:hover { color: var(--red); }
.contact-info small {
  display: block;
  font-size: var(--text-sm);
  color: var(--muted);
}

/* Gallery grid. One tile per photo, 3:4 portrait so nothing is cropped.
   Two columns from 40em and never three: that is what keeps each before beside
   its after. Keep a pair adjacent in the markup, and singles in twos or at the end. */
.gallery {
  display: grid;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.gallery figure { margin: 0; }
.gallery .ph { border-radius: var(--radius); }
.gallery figcaption {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--muted);
}
.gallery figcaption strong { color: var(--ink); }
@media (min-width: 40em) { .gallery { grid-template-columns: repeat(2, 1fr); } }
