@charset "UTF-8";

/* ==========================================================================
   Bring The Truck — design system

   Mobile-first: every base rule targets the small screen, and min-width media
   queries add complexity as space allows. Nothing here is a desktop layout
   walked backwards.

   No inline styles or inline <style> exist anywhere in the site, which is what
   lets the Content-Security-Policy hold `style-src 'self'` with no
   'unsafe-inline' escape hatch.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Brand */
  --navy-900: #0E2740;
  --navy-800: #12304E;
  --navy-700: #1B4066;
  /* Brand orange: decorative accents and LARGE text only (3.04:1 on white —
     passes the 3:1 large-text bar, fails the 4.5:1 normal-text bar). */
  --orange-500: #F26B21;
  /* CTA orange: 4.73:1 with white — every button/badge with normal-size
     text sits on this one. WCAG 2.2 AA is why it exists; don't "brighten"
     it back without re-checking contrast. */
  --orange-cta: #C84A0D;
  --orange-cta-hover: #A93E0B;
  --green-600: #1E9B5F;   /* decorative check icons */
  --green-700: #147A48;   /* green TEXT on white (5.4:1) */

  /* Neutrals */
  --ink: #1B2733;
  --muted: #566779;
  --line: #E3E8EC;
  --surface: #FFFFFF;
  --surface-muted: #F4F7F9;
  --hero-wash: #F6F8FA;

  /* Focus ring — overridden to white inside dark sections. */
  --focus: var(--navy-900);
  --focus-halo: rgba(242, 107, 33, 0.45);

  /* Radii */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-pill: 999px;

  /* Spacing */
  --gutter: 20px;
  --section-y: 56px;

  /* Type: a system stack, so no font is fetched from a third-party origin. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}

@media (min-width: 768px) {
  :root { --gutter: 32px; --section-y: 80px; }
}

/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/**
 * The [hidden] attribute must always win.
 *
 * The UA stylesheet gives [hidden] `display: none`, but that is a
 * single-attribute selector — any class rule setting `display` (grid, flex,
 * block) outranks it and the element renders anyway. That bug shipped twice
 * here: empty error slots showed a red warning glyph beside untouched
 * fields, and every step of the quote form rendered at once. Anything hidden
 * from the accessibility tree must be hidden on screen too, so this is the
 * one place !important is the correct tool.
 */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchored sections must clear the sticky header. */
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--surface);
  /*
   * Nothing may cause a sideways scroll at any width.
   *
   * `clip`, NOT `hidden`. overflow:hidden makes the element a scroll
   * container, and a scroll-container ancestor silently disables
   * `position: sticky` for everything inside it — which is exactly what
   * stopped the quote progress bar sticking. `clip` prevents the scroll
   * without creating that container, so sticky still works.
   */
  overflow-x: clip;
}

h1, h2, h3 { margin: 0; line-height: 1.15; color: var(--navy-900); font-weight: 800; letter-spacing: -0.02em; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, picture { max-width: 100%; }
img { height: auto; display: block; }

a { color: var(--navy-700); text-decoration-thickness: 1px; text-underline-offset: 3px; }

.icon { width: 24px; height: 24px; flex: none; }

/* --------------------------------------------------------------------------
   3. Accessibility utilities
   -------------------------------------------------------------------------- */
.sr-only {
  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;
}

.skip-link {
  position: absolute; left: 12px; top: -100px; z-index: 100;
  background: var(--navy-900); color: #fff;
  padding: 12px 18px; border-radius: var(--r-sm);
  font-weight: 700; text-decoration: none;
  transition: top .15s ease;
}
.skip-link:focus { top: 12px; }

/* One visible focus treatment for the whole site. The halo guarantees the
   ring stays perceivable even where the outline colour is close to the
   background it lands on. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
  box-shadow: 0 0 0 6px var(--focus-halo);
}

main:focus { outline: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: 1200px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }
.section--muted { background: var(--surface-muted); }
.section--narrow { padding-block: calc(var(--section-y) * .8); }

.section__title {
  font-size: clamp(1.65rem, 5vw, 2.4rem);
  text-align: center;
  margin-bottom: 2rem;
}
.section--narrow .section__title { text-align: left; }

.h3 { font-size: 1.15rem; font-weight: 800; color: var(--navy-900); margin-top: 2rem; margin-bottom: .75rem; }

/* --------------------------------------------------------------------------
   5. Buttons — 44px minimum touch target throughout
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 44px; padding: 12px 22px;
  border: 2px solid transparent; border-radius: var(--r-pill);
  font: inherit; font-weight: 700; line-height: 1.2;
  text-decoration: none; cursor: pointer;
  transition: background-color .15s ease, color .15s ease,
              border-color .15s ease, transform .1s ease;
  /* Drops the legacy 300ms tap delay and stops double-tap zoom on controls,
     which is what makes taps feel native rather than laggy. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
/* Pressed state, not hover: a touch device has no hover, so the press is the
   only feedback the visitor gets that the tap registered. */
.btn:active { transform: scale(.975); }
.m-card, .bottom-nav__link, .app-bar__action, .contact-card__row, .faq__q {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.btn--sm { min-height: 44px; padding: 10px 18px; font-size: .95rem; }
.btn--lg { min-height: 52px; padding: 14px 28px; font-size: 1.05rem; }
.btn--block { display: flex; width: 100%; }

.btn--primary { background: var(--orange-cta); color: #fff; }
.btn--primary:hover { background: var(--orange-cta-hover); }

.btn--ghost { background: transparent; color: var(--navy-800); border-color: #C8D3DC; }
.btn--ghost:hover { background: #fff; border-color: var(--navy-800); }

.btn__icon { width: 18px; height: 18px; }

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */
/* Stays white by Chris's call. A navy version matching the footer was built
   and measured (white links 15.19:1, orange current-page 4.99:1) and he chose
   white — do not "restore" it without asking. */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: #fff;
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  display: flex; align-items: center; gap: 1rem;
  min-height: 68px;
}

.logo { display: flex; align-items: center; gap: .6rem; text-decoration: none; margin-right: auto; }
.logo__mark { width: 46px; height: 26px; flex: none; }
.logo__text { display: flex; flex-direction: column; line-height: 1.05; }
.logo__name { font-size: 1.02rem; font-weight: 800; color: var(--navy-900); letter-spacing: -0.01em; }
.logo__tag { font-size: .55rem; font-weight: 700; color: var(--orange-cta); letter-spacing: .06em; }

.nav-desktop { display: none; }
.site-header__actions { display: flex; align-items: center; gap: .5rem; }

.nav-link {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 .65rem;
  color: var(--ink); font-weight: 600; font-size: .95rem; text-decoration: none;
  border-radius: var(--r-sm);
}
.nav-link:hover { color: var(--orange-cta); }
.nav-link--auth { display: none; }

.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  background: transparent; border: 1px solid var(--line); border-radius: var(--r-sm);
  color: var(--navy-900); cursor: pointer;
}
.nav-toggle__icon--close { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__icon--open { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__icon--close { display: block; }

/* Drawer for the 900-1023px band, where the horizontal nav has not appeared
   yet. Matches the bar it hangs off. */
.mobile-nav {
  border-top: 1px solid var(--line);
  background: #fff;
  padding: .5rem var(--gutter) 1.25rem;
}
.mobile-nav[hidden] { display: none; }
.mobile-nav__list { margin-bottom: .75rem; }
.mobile-nav__link {
  display: flex; align-items: center; min-height: 52px;
  font-size: 1.05rem; font-weight: 700; color: var(--navy-900); text-decoration: none;
  border-bottom: 1px solid var(--line);
}

@media (min-width: 1024px) {
  .nav-desktop { display: block; margin-right: .5rem; }
  .nav-desktop__list { display: flex; align-items: center; gap: .25rem; }
  .nav-link--auth { display: inline-flex; border-left: 1px solid var(--line); margin-left: .5rem; padding-left: 1rem; }
  .nav-toggle { display: none; }
  .mobile-nav { display: none !important; }
}

/* --------------------------------------------------------------------------
   7. Hero

   Mobile order is content first, image second — which is also the DOM order,
   so no reordering trickery is needed and the reading order matches the
   visual order. On wide screens the image becomes a full-bleed panel on the
   right and a gradient fades its left edge into the page wash.
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  background: linear-gradient(180deg, #FFFFFF 0%, var(--hero-wash) 100%);
  overflow: hidden;
}

.hero__inner { position: relative; z-index: 2; padding-block: 2.5rem 1.75rem; }
.hero__content { max-width: 34rem; }

.hero__title {
  font-size: clamp(2.6rem, 13vw, 3.6rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  margin-bottom: 1rem;
}
.hero__line { display: block; }
/* The verbs in brand orange — the words the promise pivots on, in the same
   grammar as the wordmark's single accented word. Upright: the emphasis is
   semantic, not italic. --orange-500 is licensed for large text only; this
   title never renders below 2.9rem. */
.hero__title em { font-style: normal; color: var(--orange-500); }

.hero__lede {
  font-size: clamp(1rem, 4vw, 1.12rem);
  color: var(--muted);
  max-width: 30rem;
  margin-bottom: 1.5rem;
}

.hero__points {
  display: flex; flex-wrap: wrap; gap: .75rem 1.5rem;
  margin-bottom: 1.75rem;
}
.hero__points li {
  display: flex; align-items: center; gap: .5rem;
  font-size: .9rem; font-weight: 700; color: var(--navy-900);
}
.hero__points .icon { width: 22px; height: 22px; color: var(--navy-800); }

.hero__actions { display: flex; flex-direction: column; gap: .75rem; margin-bottom: 1.25rem; }

.hero__price {
  display: flex; align-items: center; gap: .5rem;
  font-size: 1rem; color: var(--ink);
}
.hero__price strong { color: var(--navy-900); }
.hero__price-icon { color: var(--green-600); width: 22px; height: 22px; }

/* Secondary routes out of the hero. Quote and How-it-works were buttons until
   the two role actions took that slot; they stay one tap away as text rather
   than competing with the choice the page is actually asking for. */
.hero__alt {
  margin: 0.85rem 0 0;
  font-size: 0.95rem;
  color: var(--text-muted, #5A6B7B);
}
.hero__alt a { color: inherit; text-decoration: underline; }
.hero__alt a:hover { color: var(--brand-navy, #12304E); }

.hero__media { position: relative; }
/* The film takes the photograph's box, unchanged. Listing `video` beside
   `img` everywhere rather than restyling means the hero's proportions,
   cropping and breakpoints are still described in exactly one place. */
.hero__media img,
.hero__media video { width: 100%; height: auto; display: block; }

/* ---- The desktop hand-off ------------------------------------------------
   Hidden by default so the form is what a phone gets, and so any browser
   that cannot answer the media query below still books normally. */
.handoff { display: none; }

/* `pointer: fine` and `hover: hover` together mean a mouse-driven machine;
   phones and tablets report coarse and none. The width test is belt and
   braces — a narrow laptop window gets the form back, which is the failure
   direction we want. */
@media (min-width: 900px) and (pointer: fine) and (hover: hover) {
  .handoff { display: block; padding: 3rem 1.25rem 4rem; }
  .handoff__card {
    max-width: 30rem; margin: 0 auto; text-align: center;
    background: #fff; border: 1px solid var(--line); border-radius: 18px;
    padding: 2.25rem 2rem 2rem;
    box-shadow: 0 1px 2px rgba(15, 42, 74, .05), 0 10px 30px rgba(15, 42, 74, .08);
  }
  .handoff__h { font-size: 1.9rem; letter-spacing: -.02em; margin: 0 0 .6rem; }
  .handoff__p { color: var(--muted); line-height: 1.6; margin: 0 0 1.5rem; }
  .handoff__qr { display: flex; justify-content: center; margin: 0 0 1.25rem; }
  /* A fixed pixel size, not a percentage: a QR below about 160px is
     unreliable at arm's length, and crispEdges only helps if the modules
     land on whole pixels. */
  .handoff__svg { width: 232px; height: 232px; display: block; }
  .handoff__url { font-size: .92rem; color: var(--muted); margin: 0; }
  .handoff__url strong { color: var(--navy-900); }

  /* And the form goes away — the owner asked for the code INSTEAD of it.

     ADDRESSED AS THE HAND-OFF'S SIBLING, not as `.quote-page` on its own.
     A bare `.quote-page { display: none }` here loses: the desktop grid rule
     further down this file sets `display: grid` at the same specificity, and
     later wins. Measured on the deployed page — the panel appeared and the
     form stayed right underneath it.

     The sibling selector is a genuine description of the relationship (the
     form that follows a hand-off) and, at two classes, outranks every
     single-class rule wherever it sits in the file. */
  .handoff + .quote-page { display: none; }
}

/* THE FULL-BLEED BAND. A direct child of .hero, which is full width and
   unpadded, so this is edge to edge with no viewport units involved.

   The height is `min(natural, cap)`: 56.25vw is exactly 16:9 of the full
   viewport width, so below the cap the film plays uncropped, and above it
   the frame trims instead of the box shrinking. Using max-height for this
   was the bug — a capped height with an intrinsic ratio pulls the WIDTH in
   to match, which is how a full-bleed film ended up 992px wide in a 1265px
   viewport. object-fit is what makes cropping the outcome instead. */
.hero__media--bleed { width: 100%; }
/* Below the desktop breakpoint the filmed hero is stacked, and the split
   markup would otherwise render an orphaned headline block above the film's
   sibling column. Keep both containers in normal flow; the headline simply
   comes first. */
.hero__below { padding-block: 0 2rem; }
.hero__media--bleed .hero__film {
  width: 100%;
  height: min(56.25vw, 62vh);
  max-height: none;
  object-fit: cover;
  object-position: 50% 50%;
}

@media (min-width: 640px) {
  .hero__actions { flex-direction: row; flex-wrap: wrap; }
}

@media (min-width: 980px) {
  /* min-height only sets the hero's visual proportion — below ~1200px the
     wrapped text is taller than it anyway, so it cannot be relied on to
     control the panel's aspect ratio. Protecting the truck's nose is the
     job of object-position below. */
  .hero__inner { padding-block: 4rem 4.5rem; min-height: 27rem; display: flex; align-items: center; }
  .hero__content { max-width: 27rem; }
  .hero__title { font-size: clamp(2.9rem, 4.4vw, 4.4rem); }

  .hero__media {
    position: absolute; top: 0; right: 0; bottom: 0; left: auto;
    width: 64%; z-index: 1;
  }
  .hero__media img,
  .hero__media video,
  .hero__media picture { width: 100%; height: 100%; display: block; }
  /* X is biased toward the vehicle. Wide screens make the panel wider than the
     photo's 16:9, so cover crops only vertically and X is inert. Narrower
     desktops (~980-1150px) make the panel proportionally taller, and there
     cover crops sideways.

     The subject sits deliberately right-of-centre: the five adults span 44-76%
     of the frame and the truck runs 62-100%, leaving 0-44% as open car park
     for the headline. A high X therefore spends the sideways crop on that
     empty left edge — which the gradient below already washes out — instead
     of on a person. (X was 22% for a much older hero whose truck sat at the
     LEFT; that value would now cut the group.)

     Checked by simulating cover at panel aspects 1.20 through 1.60: at 72%
     the narrowest case still shows 23%-91% of the frame, so all five adults
     survive with room to spare.
     Y biases the vertical crop downward, favouring the group and truck over
     empty sky while keeping their feet in frame.
     Verify at 980/1024/1200/1440/1920 after any hero swap. */
  .hero__media img { object-fit: cover; object-position: 72% 62%; }
  /* Centred, unlike the still: the photograph was composed with the truck's
     nose off to one side and needed protecting from the crop. The film is a
     moving shot and its subject is in the middle of frame. */
  .hero__media video { object-fit: cover; object-position: 50% 50%; }

  /* FULL WIDTH ON DESKTOP TOO. "it should be taking up the full width of the
     viewport."

     THE FIX IS POSITIONING, NOT WIDTH, and that is the whole reason two
     earlier attempts missed. Above 980px .hero__media is not a column beside
     the text — it is an ABSOLUTELY POSITIONED 64% panel pinned to the right
     with the words laid over it (see the rule above). Setting width:100% on
     a right-pinned absolute box just makes a wider overlay behind the
     headline; measured, the film stayed 810px at left 455.

     Static puts it back in the flow at the top of the section, full width,
     and .hero__inner then falls beneath it — which is the stacked hero the
     owner asked for. */
  /* THE WORDS RETURN TO THE PICTURE. The owner: "you can put the text over
     the video like you did for the photo along with the tag line."

     Grid stacking, not the photo's absolute 64% panel: the full-frame rule
     below can make the film NARROWER than the viewport on a short window,
     and an overlay pinned to the section would drift off the picture and
     onto the wash. Both children share one grid cell, and the text column
     takes the same width formula as the film's box, so the words ride the
     frame wherever it sits. If the column ever outgrows the frame, the
     section grows with it — grid sizes the cell to the taller child — and
     nothing is clipped by the section's overflow:hidden. */
  .hero--filmed { display: grid; }
  .hero--filmed .hero__media--bleed,
  .hero--filmed .hero__inner { grid-area: 1 / 1; }
  .hero--filmed .hero__media--bleed { position: static; width: 100%; }
  /* THE WHOLE FRAME, ALWAYS. The owner: "make it so the full image is always
     in view on the home page for the desktop." The old sizing capped height
     at 62vh and let object-fit crop whatever no longer fit, so a short
     window was shown a film with its top and bottom missing.

     Now the BOX yields instead of the picture: its width is whatever lets a
     16:9 frame fit beside the 69px sticky header (68 min-height + 1 border),
     clamped to the section. Tall window: full bleed, exactly as before.
     Short window: the film narrows and centres, complete.

     On the media DIV, not the film, for two reasons: the legibility fade is
     the div's ::after with inset 0, so it keeps hugging the film's real
     edges; and a width on the div avoids the intrinsic-ratio trap noted at
     the base rule. svh, with a vh line for older engines — 100vw is NOT used
     anywhere here, because it includes the scrollbar and reintroduces the
     sideways-scroll bug this file has already fought once. */
  .hero--filmed .hero__media--bleed {
    width: min(100%, calc((100vh - 69px) * 16 / 9));
    width: min(100%, calc((100svh - 69px) * 16 / 9));
    margin-inline: auto;
  }
  .hero--filmed .hero__media--bleed .hero__film {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
  }
  .hero--filmed .hero__inner {
    /* Upper third, not centred: the film's midline belongs to the truck
       driving through it, and vertical centring parked the headline right
       on the bodywork — navy on near-black. The upper left is sky and
       trees on every frame of the shot, which is where the fade can
       actually earn the contrast. This is also where his screenshot put
       it: "keep the position of the text." */
    z-index: 2; align-self: start; justify-self: center;
    padding-block-start: clamp(2.5rem, 12vh, 7rem);
    width: min(100%, calc((100vh - 69px) * 16 / 9));
    width: min(100%, calc((100svh - 69px) * 16 / 9));
    min-height: 0; padding-block: 0;
  }
  /* NO FADE ON THE FILM — the owner's call, made looking at the live page
     with the headline in the upper third. Up there the words sit on open
     sky, the film's own brightest region, and the wash was mist on the
     scene rather than help for the text. The no-film fallback keeps its
     fades: that layout still lays a full column over the photograph.

     If the headline ever moves back toward the frame's midline, this is
     the rule to revisit first — the fade left because the words moved,
     and it would be needed again for the same reason it existed. */
  .hero--filmed .hero__media::after { display: none; }
  /* The shadow the fade used to be. The owner: "put a shadow under the text
     so that it is more readable." A light halo plus a faint navy drop — the
     halo does the readability work (dark letters gain contrast from bright
     surroundings, not from darkness), and the drop gives the glyph edges
     definition against the busier parts of the sky. Letters carry their own
     treatment wherever they sit, so unlike the fade this cannot mist the
     scene around them. */
  .hero--filmed .hero__title {
    text-shadow:
      0 1px 2px rgba(255, 255, 255, .65),
      0 2px 22px rgba(255, 255, 255, .80),
      0 2px 5px rgba(10, 31, 58, .22);
  }
  .hero--filmed .hero__content { max-width: 27rem; margin: 0; }
  /* The rest of the words, under the frame. Wider than the overlay column —
     these paragraphs are read, not glanced at, and they no longer share
     space with a truck. */
  .hero__below { padding-block: 2.5rem 3rem; }
  .hero__below .hero__content { max-width: 34rem; }

  /* Fades the photo's left edge into the page so the headline stays legible
     without a scrim over the text itself. The fade must finish before the
     truck's front end — reaching too far right washes out the grille, the
     most recognisable part of the vehicle. */
  .hero__media::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(
      to right,
      var(--hero-wash) 0%,
      rgba(246, 248, 250, 0.90) 12%,
      rgba(246, 248, 250, 0.45) 24%,
      rgba(246, 248, 250, 0.12) 33%,
      rgba(246, 248, 250, 0) 40%
    );
  }
}

@media (min-width: 1200px) {
  .hero__inner { padding-block: 5rem 5.5rem; min-height: 31rem; }
  .hero__content { max-width: 30rem; }

  .hero__media::after {
    background: linear-gradient(
      to right,
      var(--hero-wash) 0%,
      rgba(246, 248, 250, 0.88) 9%,
      rgba(246, 248, 250, 0.42) 19%,
      rgba(246, 248, 250, 0.10) 28%,
      rgba(246, 248, 250, 0) 35%
    );
  }
}

/* ---- The fade must match the page it fades into -------------------------
   The section paints a VERTICAL wash, #FFFFFF at the top down to
   --hero-wash at the foot. The photo's left-edge fade washed to a FLAT
   --hero-wash at every height, so the two agreed only along the very
   bottom. Everywhere above that the fade's grey met a whiter page and left
   a hard vertical edge down the side of the photograph — measured at the
   top of the hero as (254,254,255) beside (246,248,250).

   So the overlay now paints the SAME vertical wash as the section and is
   revealed only on the left by a mask. Identical colour at every height
   means there is no edge to see at any height.

   Behind @supports because the fallback matters: without masking the
   overlay is an opaque wash across the whole panel, which would cover the
   photograph. Browsers that cannot mask keep the old flat fade — a faint
   seam is a great deal better than no truck. */
@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  @media (min-width: 980px) {
    .hero__media::after {
      background: linear-gradient(180deg, #FFFFFF 0%, var(--hero-wash) 100%);
      -webkit-mask-image: linear-gradient(to right,
        #000 0%, rgba(0, 0, 0, .90) 12%, rgba(0, 0, 0, .45) 24%,
        rgba(0, 0, 0, .12) 33%, transparent 40%);
      mask-image: linear-gradient(to right,
        #000 0%, rgba(0, 0, 0, .90) 12%, rgba(0, 0, 0, .45) 24%,
        rgba(0, 0, 0, .12) 33%, transparent 40%);
    }
  }

  @media (min-width: 1200px) {
    .hero__media::after {
      -webkit-mask-image: linear-gradient(to right,
        #000 0%, rgba(0, 0, 0, .88) 9%, rgba(0, 0, 0, .42) 19%,
        rgba(0, 0, 0, .10) 28%, transparent 35%);
      mask-image: linear-gradient(to right,
        #000 0%, rgba(0, 0, 0, .88) 9%, rgba(0, 0, 0, .42) 19%,
        rgba(0, 0, 0, .10) 28%, transparent 35%);
    }
  }
}

@media (min-width: 1400px) {
  .hero__inner { min-height: 34rem; }
  .hero__content { max-width: 32rem; }
  .hero__media { width: 62%; }
}

/* --------------------------------------------------------------------------
   8. Benefits strip
   -------------------------------------------------------------------------- */
.strip { background: var(--navy-800); color: #fff; --focus: #fff; }
.strip__grid { display: grid; gap: 1.25rem; padding-block: 1.75rem; }
.strip__item { display: flex; align-items: center; gap: .85rem; }
.strip__icon { width: 30px; height: 30px; color: #fff; opacity: .95; }
.strip__title { display: block; font-weight: 800; font-size: .82rem; letter-spacing: .06em; text-transform: uppercase; }
.strip__sub { display: block; font-size: .9rem; color: #C6D5E2; }

@media (min-width: 560px) { .strip__grid { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; } }
@media (min-width: 1024px) { .strip__grid { grid-template-columns: repeat(4, 1fr); padding-block: 2rem; } }

/* --------------------------------------------------------------------------
   9. How it works
   -------------------------------------------------------------------------- */
.steps { display: grid; gap: 2.25rem; }

.step__art {
  display: flex; align-items: center; justify-content: center;
  width: 72px; height: 72px; margin-bottom: 1rem;
  background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg);
}
.step__icon { width: 34px; height: 34px; color: var(--navy-800); }

.step__head { display: flex; align-items: center; gap: .65rem; margin-bottom: .5rem; }
.step__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  background: var(--orange-cta); color: #fff;
  border-radius: 50%; font-size: .9rem; font-weight: 800;
}
.step__title { font-size: 1.08rem; }
.step__body { color: var(--muted); font-size: .96rem; }

@media (min-width: 640px) { .steps { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 2rem; } }
@media (min-width: 1024px) { .steps { grid-template-columns: repeat(4, 1fr); } }

/* --------------------------------------------------------------------------
   10. Pricing
   -------------------------------------------------------------------------- */
.pricing {
  margin-top: 3.5rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 1.75rem var(--gutter);
  display: grid; gap: 2rem;
}

.pricing__title { font-size: clamp(1.4rem, 5vw, 1.9rem); margin-bottom: 1.25rem; }
.pricing__label { font-weight: 700; color: var(--navy-900); }
.pricing__figure {
  font-size: clamp(3rem, 14vw, 4rem); font-weight: 800; color: var(--orange-500);
  line-height: 1; letter-spacing: -0.04em; margin-bottom: 1rem;
}
.pricing__currency { font-size: .55em; vertical-align: .32em; margin-right: .04em; }

.pricing__note { color: var(--ink); margin-bottom: .85rem; }

.pricing__factors { display: grid; gap: .5rem; margin-bottom: 1.25rem; }
.pricing__factors li { display: flex; align-items: flex-start; gap: .6rem; font-size: .95rem; }
.pricing__check { width: 20px; height: 20px; color: var(--green-600); margin-top: 3px; }

.pricing__server-note {
  font-size: .9rem; color: var(--muted);
  border-left: 3px solid var(--orange-500);
  padding-left: .85rem; margin-bottom: 1.5rem;
}

/* Illustrative quote card */
.phone { margin: 0; }
.phone__caption {
  font-size: .78rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: .6rem; text-align: center;
}
.phone__frame {
  background: var(--navy-900); border-radius: 26px; padding: 10px;
  max-width: 300px; margin-inline: auto;
  box-shadow: 0 18px 40px rgba(14, 39, 64, .18);
}
.phone__screen { background: #fff; border-radius: 18px; padding: 1.1rem; }
.phone__heading { font-weight: 800; color: var(--navy-900); margin-bottom: .75rem; }
.phone__row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .5rem 0; border-top: 1px solid var(--line);
  font-size: .85rem; color: var(--muted);
}
.phone__row--total { border-top: 0; font-size: .95rem; color: var(--navy-900); font-weight: 700; }
.phone__price { color: var(--green-700); font-weight: 800; }
.phone__cta {
  margin-top: .9rem; background: var(--orange-cta); color: #fff;
  text-align: center; font-weight: 800; padding: .7rem; border-radius: var(--r-pill);
}
.phone__disclaimer { margin-top: .6rem; font-size: .7rem; color: var(--muted); text-align: center; }

@media (min-width: 900px) {
  .pricing { grid-template-columns: 1.15fr .85fr; align-items: center; gap: 3rem; padding: 2.75rem; }
}

/* --------------------------------------------------------------------------
   11. Trust + safety
   -------------------------------------------------------------------------- */
.trust { display: grid; gap: 1.75rem; }
.trust__item { text-align: center; }
.trust__icon { width: 34px; height: 34px; color: var(--navy-800); margin-inline: auto; margin-bottom: .75rem; }
.trust__title { font-size: 1rem; margin-bottom: .35rem; }
.trust__body { font-size: .92rem; color: var(--muted); }

.safety {
  margin-top: 3rem; padding: 1.5rem;
  background: var(--surface-muted); border-radius: var(--r-lg);
}
.safety__title { font-size: 1.05rem; margin-bottom: .85rem; }
.safety__list { display: grid; gap: .6rem; }
.safety__list li { display: flex; align-items: flex-start; gap: .6rem; font-size: .95rem; }
.safety__check { width: 20px; height: 20px; color: var(--green-600); margin-top: 3px; }

@media (min-width: 560px) { .trust { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) {
  .trust { grid-template-columns: repeat(4, 1fr); gap: 2rem; }
  .safety { padding: 2rem 2.5rem; }
  .safety__list { grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
}

/* --------------------------------------------------------------------------
   12. Final CTA
   -------------------------------------------------------------------------- */
.cta { background: var(--navy-800); color: #fff; --focus: #fff; padding-block: 2.5rem; }
.cta__inner { display: grid; gap: 1.5rem; }
.cta__title { color: #fff; font-size: clamp(1.35rem, 5vw, 1.85rem); margin-bottom: .4rem; }
.cta__sub { color: #C6D5E2; font-size: .98rem; }
.cta__btn { justify-self: start; }

@media (min-width: 860px) {
  .cta__inner { grid-template-columns: 1fr auto; align-items: center; gap: 2rem; }
  .cta__btn { justify-self: end; }
}

/* --------------------------------------------------------------------------
   13. Footer
   -------------------------------------------------------------------------- */
.site-footer { background: var(--navy-900); color: #C6D5E2; --focus: #fff; padding-block: 2.5rem 1.5rem; }
.site-footer__inner { display: grid; gap: 2rem; }
.site-footer__name { display: block; font-weight: 800; color: #fff; letter-spacing: .02em; }
.site-footer__tag { display: block; font-size: .85rem; color: var(--orange-500); font-weight: 700; }

.site-footer__nav ul { display: grid; gap: .6rem; }
.site-footer a { color: #C6D5E2; text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.site-footer__nav a, .site-footer__contact a {
  display: inline-flex; align-items: center; min-height: 44px;
}

.site-footer__contact-title { font-size: .82rem; letter-spacing: .06em; text-transform: uppercase; color: #fff; margin-bottom: .25rem; }

.site-footer__legal {
  margin-top: 2rem; padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: .85rem;
}

@media (min-width: 768px) {
  .site-footer__inner { grid-template-columns: 1.2fr 1fr 1fr; gap: 2.5rem; }
  .site-footer__nav ul { gap: .1rem; }
}

/* --------------------------------------------------------------------------
   14. Notices / lists (quote + accounts placeholders, error page)
   -------------------------------------------------------------------------- */
.notice {
  background: var(--surface-muted);
  border-left: 4px solid var(--orange-500);
  border-radius: var(--r-md);
  padding: 1.25rem 1.35rem;
  display: grid; gap: .75rem;
}
.notice__lead { font-weight: 800; color: var(--navy-900); }

.list-checks { display: grid; gap: .6rem; margin-bottom: 2rem; }
.list-checks li { display: flex; align-items: flex-start; gap: .6rem; }
.list-checks .icon { width: 20px; height: 20px; color: var(--green-600); margin-top: 4px; }

.page-error { padding: 3rem 0; }
.page-error h1 { margin-bottom: 1rem; }
.page-error p { margin-bottom: .75rem; }

/* ==========================================================================
   15. MOBILE APP SHELL  (below 900px)

   Below this breakpoint the site is not a narrowed desktop page — it is an
   application shell: fixed top app bar, fixed bottom navigation, and screens
   in between. Both shells are in the DOM and swapped here, which keeps the
   markup cacheable and leaves exactly one navigation in the accessibility
   tree (display:none removes the other entirely).
   ========================================================================== */

:root {
  --app-bar-h: 56px;
  --bottom-nav-h: 60px;
  /* Resolve to 0 everywhere except notched devices, where viewport-fit=cover
     in the meta tag makes them real. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --m-pad: 18px;
}

.app-bar, .bottom-nav, .m-home, .install-sheet { display: none; }

@media (max-width: 899.98px) {
  /* Desktop chrome is fully removed, not merely restyled. */
  .site-header, .site-footer, .d-only { display: none !important; }

  body {
    background: var(--surface-muted);
    /* Content clears both fixed bars, plus the home indicator. */
    padding-top: calc(var(--app-bar-h) + var(--safe-t));
    padding-bottom: calc(var(--bottom-nav-h) + var(--safe-b) + 8px);
  }

  /* ---- Top app bar --------------------------------------------------- */
  .app-bar {
    display: block;
    position: fixed; top: 0; left: 0; right: 0; z-index: 60;
    background: #fff;
    border-bottom: 1px solid var(--line);
    padding-top: var(--safe-t);
    padding-left: var(--safe-l); padding-right: var(--safe-r);
  }
  .app-bar__inner {
    height: var(--app-bar-h);
    display: flex; align-items: center; justify-content: space-between;
    padding-inline: var(--m-pad);
  }
  /* The logo is a link home, so it is a touch target and must clear 44px
     even though the artwork itself is 40px tall. */
  .app-bar .logo { display: flex; align-items: center; min-height: 44px; }
  .app-bar .logo__img { display: block; }
  .app-bar__action {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-right: -10px;
    border-radius: 50%; color: var(--navy-800); text-decoration: none;
  }
  .app-bar__action:active { background: var(--surface-muted); }

  /* ---- Bottom navigation --------------------------------------------- */
  .bottom-nav {
    display: block;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    background: #fff;
    border-top: 1px solid var(--line);
    padding-bottom: var(--safe-b);
    padding-left: var(--safe-l); padding-right: var(--safe-r);
  }
  .bottom-nav__list {
    display: grid; grid-template-columns: repeat(5, 1fr);
    align-items: end;
    height: var(--bottom-nav-h);
  }
  .bottom-nav__link {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; height: 100%; min-height: 44px;
    color: var(--muted); text-decoration: none;
    font-size: .68rem; font-weight: 700; letter-spacing: .01em;
  }
  .bottom-nav__icon { display: flex; }
  .bottom-nav__icon .icon { width: 23px; height: 23px; }
  .bottom-nav__link.is-active { color: var(--navy-800); }
  /* Active state is carried by weight + an indicator bar as well as colour,
     so it is not communicated by colour alone. */
  .bottom-nav__link.is-active .bottom-nav__icon { position: relative; }
  .bottom-nav__link.is-active .bottom-nav__icon::after {
    content: ""; position: absolute; left: 50%; top: -9px;
    transform: translateX(-50%);
    width: 20px; height: 3px; border-radius: 2px;
    background: var(--orange-cta);
  }
  .bottom-nav__link:active { background: var(--surface-muted); }

  /* Centre Quote action: raised, brand-coloured, unmistakably the primary
     task and one tap from every screen. */
  .bottom-nav__item--primary { position: relative; }
  .bottom-nav__item--primary .bottom-nav__link {
    color: #fff;
    position: absolute; left: 50%; bottom: 8px;
    transform: translateX(-50%);
    width: 60px; height: 60px; min-height: 60px;
    border-radius: 50%;
    background: var(--orange-cta);
    box-shadow: 0 6px 18px rgba(200, 74, 13, .38);
    gap: 1px;
  }
  .bottom-nav__item--primary .bottom-nav__link:active {
    background: var(--orange-cta-hover);
    transform: translateX(-50%) scale(.95);
  }
  .bottom-nav__item--primary .bottom-nav__icon .icon { width: 22px; height: 22px; }
  .bottom-nav__item--primary .bottom-nav__label { font-size: .6rem; }
  .bottom-nav__item--primary .bottom-nav__link.is-active .bottom-nav__icon::after { display: none; }
  .bottom-nav__item--primary .bottom-nav__link.is-active {
    box-shadow: 0 0 0 4px rgba(200, 74, 13, .22), 0 6px 18px rgba(200, 74, 13, .38);
  }

  /* ---- Mobile home ---------------------------------------------------- */
  .m-home { display: block; padding: 0 0 1.5rem; }

  .m-hero {
    background: #fff;
    padding: 1.35rem var(--m-pad) 1.25rem;
    border-bottom: 1px solid var(--line);
  }
  .m-hero__eyebrow {
    font-size: .72rem; font-weight: 800; letter-spacing: .09em;
    text-transform: uppercase; color: var(--orange-cta); margin-bottom: .5rem;
  }
  .m-hero__title {
    font-size: clamp(2.1rem, 10.5vw, 2.7rem); line-height: 1.03;
    letter-spacing: -0.035em; margin-bottom: .6rem;
  }
  .m-hero__title span { display: block; }
  .m-hero__lede { color: var(--muted); font-size: .97rem; margin-bottom: .85rem; }
  .m-hero__price {
    display: flex; align-items: center; gap: .5rem;
    font-size: .95rem; margin-bottom: 1.1rem;
  }
  .m-hero__price .icon { width: 20px; height: 20px; color: var(--green-600); flex: none; }
  .m-hero__price strong { color: var(--navy-900); }
  .m-hero__actions { display: grid; gap: .6rem; }

  .m-hero__media { padding: 0; background: #fff; border-bottom: 1px solid var(--line); }
  /* The mobile source is a deliberate 2:1 crop, so the box tracks 2:1 rather
     than a fixed height. A fixed 190px became a 4:1 letterbox by 768px, which
     cut the group off at the hip and pushed the store name out of frame
     entirely — the one element that establishes the retail setting.
     min-height keeps the familiar band on small phones (320px crops sideways
     by ~16%, which the group survives); max-height stops it dominating at the
     top of the mobile shell. The primary action sits ABOVE this image, so a
     taller box never pushes the CTA down. */
  .m-hero__media img,
  .m-hero__media video {
    width: 100%; height: auto;
    aspect-ratio: 2 / 1;
    min-height: 190px; max-height: 420px;
    object-fit: cover; object-position: 62% 55%; display: block;
  }

  .m-section-title {
    font-size: .78rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--muted); padding: 1.35rem var(--m-pad) .6rem;
  }

  .m-cards { display: grid; gap: .6rem; padding-inline: var(--m-pad); }
  .m-card {
    display: flex; align-items: center; gap: .85rem;
    min-height: 68px; padding: .85rem 1rem;
    background: #fff; border: 1px solid var(--line); border-radius: 14px;
    text-decoration: none; color: inherit;
    box-shadow: 0 1px 2px rgba(14, 39, 64, .04);
    transition: transform .12s ease, background-color .12s ease;
  }
  .m-card:active { transform: scale(.985); background: var(--surface-muted); }
  .m-card__icon {
    display: flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; flex: none;
    background: var(--surface-muted); border-radius: 11px; color: var(--navy-800);
  }
  .m-card--accent .m-card__icon { background: rgba(200, 74, 13, .1); color: var(--orange-cta); }
  .m-card--accent { border-color: rgba(200, 74, 13, .32); }
  .m-card__text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
  .m-card__title { font-weight: 800; color: var(--navy-900); font-size: .98rem; }
  .m-card__sub { color: var(--muted); font-size: .84rem; }
  .m-card__chevron { color: #A7B4C0; display: flex; flex: none; }
  .m-card__chevron .icon { width: 20px; height: 20px; }

  .m-foot-note {
    text-align: center; color: var(--muted); font-size: .78rem;
    padding: 1.75rem var(--m-pad) .5rem;
  }

  /* ---- Sub-pages ------------------------------------------------------ */
  .page { padding: 1.25rem 0 1.5rem; }
  .page .container { padding-inline: var(--m-pad); }
  .page__title { font-size: 1.75rem; margin-bottom: .5rem; }
  .page__lede { color: var(--muted); margin-bottom: 1.25rem; }

  .m-steps { display: grid; gap: .7rem; margin-bottom: 1.5rem; }
  .m-step {
    display: grid; grid-template-columns: auto auto 1fr; align-items: start; gap: .8rem;
    background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1rem;
  }
  .m-step__num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--orange-cta); color: #fff; font-weight: 800; font-size: .82rem;
  }
  .m-step__icon { color: var(--navy-800); display: flex; }
  .m-step__text { display: flex; flex-direction: column; }
  .m-step__title { font-weight: 800; color: var(--navy-900); }
  .m-step__body { color: var(--muted); font-size: .9rem; }

  .price-card {
    background: #fff; border: 1px solid var(--line); border-radius: 16px;
    padding: 1.35rem; text-align: center; margin-bottom: 1.5rem;
  }
  .price-card__label { font-weight: 700; color: var(--navy-900); }
  .price-card__figure {
    font-size: 3.4rem; font-weight: 800; color: var(--orange-500);
    line-height: 1; letter-spacing: -.04em; margin: .35rem 0 .4rem;
  }
  .price-card__currency { font-size: .5em; vertical-align: .35em; }
  .price-card__unit { font-weight: 800; color: var(--navy-900); margin-top: -.25rem; margin-bottom: .3rem; }
  .price-card__note { color: var(--muted); font-size: .9rem; }
  .price-card__note strong { color: var(--navy-900); }

  .contact-card { display: grid; gap: .5rem; margin-bottom: 1.5rem; }
  .contact-card__row {
    display: flex; align-items: center; gap: .75rem; min-height: 56px;
    background: #fff; border: 1px solid var(--line); border-radius: 14px;
    padding: .85rem 1rem; text-decoration: none; color: var(--navy-900); font-weight: 700;
  }
  .contact-card__row:active { background: var(--surface-muted); }

  .faq { display: grid; gap: .5rem; margin-bottom: 1.5rem; }
  .faq__item { background: #fff; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
  .faq__q {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    min-height: 56px; padding: .9rem 1rem; cursor: pointer;
    font-weight: 700; color: var(--navy-900); list-style: none;
  }
  .faq__q::-webkit-details-marker { display: none; }
  .faq__marker { color: var(--muted); display: flex; transition: transform .18s ease; }
  .faq__item[open] .faq__marker { transform: rotate(180deg); }
  .faq__a { padding: 0 1rem 1rem; color: var(--muted); font-size: .92rem; }

  .callout {
    background: #fff; border: 1px solid var(--line); border-radius: 14px;
    padding: 1rem; display: grid; gap: .6rem; margin-bottom: 1.5rem;
    font-size: .92rem; color: var(--muted);
  }
  .callout strong { color: var(--navy-900); }
  .callout--accent { border-left: 4px solid var(--orange-cta); }

  .section--narrow .container { padding-inline: var(--m-pad); }

}

/* .m-home is its own page now (/customer), not the mobile half of `/`, so it
   must render at every width. Hiding it above 900px left that route blank on a
   desktop. The app-bar and bottom-nav around it stay mobile-only; on a wide
   screen the customer page simply sits inside the normal site chrome. */
/* Desktop keeps the classic site; mobile-only blocks stay hidden. */
@media (min-width: 900px) {
  .m-home { display: none !important; }
}

/* ==========================================================================
   16. FORMS  (quote screen)
   ========================================================================== */
.quote-form { display: grid; gap: 1.1rem; }

.field { display: grid; gap: .35rem; }
.field__label { font-weight: 700; color: var(--navy-900); font-size: .95rem; }
.field__input {
  font: inherit; color: var(--ink);
  min-height: 52px; padding: .8rem .9rem;
  background: #fff;
  border: 1.5px solid #CFD8E0; border-radius: 12px;
  width: 100%;
  /* 16px minimum stops iOS zooming the viewport when a field is focused. */
  font-size: 16px;
}
.field__input--area { min-height: 96px; resize: vertical; }
.field__input:focus-visible { border-color: var(--navy-800); }
.field__hint { font-size: .82rem; color: var(--muted); }

.field__error {
  display: flex; align-items: center; gap: .35rem;
  font-size: .85rem; font-weight: 600; color: #B3261E;
}
.field__error::before { content: "⚠"; font-size: .9em; }
.field.is-invalid .field__input { border-color: #B3261E; background: #FFF7F6; }

.quote-form__legal { font-size: .82rem; color: var(--muted); text-align: center; }

.notice--pending { border-left-color: var(--navy-800); }

/* ==========================================================================
   17. PWA SURFACES — status, update, install
   ========================================================================== */
.status-stack {
  position: fixed; left: 0; right: 0; z-index: 80;
  top: calc(var(--safe-t)); pointer-events: none;
}
@media (min-width: 900px) { .status-stack { top: 0; } }

.status-bar {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .55rem 1rem; font-size: .86rem; font-weight: 700; color: #fff;
}
.status-bar[hidden] { display: none; }
.status-bar--offline { background: #8A5A00; }
.status-bar--back { background: var(--green-600); }
.status-bar__dot {
  width: 8px; height: 8px; border-radius: 50%; background: currentColor;
  flex: none; opacity: .9;
}

.update-toast {
  position: fixed; z-index: 85;
  left: 12px; right: 12px;
  bottom: calc(var(--bottom-nav-h) + var(--safe-b) + 12px);
  display: flex; align-items: center; gap: .75rem;
  background: var(--navy-900); color: #fff;
  border-radius: 14px; padding: .85rem 1rem;
  box-shadow: 0 10px 30px rgba(14, 39, 64, .3);
  --focus: #fff;
}
.update-toast[hidden] { display: none; }
.update-toast__text { display: flex; flex-direction: column; flex: 1 1 auto; font-size: .85rem; }
.update-toast__text span { color: #C6D5E2; }
.update-toast__dismiss {
  background: transparent; border: 0; color: #C6D5E2; cursor: pointer;
  width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center;
}
@media (min-width: 900px) {
  .update-toast { left: auto; right: 24px; bottom: 24px; max-width: 26rem; }
}

.install-sheet {
  position: fixed; z-index: 90; left: 0; right: 0; bottom: 0;
  background: #fff;
  border-radius: 20px 20px 0 0;
  padding: .6rem var(--m-pad) calc(1rem + var(--safe-b));
  box-shadow: 0 -8px 40px rgba(14, 39, 64, .22);
  animation: sheet-up .28s ease;
}
@media (max-width: 899.98px) { .install-sheet { display: block; } }

@keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }

.install-sheet__grip {
  width: 40px; height: 4px; border-radius: 2px; background: #D6DEE5;
  margin: 0 auto .9rem;
}
.install-sheet__body { display: flex; align-items: center; gap: .85rem; margin-bottom: 1rem; }
.install-sheet__icon { border-radius: 11px; }
.install-sheet__title { font-weight: 800; color: var(--navy-900); }
.install-sheet__sub { font-size: .88rem; color: var(--muted); }
.install-sheet__actions { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }

/* Standalone-only affordances: shown when launched from the home screen. */
.standalone-only { display: none; }
body.is-standalone .standalone-only { display: block; }
body.is-standalone .browser-only { display: none; }

@media (prefers-reduced-motion: reduce) {
  .install-sheet { animation: none; }
}

/* ==========================================================================
   18. OFFLINE SCREEN
   ========================================================================== */
.offline-screen {
  min-height: 100vh; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: 2rem calc(var(--m-pad) + var(--safe-l)) calc(2rem + var(--safe-b));
  background: var(--surface-muted);
}
.offline-screen__inner { max-width: 26rem; width: 100%; text-align: center; }
.offline-screen__icon { width: 56px; height: 56px; color: #8A5A00; margin: 0 auto 1.25rem; }
.offline-screen h1 { font-size: 1.75rem; margin-bottom: .6rem; }
.offline-screen__lede { color: var(--muted); margin-bottom: 1.25rem; }
.offline-screen__list {
  display: grid; gap: .5rem; text-align: left;
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  padding: 1rem 1.1rem; margin-bottom: 1.25rem;
  font-size: .92rem; color: var(--ink);
}
.offline-screen__list li { position: relative; padding-left: 1.1rem; }
.offline-screen__list li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 6px; height: 6px; border-radius: 50%; background: #8A5A00;
}
.offline-screen__note { font-size: .88rem; color: var(--muted); margin-bottom: 1.5rem; }

/* ==========================================================================
   19. QUOTE SCREEN — one content grid, one set of edges

   Everything in the form — labels, inputs, native date/time pickers, the
   textarea, the upload area, tips, errors and the Continue button — shares
   the same left and right boundary. That is achieved structurally, not by
   nudging individual widths:

     .form-field  >  label  +  .input-shell > control  +  .field-error

   The SHELL owns border, radius, height and horizontal padding. The control
   inside is borderless, padding-free and width:100%. Padding a native
   date/time input directly is what makes it overflow on iOS, so that never
   happens here.

   Every grid child carries `min-width: 0`. Grid items default to
   `min-width: auto`, which lets a long placeholder or a native picker push a
   track wider than its container — that was the cause of the right-edge
   overflow, and the reason nothing lined up.
   ========================================================================== */

.quote-page {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 20px 16px calc(110px + env(safe-area-inset-bottom, 0px));
}

/* The page owns its bottom clearance, so the global mobile body padding
   would otherwise double up. */
@media (max-width: 899.98px) {
  body[data-route="/quote"] { padding-bottom: 0; }
}

/* ---- Hero ---- */
.q-hero {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  background: var(--navy-900);
  margin-bottom: 16px;
}
.q-hero img { width: 100%; height: 260px; object-fit: cover; object-position: 62% 50%; display: block; }
.q-hero__overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 18px;
  background: linear-gradient(100deg,
    rgba(9, 24, 40, .93) 0%, rgba(9, 24, 40, .82) 34%,
    rgba(9, 24, 40, .30) 64%, rgba(9, 24, 40, .06) 100%);
  color: #fff;
}
.q-hero__title { color: #fff; font-size: clamp(1.6rem, 7.6vw, 2rem); line-height: 1.06; letter-spacing: -.03em; margin-bottom: 6px; }
.q-hero__sub { font-size: .86rem; color: #D6E2EC; margin-bottom: 12px; max-width: 15rem; }
.q-hero__points { display: grid; gap: 6px; }
.q-hero__points li { display: flex; align-items: center; gap: 8px; font-size: .8rem; font-weight: 600; min-width: 0; }
.q-hero__points .icon { width: 17px; height: 17px; color: var(--orange-500); flex: none; }

/* ---- Card ---- */
.q-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 20px 16px;
  box-shadow: 0 1px 3px rgba(14, 39, 64, .05);
}
.q-card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.q-card__heading { min-width: 0; }
.q-card__title { font-size: 1.3rem; }
.q-card__sub { color: var(--muted); font-size: .9rem; }
.q-secure { display: inline-flex; align-items: center; gap: 5px; flex: none; font-size: .72rem; font-weight: 700; color: var(--navy-800); }
.q-secure .icon { width: 16px; height: 16px; color: var(--green-600); }

/* ---- Progress: sticks beneath the app bar once scrolled past --------------
   `top` is derived from --app-bar-h, the same token the app bar itself uses,
   so the two can never drift apart. Guessing a header height is how sticky
   bars end up tucked under the status bar or floating below it. */
:root {
  --progress-bg: rgba(255, 255, 255, .97);
  --progress-border: #DBE4EC;
  --progress-active: var(--orange-cta);
  --progress-complete: #0A2540;
  --progress-upcoming: #93A1AF;
}


/* An invalid or focused field must clear both the app bar and the sticky
   progress bar when scrolled to. */
.form-field {
  scroll-margin-top: calc(var(--app-bar-h) + var(--safe-t) + 90px);
}

/* ---- Step headers ------------------------------------------------------
   Every step of the quote says which step it is, and on a phone each one
   heads its own wizard screen: icon in a soft circle, the question large.
   One typography in every state, by the owner's rule.

   text-transform is pinned because this is a <button>, and buttons are
   capitalized site-wide further down this file. Without it "Pick a day"
   renders as "Pick A Day". */
.q-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  width: 100%; margin: 0; padding: 0;
  background: none; border: 0;
  font: inherit; color: inherit;
  text-align: left; text-transform: none;
  cursor: default;
}
.q-step__icon {
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border-radius: 999px;
  background: #FDEEDF;
  color: var(--orange-cta);
}
.q-step__icon .icon { width: 22px; height: 22px; }
.q-step__text { display: grid; gap: 3px; min-width: 0; }
.q-step__n {
  font-size: .7rem; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.q-step__t {
  font-size: 1.22rem; font-weight: 800; line-height: 1.22;
  color: var(--navy-900);
}
.q-step__h { font-size: .9rem; line-height: 1.35; color: var(--muted); }
.q-step__sum { display: none; }

/* Review rows carry an Edit at every width — buildReview is one function —
   so this lives outside the phone block. */
.q-review__edit {
  border: 0; background: none; padding: 2px 4px;
  font: inherit; font-size: .85rem; font-weight: 700;
  color: var(--orange-cta); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* ---- Form grid ---- */
.quote-form { display: grid; gap: 20px; min-width: 0; }
.form-section { display: grid; gap: 18px; min-width: 0; }
/* Steps receive programmatic focus so a screen reader announces the new
   context. That must not paint a ring around the entire panel — a big orange
   halo around every field reads as an error state. Both the outline AND the
   focus halo have to go; clearing only the outline leaves the box-shadow. */
.form-section:focus,
.form-section:focus-visible { outline: none; box-shadow: none; }

.form-field { width: 100%; min-width: 0; display: grid; gap: 8px; }
.form-field > label,
.form-field .field-label {
  display: block; margin: 0;
  font-size: .95rem; font-weight: 700; color: var(--navy-900);
}
.label-opt { font-weight: 600; color: var(--muted); font-size: .85em; }
.label-req { font-weight: 700; color: var(--orange-cta); font-size: .85em; }
.field-hint { margin: 0; font-size: .84rem; color: var(--muted); }

/* ---- The shell: single source of control geometry ---- */
.input-shell {
  width: 100%; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  min-height: 56px;
  padding: 0 16px;
  border: 1px solid #CBD5E1;
  border-radius: 14px;
  background: #fff;
}
.input-shell > input,
.input-shell > textarea,
.input-shell > select {
  display: block;
  width: 100%; min-width: 0;
  border: 0; padding: 14px 0; margin: 0;
  background: transparent;
  font: inherit;
  /* 16px stops iOS zooming the viewport on focus. */
  font-size: 16px;
  color: var(--ink);
}
.input-shell > input:focus,
.input-shell > textarea:focus { outline: none; }
.input-shell:focus-within {
  border-color: var(--navy-800);
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.input-shell--area { align-items: stretch; }
.input-shell--area > textarea { min-height: 112px; resize: vertical; }

.input-icon { display: flex; flex: none; color: var(--navy-800); }
.input-icon .icon { width: 21px; height: 21px; }

/* ---- "Use my current location": a real control, not a decorative icon ----
   It sits inside the shell as a flex sibling rather than absolutely
   positioned, so it can never overlap the text the customer is typing. */
.input-shell--action { padding-right: 6px; }
.location-button {
  flex: none;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border: 0; border-radius: 12px;
  color: #0A2540; background: transparent;
  cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.location-button:active { background: #EEF4F8; }
.location-button:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.location-button:disabled { cursor: wait; opacity: .55; }
.location-button svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; }

/* Busy state: the pin pulses while the fix is being acquired, so a slow
   lookup does not look like a dead tap. */
.location-button.is-busy svg { animation: pin-pulse 1s ease-in-out infinite; }
@keyframes pin-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .location-button.is-busy svg { animation: none; }
}

.field-status {
  margin: 0; min-height: 20px; line-height: 20px;
  font-size: .82rem; color: var(--muted);
}
/* A located address is long and must be readable before it is accepted, so
   the status line wraps instead of running off the edge. */
.field-status { overflow-wrap: anywhere; }

/* ---- Located-address confirmation ---------------------------------------
   The address is proposed here and only committed when the customer taps
   "Use this address". Full width and wrapping, because the whole point is
   that it can be read before it is trusted. */
.geo-cancel {
  align-self: flex-start;
  border: 1px solid #C8D3DC; border-radius: 999px; background: #fff;
  min-height: 36px; padding: 0 .9rem; margin-top: .1rem;
  font: inherit; font-size: .82rem; font-weight: 700; color: var(--navy-800);
  cursor: pointer;
}
.geo-cancel[hidden] { display: none; }

.geo-proposal {
  display: grid; gap: .45rem;
  margin-top: .5rem; padding: .85rem .9rem;
  border: 1px solid #CBD5E1; border-radius: 12px; background: var(--surface-muted);
  min-width: 0;
}
.geo-proposal[hidden] { display: none; }
.geo-proposal__address {
  margin: 0; font-weight: 700; color: var(--navy-900); font-size: .92rem;
  line-height: 1.35; overflow-wrap: anywhere;   /* never clipped */
}
.geo-proposal__accuracy { margin: 0; font-size: .8rem; color: var(--muted); }
.geo-proposal__warn { margin: 0; font-size: .82rem; font-weight: 600; color: #8E4B00; line-height: 1.4; }
.geo-proposal__actions { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: .15rem; }
.geo-proposal__btn {
  flex: 1 1 auto; min-height: 44px; min-width: 0;
  padding: 0 .8rem;
  border: 1px solid #C8D3DC; border-radius: 10px; background: #fff;
  font: inherit; font-size: .85rem; font-weight: 700; color: var(--navy-800);
  cursor: pointer;
}
.geo-proposal__btn--use { background: var(--orange-cta); border-color: var(--orange-cta); color: #fff; }
.geo-proposal__btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Native pickers: no padding of their own, and allowed to shrink. */
.native-date, .native-time { min-width: 0; }

/* The preferred time is a <select> now. It inherits the shell's geometry like
   every other control, and keeps the native menu affordance rather than a
   custom chevron that would have to be positioned against a control whose
   width the browser decides. */
select.native-time {
  appearance: auto;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
select.native-time:disabled { color: var(--muted); cursor: not-allowed; }

/* ---- Date + time: stacked on phones ---- */
.date-time-grid { display: grid; grid-template-columns: 1fr; gap: 18px; min-width: 0; }
@media (min-width: 640px) {
  .date-time-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- Errors: space reserved, nothing shown until there is something to say ---- */
.field-error {
  margin: 0;
  min-height: 20px;
  font-size: .875rem;
  font-weight: 600;
  color: #B3261E;
  line-height: 20px;
}
/* The warning glyph exists only when there is a message — never an empty red
   icon beside an untouched field. */
.field-error:not(:empty)::before { content: "\26A0  "; }
.form-field.is-invalid .input-shell { border-color: #B3261E; background: #FFF8F7; }

/* ---- Photos ---- */
.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;
}
.photo-upload { width: 100%; min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 359.98px) { .photo-upload { grid-template-columns: 1fr; } }
.photo-btn {
  min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; min-height: 76px; padding: 12px;
  background: var(--surface-muted);
  border: 1.5px dashed #C3CFDA; border-radius: 14px;
  font-size: .82rem; font-weight: 700; color: var(--navy-800);
  text-align: center; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.photo-btn:active { background: #E9EFF4; }
.photo-btn .icon { width: 22px; height: 22px; color: var(--orange-cta); }
.photo-btn:focus-within { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Previews use blob: URLs from URL.createObjectURL(); the CSP allows blob:
   on img-src only for exactly this. */
.photo-list {
  width: 100%; min-width: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
}
.photo-list:empty { display: none; }

.photo-preview {
  position: relative; width: 100%; aspect-ratio: 1; margin: 0;
  overflow: hidden; min-width: 0;
  border: 1px solid #CBD5E1; border-radius: 14px;
  background: #EEF4F8;
}
.photo-preview img { display: block; width: 100%; height: 100%; object-fit: cover; }

.photo-remove {
  position: absolute; top: 6px; right: 6px;
  display: grid; place-items: center;
  width: 36px; height: 36px;
  border: 0; border-radius: 999px;
  color: #fff; background: rgba(10, 37, 64, .88);
  font-size: 22px; line-height: 1; cursor: pointer;
  touch-action: manipulation;
}
.photo-remove:active { background: rgba(10, 37, 64, .96); }

.photo-preview-error {
  display: grid; place-items: center;
  width: 100%; height: 100%; padding: 10px;
  text-align: center; color: #9F1C14; font-size: 13px;
}

/* ---- Tip ---- */
.form-tip {
  width: 100%; min-width: 0;
  display: flex; gap: 12px; align-items: flex-start;
  background: #EEF4FA; border-radius: 14px; padding: 14px 16px;
}
.form-tip__icon { color: var(--orange-cta); flex: none; display: flex; }
.form-tip__text { min-width: 0; font-size: .85rem; color: var(--muted); display: flex; flex-direction: column; gap: 2px; }
.form-tip__text strong { color: var(--navy-900); }

/* ---- Review ---- */
/* THE REVIEW READS AT ARM'S LENGTH.
   .88rem is about 14px — fine on a desktop, small on a phone held at
   reading distance while somebody checks an address they are about to
   commit money to. Everything here steps up, and the rows get room rather
   than being packed to fit. */
.q-review__title { font-size: 1.2rem; margin: 22px 0 12px; font-weight: 800; }
.q-review { display: grid; gap: 0; min-width: 0; }
.q-review__row {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 14px 0; border-bottom: 1px solid var(--line); font-size: 1rem; min-width: 0;
}
.q-review dt { color: var(--muted); flex: none; }
.q-review dd { margin: 0; color: var(--navy-900); font-weight: 600; text-align: right; min-width: 0; word-break: break-word; }

.q-price { background: var(--surface-muted); border-radius: 14px; padding: 18px; text-align: center; }
.q-price__label { font-size: .76rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
.q-price__figure { font-size: 3rem; font-weight: 800; color: var(--orange-500); line-height: 1.05; letter-spacing: -.04em; }
.q-price__figure span { font-size: .48em; vertical-align: .42em; }
.q-price__unit { font-weight: 800; color: var(--navy-900); margin-top: -4px; }
.q-price__def { font-size: .82rem; color: var(--muted); margin-top: 6px; }

/* ---- Actions: identical width and edges to every control above ---- */
.form-actions { display: grid; gap: 12px; width: 100%; min-width: 0; }
.primary-button, .secondary-button {
  width: 100%; min-width: 0;
  min-height: 56px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; border-radius: 999px; margin: 0; padding: 0 20px;
  font: inherit; font-weight: 700; font-size: 1rem; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: background-color .15s ease, transform .1s ease;
}
.primary-button { background: var(--orange-cta); color: #fff; }
.primary-button:active { background: var(--orange-cta-hover); transform: scale(.985); }
.primary-button[disabled] { background: #B9C4CE; cursor: default; transform: none; }
.secondary-button { background: transparent; color: var(--navy-800); border: 2px solid #C8D3DC; }
.secondary-button:active { background: var(--surface-muted); transform: scale(.985); }
.primary-button .icon, .secondary-button .icon { width: 18px; height: 18px; }

.q-legal {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: .8rem; color: var(--muted); text-align: center; min-width: 0;
}
.q-legal .icon { width: 16px; height: 16px; flex: none; }

/* ---- Address suggestions (combobox listbox) ---------------------------- */
.address-field { position: relative; }
.address-suggestions {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  z-index: 45;                     /* above neighbouring fields */
  margin: 0; padding: 6px;
  list-style: none;
  background: #fff;
  border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 12px 28px rgba(10, 37, 64, .16);
  max-height: 280px; overflow-y: auto; overscroll-behavior: contain;
}
.address-suggestion {
  display: flex; align-items: center; gap: 10px;
  min-height: 56px; padding: 8px 10px;
  border-radius: 10px; cursor: pointer; min-width: 0;
}
.address-suggestion.is-active,
.address-suggestion:active { background: var(--surface-muted); }
.address-suggestion__pin { flex: none; display: flex; color: var(--orange-cta); }
.address-suggestion__pin svg { width: 20px; height: 20px; }
.address-suggestion__text { display: flex; flex-direction: column; min-width: 0; }
.address-suggestion__primary {
  font-weight: 700; color: var(--navy-900); font-size: .95rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.address-suggestion__secondary {
  font-size: .82rem; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A confirmed address is marked by more than colour. */
.input-shell.is-verified { border-color: var(--green-600); }

/* ---- Load count choices ------------------------------------------------ */
.choice-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.choice { min-width: 0; }
.choice__box {
  display: grid; place-items: center;
  min-height: 52px; padding: 10px;
  border: 1.5px solid #CBD5E1; border-radius: 14px;
  background: #fff; font-weight: 700; font-size: .92rem; color: var(--navy-900);
  text-align: center; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.choice input:checked + .choice__box {
  border-color: var(--orange-cta); background: rgba(200, 74, 13, .08); color: var(--orange-cta);
}
.choice input:focus-visible + .choice__box { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The one choice that changes the price silently, so it says what it does.
   Small and muted: it explains the option, it does not compete with it. */
.choice__hint {
  display: block;
  margin-top: 3px;
  font-size: .72rem; font-weight: 600; line-height: 1.25;
  color: var(--muted, #5A6B7B);
}

/* ---- The account step ---------------------------------------------------
   Set apart from the fields above it, because it is a different KIND of thing:
   the trip details describe a job, this creates a relationship. A plain
   continuation of the form would read as one more box to fill. */
.acct {
  margin-top: 18px; padding: 16px;
  background: var(--wash, #F6F8FA);
  border: 1px solid var(--line, #DCE4EC); border-radius: 14px;
}
.acct__head { margin-bottom: 12px; }
.acct__title { margin: 0 0 3px; font-size: 1.02rem; font-weight: 800; color: var(--navy-900, #12304E); }
.acct__sub { margin: 0; font-size: .86rem; line-height: 1.4; color: var(--muted, #5A6B7B); }

/* Said where people brace for it. "Create an account" is the moment somebody
   expects to be asked for a card, so the answer sits next to the password
   field rather than in small print further down. */
.acct__free {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 12px 0 0; font-size: .84rem; font-weight: 600; line-height: 1.4;
  color: var(--navy-900, #12304E);
}
.acct__free-icon { width: 17px; height: 17px; flex: none; color: var(--green-ok, #1F8A54); margin-top: 1px; }

.acct__switch {
  margin: 14px 0 0; font-size: .86rem; color: var(--muted, #5A6B7B);
}
.link-button {
  border: 0; background: none; padding: 0;
  font: inherit; font-weight: 700; color: var(--orange-cta, #F26B21);
  text-decoration: underline; cursor: pointer;
}
.link-button:focus-visible { outline: 3px solid var(--navy-900, #12304E); outline-offset: 2px; }

/* A returning customer sees this instead: no second account to make. */
.acct-known {
  display: flex; align-items: flex-start; gap: 10px;
  margin-top: 18px; padding: 14px 16px;
  background: #F1F8F4; border: 1px solid #CDE7D8; border-radius: 14px;
}
.acct-known__icon { width: 19px; height: 19px; flex: none; color: var(--green-ok, #1F8A54); margin-top: 1px; }
.acct-known__t { margin: 0 0 2px; font-size: .92rem; font-weight: 700; color: var(--navy-900, #12304E); }
.acct-known__s { margin: 0; font-size: .84rem; color: var(--muted, #5A6B7B); }

.load-help { margin-top: 2px; }
.load-help summary {
  cursor: pointer; font-size: .85rem; font-weight: 700; color: var(--navy-700);
  min-height: 44px; display: flex; align-items: center;
}
.load-help p { font-size: .86rem; color: var(--muted); padding-bottom: 4px; }

/* ---- Submission states ------------------------------------------------- */
.primary-button__spinner { display: none; width: 18px; height: 18px; }
.primary-button.is-busy .primary-button__arrow { display: none; }
.primary-button.is-busy .primary-button__spinner {
  display: block;
  border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: #fff; border-radius: 50%;
  animation: btn-spin .7s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .primary-button.is-busy .primary-button__spinner { animation: none; }
}

.form-error-summary {
  padding: 14px 16px; border-radius: 14px;
  background: #FFF3F2; border: 1px solid #F0C4C0;
  color: #8E1D16; font-size: .9rem; font-weight: 600;
}

/* ---- Quote result ------------------------------------------------------ */
.quote-result { display: grid; gap: 14px; }
.quote-result:focus { outline: none; }
.quote-result__eyebrow {
  font-size: .76rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted); text-align: center;
}
.quote-result__total {
  font-size: 3.2rem; font-weight: 800; color: var(--navy-900);
  text-align: center; line-height: 1.02; letter-spacing: -.04em;
}
.quote-result__ref {
  text-align: center; font-size: .82rem; color: var(--muted);
  font-variant-numeric: tabular-nums; word-break: break-all;
}
.quote-result__ref--large { font-size: 1.1rem; font-weight: 800; color: var(--navy-900); }
.quote-result__lines { display: grid; gap: 0; }
.quote-result__line {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 10px 0; border-bottom: 1px solid var(--line); font-size: .9rem;
}
.quote-result__line dt { color: var(--muted); }
.quote-result__line dd { margin: 0; font-weight: 700; color: var(--navy-900); }
.quote-result__message { font-size: .95rem; color: var(--ink); }
.quote-result__note { font-size: .84rem; color: var(--muted); }
/* Shown when the confirmation email has not gone out yet. Darker than the
   muted default so it reads as information the customer should notice, but
   deliberately not an error colour — nothing has gone wrong with their quote. */
.quote-result__note--warn { color: var(--navy-900); font-weight: 600; }
.quote-result__actions { display: grid; gap: 10px; }

.attribution {
  font-size: .74rem; color: var(--muted); text-align: center;
  padding-top: 4px;
}

/* Desktop: same app-shaped flow, centred with breathing room. */
@media (min-width: 900px) {
  .quote-page { padding: 40px 16px 60px; }
  .q-hero img { height: 240px; }
}

/* ==========================================================================
   18. DESKTOP CONTENT PAGES  (How It Works, Pricing — approved mockups)
   Everything here renders inside .d-only, so the mobile shell is untouched.
   Class names are page-prefixed (hiw-, pp-) because the legacy homepage
   sections already own .pricing__* — reusing those names is how this section
   broke the /customer pricing block on its first attempt.
   ========================================================================== */

/* Inverse of .d-only: keeps a page original mobile block out of desktop. */
@media (min-width: 900px) {
  .m-only { display: none !important; }
}

/* ---- Breadcrumbs ---------------------------------------------------------
   On every content page, both shells. The separator is CSS so assistive tech
   hears the labels, not the punctuation. */
.crumbs { padding-top: 1.1rem; font-size: .86rem; }
.crumbs__list { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; }
.crumbs__item { display: flex; align-items: center; gap: .4rem; color: #5A6B7B; }
.crumbs__item + .crumbs__item::before { content: "\203A"; color: #9AA9B7; }
.crumbs a { color: #5A6B7B; text-decoration: none; }
.crumbs a:hover { color: var(--navy-800); text-decoration: underline; }
.crumbs [aria-current] { color: var(--navy-800); font-weight: 600; }
@media (max-width: 899.98px) {
  .crumbs { padding-top: .85rem; }
}

/* Current page in the desktop nav: orange with an underline. Normal-size text
   on white, so it uses the CTA orange (4.73:1), same contrast rule as buttons. */
.nav-link[aria-current="page"] {
  color: var(--orange-cta);
  font-weight: 700;
  border-bottom: 3px solid var(--orange-500);
  padding-bottom: .35rem;
}

/* Footer: social marks and the Quick Links heading. */
.site-footer__social { display: flex; gap: .9rem; margin-top: .9rem; }
.site-footer__social-item { color: #fff; }
.site-footer__social-item .icon { width: 21px; height: 21px; }

/* These two pages were approved at a wider measure than the default 1200. */
.hiw .container, .price-page .container { max-width: 1340px; }

/* ---- Shared orange CTA band ---------------------------------------------- */
.cta-band {
  display: flex; align-items: center; gap: 1.1rem;
  background: var(--orange-cta);
  border-radius: 12px;
  padding: 1.15rem 1.6rem;
  color: #fff;
  margin-top: 1.4rem;
}
.cta-band__bubble { flex: none; width: 44px; height: 44px; border: 2px solid rgba(255,255,255,.85); border-radius: 10px; display: grid; place-items: center; }
.cta-band__bubble svg { width: 26px; height: 26px; }
.cta-band__text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.cta-band__text strong { font-size: 1.22rem; letter-spacing: -.01em; }
.cta-band__text span { font-size: .95rem; opacity: .95; }
.cta-band__btn {
  margin-left: auto; flex: none;
  background: #fff; color: var(--orange-cta);
  font-weight: 800; font-size: 1.02rem;
  padding: .85rem 1.9rem; border-radius: 10px;
  text-decoration: none;
  transition: transform .12s ease, box-shadow .12s ease;
}
.cta-band__btn:hover { box-shadow: 0 6px 18px rgba(0,0,0,.18); transform: translateY(-1px); }

/* Orange accent strokes inside page artwork. */
.hiw-a { stroke: var(--orange-500); }
.hiw-a--fill { fill: #fff; }

/* ---- How It Works --------------------------------------------------------- */
.hiw { padding-block: 3.2rem 4.5rem; }
.hiw__title {
  text-align: center;
  font-size: clamp(2.6rem, 4.2vw, 3.4rem);
  letter-spacing: -.03em;
  margin-bottom: .6rem;
}
.hiw__lede { text-align: center; color: #5A6B7B; font-size: 1.12rem; margin-bottom: 3rem; }

.hiw__steps {
  list-style: none; margin: 0 0 2.6rem; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2.6rem;
}
.hiw__step { text-align: center; }
.hiw__art { position: relative; display: inline-block; margin-bottom: 1.2rem; }
.hiw__circle {
  width: 104px; height: 104px; border-radius: 50%;
  background: #EDF2F8; color: var(--navy-800);
  display: grid; place-items: center;
}
.hiw__circle svg { width: 52px; height: 52px; }
.hiw__num {
  position: absolute; top: -4px; left: -14px; z-index: 1;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--navy-800); color: #fff;
  font-weight: 800; font-size: 1.05rem;
  display: grid; place-items: center;
}
.hiw__next {
  position: absolute; right: -2.05rem; top: 38px;
  color: var(--orange-500); width: 28px; height: 28px;
}
.hiw__next svg { width: 28px; height: 28px; }
.hiw__step-title { font-size: 1.16rem; letter-spacing: -.015em; margin-bottom: .5rem; }
.hiw__step-body { color: #5A6B7B; font-size: .95rem; line-height: 1.55; max-width: 30ch; margin-inline: auto; }

.hiw__included {
  display: flex; align-items: center; gap: 1.4rem;
  background: #EAF1F8; border-radius: 14px;
  padding: 1.5rem 2rem;
}
.hiw__shield {
  flex: none; width: 58px; height: 58px; border-radius: 50%;
  background: #fff; color: var(--navy-800);
  display: grid; place-items: center;
}
.hiw__shield svg { width: 30px; height: 30px; }
.hiw__included-text p { margin: 0; line-height: 1.6; }
.hiw__included-text p + p { margin-top: .1rem; }
.hiw__couch { flex: none; margin-left: auto; width: 150px; height: 84px; color: var(--navy-800); }

/* ---- Pricing --------------------------------------------------------------- */
.price-page { padding-block: 3rem 4.5rem; }
.pp-title { font-size: clamp(2.6rem, 4.2vw, 3.2rem); letter-spacing: -.03em; margin-bottom: 1.8rem; }

.pp-top {
  display: grid; grid-template-columns: 270px 1fr; gap: 3rem;
  align-items: start; margin-bottom: 2rem;
}
.pp-card {
  position: relative; text-align: center;
  border: 1px solid #E2E9F1; border-radius: 14px;
  background: #fff; box-shadow: 0 6px 22px rgba(14, 39, 64, .06);
  padding: 1.6rem 1.4rem 4.8rem;
}
.pp-card-label { font-weight: 700; color: var(--navy-800); margin-bottom: .4rem; }
/* $85 is display-size text, so the brand orange passes the large-text bar. */
.pp-card-figure { font-size: 3.2rem; font-weight: 800; color: var(--orange-500); letter-spacing: -.03em; line-height: 1.05; }
.pp-card-unit { font-weight: 700; color: var(--navy-800); margin-top: .25rem; }
.pp-card-art {
  position: absolute; right: 1.2rem; bottom: 1rem;
  width: 84px; height: 48px; color: var(--navy-800); z-index: 1;
}
.pp-card-art::before {
  content: ""; position: absolute; right: -4px; bottom: -2px;
  width: 46px; height: 46px; border-radius: 50%;
  background: #FBE8DC; z-index: -1;
}
.pp-explain p { margin: 0 0 .9rem; line-height: 1.65; max-width: 76ch; }

.pp-factors {
  border: 1px solid #E2E9F1; border-radius: 14px;
  background: #fff; box-shadow: 0 6px 22px rgba(14, 39, 64, .05);
  padding: 1.4rem 1rem 1rem; margin-bottom: 1.6rem;
}
.pp-factors-title { text-align: center; font-size: 1.35rem; letter-spacing: -.02em; margin-bottom: 1rem; }
.pp-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
}
.pp-factor {
  display: flex; align-items: center; gap: .8rem;
  padding: .95rem 1.1rem; position: relative; min-height: 64px;
}
/* Hairlines: verticals between columns, one horizontal between the rows. */
.pp-factor:not(:nth-child(4n + 1))::before {
  content: ""; position: absolute; left: 0; top: 18%; bottom: 18%;
  width: 1px; background: #E7EDF3;
}
.pp-factor:nth-child(n + 5) { border-top: 1px solid #E7EDF3; }
.pp-tick {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: #E5F5EC; color: var(--green-700);
  display: grid; place-items: center;
}
.pp-tick svg { width: 19px; height: 19px; }
.pp-factor-label span { display: block; line-height: 1.35; font-size: .98rem; }

.pp-assure {
  display: flex; align-items: center; gap: 1.3rem;
  background: #E8F0FA; border-radius: 12px;
  padding: 1.3rem 1.8rem;
}
/* The blue is deliberate and local: it reads as "information", and nothing
   brand-orange or navy says that. */
.pp-info {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  background: #1D4ED8; color: #fff;
  font-weight: 800; font-size: 1.2rem; font-style: italic;
  display: grid; place-items: center;
}
.pp-assure-text p { margin: 0; line-height: 1.6; }
.pp-shield { flex: none; margin-left: auto; width: 56px; height: 56px; color: #1D4ED8; }
.pp-shield svg { width: 56px; height: 56px; }

/* ---- Services --------------------------------------------------------------
   One responsive layout: there was no earlier mobile page to preserve, so the
   mockup's desktop composition simply stacks below 900px. */
.svc { padding-block: 1.6rem 4.5rem; }
.svc .container { max-width: 1340px; }
.svc-title { font-size: clamp(2.2rem, 4.2vw, 3.2rem); letter-spacing: -.03em; margin-bottom: .7rem; }
.svc-lede { color: #4A5B6C; line-height: 1.6; max-width: 46ch; margin-bottom: 1.6rem; }

.svc-mini { list-style: none; margin: 0 0 1.7rem; padding: 0; display: flex; flex-wrap: wrap; gap: 1.2rem 2.6rem; }
.svc-mini__item { display: flex; align-items: center; gap: .7rem; }
.svc-mini__icon { color: var(--orange-500); width: 26px; height: 26px; flex: none; }
.svc-mini__icon svg { width: 26px; height: 26px; }
.svc-mini__text { display: flex; flex-direction: column; font-weight: 700; font-size: .92rem; line-height: 1.3; color: var(--navy-800); }

.svc-actions { display: flex; flex-wrap: wrap; gap: .9rem; margin-bottom: 3rem; }

.svc-h2 { text-align: center; font-size: clamp(1.5rem, 2.6vw, 1.9rem); letter-spacing: -.02em; margin-bottom: .4rem; }
.svc-h2--left { text-align: left; }
.svc-sub { text-align: center; color: #5A6B7B; font-size: .98rem; margin-bottom: 1.8rem; }

.svc-grid { list-style: none; margin: 0 0 2.6rem; padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.4rem; }
.svc-card {
  display: flex; flex-direction: column;
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.4rem 1.3rem 1.2rem;
}
.svc-card__icon {
  width: 66px; height: 66px; border-radius: 50%;
  background: #EDF2F8; color: var(--navy-800);
  display: grid; place-items: center; margin-bottom: 1rem;
}
.svc-card__icon svg { width: 34px; height: 34px; }
.svc-card__title { font-size: 1.12rem; letter-spacing: -.015em; margin-bottom: .5rem; }
.svc-card__body { color: #5A6B7B; font-size: .93rem; line-height: 1.55; margin-bottom: 1rem; }
.svc-photo { width: 100%; height: 128px; object-fit: cover; border-radius: 8px; margin-bottom: 1rem; }
.svc-card__cta {
  margin-top: auto; display: inline-flex; align-items: center; gap: .45rem;
  color: var(--orange-cta); font-weight: 700; font-size: .95rem; text-decoration: none;
}
.svc-card__cta svg { width: 18px; height: 18px; transition: transform .15s ease; }
.svc-card__cta:hover svg { transform: translateX(3px); }

.svc-haul {
  display: grid; grid-template-columns: 1fr 1.15fr .9fr; gap: 2rem; align-items: center;
  background: #EDF3F9; border-radius: 16px;
  padding: 2rem 2.2rem; margin-bottom: 2.8rem;
}
.svc-haul__lede { color: #4A5B6C; margin-bottom: 1rem; }
.svc-haul__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.svc-haul__list li { display: flex; align-items: center; gap: .6rem; font-size: .95rem; color: var(--navy-800); }
.svc-haul__list svg { width: 18px; height: 18px; color: var(--green-600); flex: none; }
.svc-haul > .svc-photo { height: 200px; margin: 0; }
.svc-haul__card {
  background: #fff; border-radius: 14px; text-align: center;
  box-shadow: 0 8px 24px rgba(14, 39, 64, .08);
  padding: 1.6rem 1.4rem;
}
.svc-haul__bag { display: inline-grid; place-items: center; width: 52px; height: 52px; color: var(--navy-800); margin-bottom: .5rem; }
.svc-haul__bag svg { width: 40px; height: 40px; }
.svc-haul__card h3 { font-size: 1.08rem; margin-bottom: .45rem; }
.svc-haul__card p { color: #5A6B7B; font-size: .9rem; line-height: 1.5; margin-bottom: 1rem; }

.svc-inc { list-style: none; margin: 0 0 1rem; padding: 1rem 0 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.4rem; }
.svc-inc__item { display: flex; gap: .85rem; align-items: flex-start; }
.svc-inc__icon {
  flex: none; width: 52px; height: 52px; border-radius: 50%;
  background: var(--navy-800); color: #fff;
  display: grid; place-items: center;
}
.svc-inc__icon svg { width: 26px; height: 26px; }
.svc-inc__text { display: flex; flex-direction: column; gap: .2rem; }
.svc-inc__text strong { font-size: .98rem; color: var(--navy-800); }
.svc-inc__text span { font-size: .88rem; color: #5A6B7B; line-height: 1.5; }

/* Navy variant of the shared band, with the orange button from the mockup. */
.cta-band--navy { background: var(--navy-800); }
.cta-band__btn--orange { background: var(--orange-cta); color: #fff; }
.cta-band__btn--orange:hover { background: var(--orange-cta-hover); }
.cta-band__badge {
  flex: none; width: 52px; height: 52px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.85); color: #fff;
  display: grid; place-items: center;
}
.cta-band__badge svg { width: 26px; height: 26px; }

/* ---- Safety (also lends its hero/banner/know styles to Help) -------------- */
.sft, .hlp { padding-block: 1.4rem 4.5rem; }
.sft .container, .hlp .container { max-width: 1340px; }
.sft-hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 2rem; margin-bottom: 2rem; }
.sft-title { font-size: clamp(2.2rem, 4.2vw, 3.2rem); letter-spacing: -.03em; margin-bottom: .7rem; }
.sft-lede { color: #4A5B6C; line-height: 1.65; max-width: 52ch; }
.sft-hero__art { flex: none; width: 150px; height: 150px; }
.sft-hero__art svg { width: 100%; height: 100%; }

.sft-grid { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.sft-card {
  text-align: center;
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.5rem 1.2rem;
}
.sft-card__icon { display: inline-grid; place-items: center; width: 56px; height: 56px; color: var(--navy-800); margin-bottom: .8rem; }
.sft-card__icon svg { width: 46px; height: 46px; }
.sft-card__title { font-size: 1.04rem; letter-spacing: -.015em; margin-bottom: .5rem; }
.sft-card__body { color: #5A6B7B; font-size: .89rem; line-height: 1.55; }

.sft-h2 { text-align: center; font-size: clamp(1.4rem, 2.4vw, 1.7rem); letter-spacing: -.02em; margin-bottom: 1.2rem; }

.sft-protect {
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.6rem 2rem; margin-bottom: 2.2rem;
}
.sft-protect__list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: .7rem 2.4rem; }
.sft-protect__list li { display: flex; align-items: flex-start; gap: .6rem; font-size: .95rem; color: var(--navy-800); line-height: 1.45; }
.sft-protect__list svg { width: 19px; height: 19px; color: var(--green-600); flex: none; margin-top: .1rem; }

.sft-know { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.3rem; }
.sft-know__card {
  display: flex; gap: .9rem; align-items: flex-start;
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.2rem 1.2rem;
}
.sft-know__icon {
  flex: none; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  font-weight: 800; font-style: italic; font-size: 1.05rem;
}
.sft-know__icon--orange { background: var(--orange-cta); }
.sft-know__icon--navy { background: var(--navy-800); }
.sft-know__icon svg { width: 22px; height: 22px; }
.sft-know__text { display: flex; flex-direction: column; gap: .25rem; }
.sft-know__text strong { font-size: .96rem; color: var(--navy-800); }
.sft-know__text span { font-size: .87rem; color: #5A6B7B; line-height: 1.5; }

.sft-banner {
  display: flex; align-items: center; gap: 1.1rem;
  background: #EDF3F9; border-radius: 14px;
  padding: 1.2rem 1.6rem; margin-bottom: 1.6rem;
}
.sft-banner__icon {
  flex: none; width: 52px; height: 52px; border-radius: 50%;
  background: #fff; display: grid; place-items: center;
  box-shadow: 0 3px 10px rgba(14, 39, 64, .08);
}
.sft-banner__icon svg { width: 28px; height: 28px; }
.sft-banner strong { display: block; font-size: 1.05rem; color: var(--navy-800); margin-bottom: .15rem; }
.sft-banner p { margin: 0; color: #4A5B6C; font-size: .93rem; line-height: 1.55; }

/* ---- Help ------------------------------------------------------------------ */
.hlp-grid { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.hlp-card {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.5rem 1.2rem 1.2rem;
}
.hlp-card__icon { display: inline-grid; place-items: center; width: 56px; height: 56px; color: var(--navy-800); margin-bottom: .8rem; }
.hlp-card__icon svg { width: 46px; height: 46px; }
.hlp-card__title { font-size: 1.04rem; letter-spacing: -.015em; margin-bottom: .5rem; }
.hlp-card__body { color: #5A6B7B; font-size: .89rem; line-height: 1.55; margin-bottom: .9rem; }
.hlp-card .svc-card__cta { margin-top: auto; }

.hlp-faq {
  border: 1px solid #E2E9F1; border-radius: 14px; background: #FBFCFE;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.6rem 1.6rem 1.2rem; margin-bottom: 2.2rem;
}
.hlp-faq__grid { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; align-items: start; }
.hlp-faq__item {
  border: 1px solid #E4EAF1; border-radius: 10px; background: #fff;
  padding: .85rem 1rem;
}
.hlp-faq__item summary {
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
  cursor: pointer; list-style: none;
  font-weight: 700; font-size: .95rem; color: var(--navy-800);
}
.hlp-faq__item summary::-webkit-details-marker { display: none; }
.hlp-faq__item summary svg { width: 18px; height: 18px; flex: none; color: #7A8CA0; transition: transform .16s ease; }
.hlp-faq__item[open] summary svg { transform: rotate(180deg); }
.hlp-faq__item p { margin: .6rem 0 0; font-size: .88rem; color: #5A6B7B; line-height: 1.55; }

.hlp-how { list-style: none; margin: 0 0 1.8rem; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.3rem; }
.hlp-how__card {
  display: flex; gap: .9rem; align-items: flex-start;
  border: 1px solid #E2E9F1; border-radius: 14px; background: #fff;
  box-shadow: 0 6px 20px rgba(14, 39, 64, .05);
  padding: 1.2rem;
}
.hlp-how__icon {
  flex: none; width: 44px; height: 44px; border-radius: 10px;
  background: #EDF2F8; color: var(--navy-800);
  display: grid; place-items: center;
}
.hlp-how__icon svg { width: 24px; height: 24px; }

@media (max-width: 1099.98px) {
  .sft-grid, .hlp-grid { grid-template-columns: repeat(2, 1fr); }
  .sft-know, .hlp-how { grid-template-columns: 1fr; }
}
@media (max-width: 899.98px) {
  .sft-hero__art { display: none; }
  .sft-grid, .hlp-grid, .sft-protect__list, .hlp-faq__grid { grid-template-columns: 1fr; }
  .sft-protect { padding: 1.2rem 1.2rem; }
}

/* ---- Quote: "What are we moving?" category picker ------------------------- */
/* auto-fit rather than a fixed four columns. `repeat(4, 1fr)` is really
   `repeat(4, minmax(auto, 1fr))`, and that auto floor is the card's own
   intrinsic width — so at 900px, where the support rail has just claimed
   400px and the form card is at its narrowest, four cards could not fit and
   the grid pushed 85px past the viewport instead of shrinking. Letting the
   track count follow the space gives the approved 4x3 at desktop widths and
   3 columns in the narrow band, and it cannot overflow at any width. */
.qcat__grid { list-style: none; margin: 0 0 .6rem; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: .7rem; }
.qcat__card {
  display: flex; flex-direction: column; align-items: center; gap: .35rem;
  border: 1.5px solid #E2E9F1; border-radius: 12px; background: #fff;
  padding: .8rem .5rem .7rem; text-align: center;
}
.qcat__card.is-on { border-color: var(--orange-500); box-shadow: 0 0 0 1px var(--orange-500) inset; background: #FFFBF8; }
.qcat__icon { color: var(--navy-800); width: 34px; height: 34px; }
.qcat__icon svg { width: 34px; height: 34px; }
.qcat__name { font-weight: 700; font-size: .8rem; line-height: 1.25; color: var(--navy-800); }
.qcat__stepper { display: flex; align-items: center; gap: .55rem; }
.qcat__btn {
  width: 30px; height: 30px; border-radius: 50%;
  border: 0; background: #EDF2F8; color: var(--navy-800);
  font-size: 1.15rem; font-weight: 700; line-height: 1; cursor: pointer;
  display: grid; place-items: center; font-family: inherit;
}
.qcat__btn:hover { background: #DFE8F2; }
.qcat__count { min-width: 1.2ch; font-weight: 800; font-size: .95rem; color: var(--navy-800); }
.qcat__card.is-on .qcat__count { color: var(--orange-cta); }
.qcat__subs { display: flex; flex-wrap: wrap; gap: .3rem; justify-content: center; }
.qcat__chip {
  border: 1px solid #D8E0E9; border-radius: 999px; background: #fff;
  padding: .25rem .6rem; font-size: .72rem; font-weight: 600; color: var(--navy-800);
  cursor: pointer; font-family: inherit; min-height: 28px;
}
.qcat__chip[aria-pressed="true"] { background: var(--navy-800); border-color: var(--navy-800); color: #fff; }
.qcat__more { display: none; }
.qcat__note { text-align: center; margin: .4rem 0 .2rem; }
.qcat__note-toggle {
  border: 0; background: none; color: #2C5FA8; font-weight: 600; font-size: .88rem;
  cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; gap: .35rem;
  min-height: 44px;
}
.qcat__note-toggle .icon { width: 16px; height: 16px; }
.qcat__note .input-shell { margin-top: .3rem; }

/* Mobile: compact category rows, first five visible, the rest behind
   "View all categories".

   The categories and the photo card are STACKED, not side by side. Side by
   side was the original arrangement and it does not survive a real phone: at
   360px the two tracks are roughly 200px and 140px, which is not enough for
   a heading, a four-line hint, two upload buttons and a counter. On a Galaxy
   screenshot the photo column's hint sprawled down the page and ran into the
   category cards, and every category name broke across three lines. Full
   width for each, one after the other, is what actually reads. */
@media (max-width: 559.98px) {
  .qcat-zone { display: grid; grid-template-columns: 1fr; gap: 1.15rem; align-items: start; }
  /* min-width:0 or a grid child refuses to shrink below its content's
     intrinsic width and pushes out of its track. */
  .qcat-zone > .form-field { margin-bottom: 0; min-width: 0; }
  /* Type is the form's own scale again. It was shrunk only to survive the
     narrow column, and shrunken labels next to full-size ones read as a
     mistake now that the column is full width. */
  /* Icon above the label, not beside it: side by side leaves roughly 85px of
     text width at 360px and "Take a photo" breaks across two lines. */
  .qcat-zone .photo-btn { flex-direction: column; gap: .35rem; min-height: 68px; font-size: .84rem; }
  .qcat-zone .photo-btn .icon { width: 20px; height: 20px; }
  /* Two upload buttons share a row: they are short labels and stacking them
     pushes the date field below the fold for no benefit. */
  .qcat-zone .photo-upload { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .55rem; }
  .qcat__stepper { gap: .45rem; flex: none; }
  .qcat__btn { width: 32px; height: 32px; font-size: 1.1rem; }
  .qcat__count { font-size: .95rem; }
  .qcat__grid { grid-template-columns: 1fr; gap: .5rem; }
  .qcat__card { flex-direction: row; text-align: left; padding: .6rem .7rem; min-height: 52px; }
  .qcat__icon, .qcat__icon svg { width: 26px; height: 26px; flex: none; }
  .qcat__name { flex: 1; min-width: 0; font-size: .9rem; }
  .qcat__subs { justify-content: flex-start; width: 100%; }
  .qcat__card--more { display: none; }
  .qcat.is-expanded .qcat__card--more, .qcat__card--more.is-on { display: flex; }
  .qcat__more {
    display: inline-flex; align-items: center; justify-content: center; gap: .3rem;
    width: 100%; min-height: 46px; margin-top: .3rem;
    border: 1px solid #D8E0E9; border-radius: 10px; background: #fff;
    font-weight: 700; font-size: .88rem; color: var(--navy-800);
    cursor: pointer; font-family: inherit;
  }
  .qcat__more .icon { width: 16px; height: 16px; }
}

/* A selected card grows a chip row, so it can no longer be a single flex
   line. Only the cards that actually have sub-choices are affected. */
@media (max-width: 559.98px) {
  .qcat__card:has(.qcat__subs:not([hidden])) { flex-wrap: wrap; row-gap: .45rem; }
}

/* ---- Log In ----------------------------------------------------------------- */
.lgn { padding-block: 1.4rem 4rem; }
.lgn .container { max-width: 1180px; display: grid; grid-template-columns: 1.05fr 1fr; gap: 3rem; align-items: start; }
.lgn-eyebrow { color: var(--orange-cta); font-weight: 800; font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; margin-bottom: .5rem; }
.lgn-title { font-size: clamp(1.9rem, 3.4vw, 2.5rem); letter-spacing: -.028em; margin-bottom: .7rem; }
.lgn-lede { color: #4A5B6C; line-height: 1.6; max-width: 44ch; margin-bottom: 1.4rem; }
.lgn-art { display: block; width: min(320px, 70%); height: auto; margin-bottom: 1.6rem; }
.lgn-feats { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.1rem; }
.lgn-feat { display: flex; gap: .9rem; align-items: flex-start; }
.lgn-feat__icon {
  flex: none; width: 42px; height: 42px; border-radius: 50%;
  background: #fff; border: 1px solid #E2E9F1; color: var(--navy-800);
  display: grid; place-items: center; box-shadow: 0 3px 10px rgba(14,39,64,.06);
}
.lgn-feat__icon svg { width: 20px; height: 20px; }
.lgn-feat strong { display: block; font-size: .95rem; color: var(--navy-800); margin-bottom: .15rem; }
.lgn-feat p { margin: 0; font-size: .86rem; color: #5A6B7B; line-height: 1.5; }

.lgn-card {
  background: #fff; border: 1px solid #E9EEF4; border-radius: 16px;
  box-shadow: 0 10px 32px rgba(14, 39, 64, .08);
  padding: 1.8rem 1.7rem;
}
.lgn-card__title { font-size: 1.5rem; letter-spacing: -.02em; margin-bottom: .2rem; }
.lgn-card__sub { color: #5A6B7B; font-size: .9rem; margin-bottom: 1.3rem; }
.lgn-row { display: flex; justify-content: space-between; align-items: center; margin: .7rem 0 1rem; font-size: .85rem; }
.lgn-remember { display: flex; align-items: center; gap: .45rem; color: #4A5B6C; }
.lgn-remember input { width: 17px; height: 17px; accent-color: var(--orange-cta); }
.lgn-forgot { color: var(--orange-cta); font-weight: 600; text-decoration: none; }
.lgn-forgot:hover { text-decoration: underline; }
.lgn-div { display: flex; align-items: center; gap: .8rem; color: #8A99A8; font-size: .8rem; margin: 1rem 0; }
.lgn-div::before, .lgn-div::after { content: ""; flex: 1; height: 1px; background: #E4EAF1; }
.lgn-note { text-align: center; color: #4A5B6C; font-size: .88rem; margin: 0 0 .8rem; }
.lgn-info {
  display: flex; gap: .7rem; align-items: flex-start;
  background: #EDF3F9; border-radius: 10px; padding: .9rem 1rem; margin-top: 1.2rem;
}
.lgn-info svg { width: 20px; height: 20px; flex: none; color: var(--navy-800); margin-top: .1rem; }
.lgn-info strong { display: block; font-size: .88rem; color: var(--navy-800); }
.lgn-info span { font-size: .83rem; color: #4A5B6C; line-height: 1.5; }
.btn--outline-navy { background: #fff; border: 1.5px solid #C9D6E4; color: var(--navy-800); font-weight: 700; }
.btn--outline-orange { background: #fff; border: 1.5px solid var(--orange-cta); color: var(--orange-cta); font-weight: 700; }
@media (max-width: 899.98px) {
  .lgn .container { grid-template-columns: 1fr; gap: 1.6rem; }
  .lgn-art { margin-inline: auto; }
}

@media (max-width: 1099.98px) {
  .svc-grid, .svc-inc { grid-template-columns: repeat(2, 1fr); }
  .svc-haul { grid-template-columns: 1fr 1fr; }
  .svc-haul__card { grid-column: 1 / -1; }
}
@media (max-width: 899.98px) {
  .svc { padding-top: .6rem; }
  .svc-grid, .svc-inc, .svc-haul { grid-template-columns: 1fr; }
  .svc-haul { padding: 1.4rem 1.2rem; }
  .cta-band { flex-direction: column; text-align: center; }
  .cta-band__btn { margin-left: 0; width: 100%; text-align: center; justify-content: center; display: flex; }
}

/* ==========================================================================
   19. DESKTOP QUOTE  (approved mockup)
   Two columns: a ~400px support rail (photo card, trust points, encrypted
   strip, the 3-step progress, "How pricing works") and the white form card.
   Everything here is ≥900px only; the phone flow is untouched — the rail is
   .d-only markup and the mobile hero/progress are hidden, not restyled.
   ========================================================================== */
@media (min-width: 900px) {
  body[data-route="/quote"] { background: var(--surface-muted); }

  /* The desktop heading is the one in the rail. Without this the mobile h1
     is still a grid child and claims the form card's column, dropping the
     card onto a second row. */
  .q-h1-m { display: none; }

  /* Leading row icons belong to the phone layout. The desktop fields are not
     rows, so an un-placed icon just stacks above its label. */
  .row-lead { display: none; }

  .quote-page {
    max-width: 1320px;
    display: grid;
    grid-template-columns: 400px minmax(0, 1fr);
    gap: 32px;
    align-items: start;
    padding: 24px 32px 72px;
  }

  /* The wizard is a phone. Desktop keeps the shipped two-column layout,
     so none of its chrome may leak here whatever the client script says. */
  .wiz-intro, .wiz-progress, .wiz-chip, .wiz-nopay,
  .wiz-cal, .wiz-quick, .wiz-times { display: none !important; }

  /* The card's sticky progress bar lives in the rail out here. */

  .q-card { padding: 32px 36px; border-radius: 18px; }
  .q-card__title { font-size: 1.55rem; }

  /* ---- Support rail ---- */
  .qd-rail { display: grid; gap: 16px; min-width: 0; }

  .qd-photo {
    position: relative;
    border-radius: 16px;
    overflow: hidden;
    background: var(--navy-900);
    box-shadow: 0 10px 26px rgba(14, 39, 64, .14);
  }
  /* Same photograph and framing rule as the homepage hero: the truck sits at
     the RIGHT of this source, so a high object-position-x keeps it in frame
     when cover crops the 16:9 image into this taller panel. */
  .qd-photo img {
    display: block;
    width: 100%; height: 300px;
    object-fit: cover; object-position: 70% 62%;
  }
  .qd-photo__overlay {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; justify-content: flex-end;
    padding: 20px;
    background: linear-gradient(180deg,
      rgba(9, 24, 40, .10) 0%, rgba(9, 24, 40, .38) 55%, rgba(9, 24, 40, .84) 100%);
  }
  .qd-photo__title { color: #fff; font-size: 1.7rem; line-height: 1.08; letter-spacing: -.03em; margin-bottom: 4px; }
  .qd-photo__sub { color: #D6E2EC; font-size: .9rem; }

  .qd-points { list-style: none; margin: 0; padding: 2px 6px 0; display: grid; gap: 10px; }
  .qd-points li { display: flex; align-items: center; gap: 10px; font-size: .92rem; font-weight: 600; color: var(--navy-900); min-width: 0; }
  .qd-points .icon { width: 19px; height: 19px; color: var(--orange-cta); flex: none; }

  .qd-secure {
    display: flex; align-items: flex-start; gap: 9px;
    background: #fff; border: 1px solid var(--line); border-radius: 12px;
    padding: 12px 14px;
    font-size: .84rem; color: var(--muted);
  }
  .qd-secure .icon { width: 18px; height: 18px; color: var(--green-600); flex: none; margin-top: 1px; }
  .qd-secure strong { color: var(--navy-900); }

  .qd-pricing { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; }
  .qd-pricing__title { font-size: 1.02rem; margin-bottom: .4rem; }
  .qd-pricing__text { font-size: .88rem; color: var(--muted); line-height: 1.55; }
  .qd-pricing__text strong { color: var(--navy-900); }
  .qd-pricing__link {
    display: inline-block; margin-top: .6rem;
    color: var(--orange-cta); font-weight: 700; font-size: .9rem;
    text-decoration: none;
  }
  .qd-pricing__link:hover { text-decoration: underline; }

  /* ---- Form card internals ---- */
  .loc-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }

  /* Date and time sit side by side, and their input boxes must line up.
     Both fields stretch to the tallest of the pair, but the time field has an
     extra status row, so the two grids had different amounts of slack to
     share out and their shells settled at different heights — 13px apart.
     Packing each field's rows to the top makes the label, then the box, land
     at the same y in both regardless of what follows them. */
  .date-time-grid > .form-field { align-content: start; }
  /* An empty status line is reserved space for a message that does not exist
     yet; it belongs under the control, not between it and the label. */
  .date-time-grid .field-status:empty { display: none; }
  /* Chrome gives a native date input a taller intrinsic box than a select,
     which left the two shells 2px apart even once their tops matched. Pinning
     the control height makes both shells exactly 56px. */
  .date-time-grid .input-shell > .native-date,
  .date-time-grid .input-shell > .native-time { height: 54px; padding-block: 0; }

  .choice-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* Category picker: spacious 4×3 grid. Photos become a right rail only once
     there is room for both — below 1200 the form card is not wide enough to
     hold four category columns AND a photo panel, so photos drop underneath
     rather than squeezing the cards to an unreadable width. */
  .qcat__grid { gap: .8rem; }
  .qcat__card { padding: .9rem .6rem .8rem; }
  .qcat-zone .photo-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qcat-zone { display: grid; gap: 22px; align-items: start; }
  /* Grid children default to min-width:auto, so the 4-column card grid can
     force its own track wider than 1fr and overlap the photo rail. This is
     the single line that stops that. */
  .qcat-zone > .form-field { min-width: 0; }

  /* ---- Benefits strip above the footer ---- */
  .qd-benefits { background: #fff; border-top: 1px solid var(--line); padding: 42px 0 46px; }
  .qd-benefits__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 28px; }
  .qd-benefit { display: grid; justify-items: center; text-align: center; gap: 8px; min-width: 0; }
  .qd-benefit__icon {
    width: 52px; height: 52px; border-radius: 50%;
    background: rgba(242, 107, 33, .12); color: var(--orange-cta);
    display: grid; place-items: center;
  }
  .qd-benefit__icon .icon { width: 26px; height: 26px; }
  .qd-benefit__title { font-size: 1rem; color: var(--navy-900); }
  .qd-benefit__text { font-size: .88rem; color: var(--muted); }
}

/* Photos become the category picker's right rail only where the form card is
   genuinely wide enough for both. */
@media (min-width: 1200px) {
  .qcat-zone { grid-template-columns: minmax(0, 1fr) 260px; }
  .qcat-zone .photo-upload { grid-template-columns: 1fr; }
}

/* ==========================================================================
   20. MOBILE QUOTE SCREEN  (approved phone mockup)

   The phone screen is a stack of white cards on the muted page, not one long
   card: progress, locations, items, photos, timing, loads, tips, submit. The
   single .q-card that wraps the form is dissolved here and each section
   becomes the card instead, so no extra wrappers were needed in the template
   and the desktop layout above is untouched.

   Every id, name and data- hook is exactly as it was — this is presentation
   only. The address fields are still real comboboxes with their own "use my
   location" buttons, and the date and time fields are still native pickers;
   the mockup's chevrons are drawn by those real controls rather than by
   decorative glyphs that look tappable and do nothing.
   ========================================================================== */
@media (max-width: 899.98px) {
  /* The mockup opens on the progress card, so the h1 is carried for the
     document outline without being drawn. */
  .q-h1-m {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }

  .quote-page { max-width: none; padding: 12px 12px calc(96px + env(safe-area-inset-bottom, 0px)); }

  /* Dissolve the outer card; the sections below become the cards. */
  .q-card { background: transparent; border: 0; box-shadow: none; padding: 0; border-radius: 0; }
  .q-card__head { display: none; }
  .quote-form { gap: 12px; }
  .form-section { gap: 12px; }

  /* ---- The wizard ------------------------------------------------------
     One question per screen. `wiz-off` is the whole mechanism: the current
     step is the one screen without it. Values in hidden steps are untouched
     — hiding is display, never data, and the form submits everything it
     holds wherever the customer is standing. */
  /* The pinned action bar's height: the button row, the foot line under it,
     and the padding around both — measured at 139px, plus air. Declared once
     so the page's bottom padding and the bar cannot drift apart, and a
     constant rather than something JavaScript measures after paint, because
     correcting it late would be a layout shift on a page whose whole point
     is not having any. The safe-area inset is added on top where it is used,
     never baked in here. */
  :root { --wiz-bar: 148px; }

  .wiz-off { display: none !important; }

  /* ---- The opening state, painted rather than scripted -------------------
     This used to be installed by app.js at DOMContentLoaded, which is after
     the browser has painted. The page arrived and then rearranged itself:
     main jumped up 56px as the install band went and lost 265px as seven of
     the eight steps folded away — 0.10 CLS, and the whole of this page's
     layout instability, in one frame that never needed to happen.

     Nothing below is a new decision. It is exactly what wizardShow(-1) does
     on its first call, said in CSS so it is true at the first paint rather
     than the fifth.

     THE TWO CLASSES. boot.js sets .js before the first paint — scripting is
     alive, so the wizard is coming, so paint it now. wizardShow adds
     .wiz-ready the instant it takes control, in the same frame it sets the
     real classes, and from there the classes it toggles are the only
     authority. Without .js — a browser with a dead script — none of this
     applies and the long form underneath stays exactly where it is, which
     is the fallback the markup was written for. */
  html.js:not(.wiz-ready) body[data-route="/quote"] .quote-form { display: none; }

  /* The boundary sentence sits inline in the markup on purpose: for a
     scripting-off browser, inline is the only option there is. With a script
     it is a popup — fixed, out of the flow — or it has been seen before and
     is hidden outright. Either way it is not part of this column, and being
     left in it for the first few hundred milliseconds pushed the porch down
     123px, which was the last of the jitter. */
  html.js:not(.wiz-ready) body[data-route="/quote"] .q-boundary { display: none; }

  /* The porch is hidden by the markup so a dead script still gets the form;
     with a live one it is the first thing on the screen. Beating [hidden]
     needs !important, because that is how [hidden] is set. */
  html.js:not(.wiz-ready) body[data-route="/quote"] .wiz-intro[hidden] {
    display: grid !important;
  }

  /* The wizard owns the bottom of the screen; the tab bar would crowd the
     Continue button it must never cover. */
  /* WHY EACH OF THESE IS SAID TWICE. app.js puts wiz-active on the body;
     boot.js puts it on the html element before the first paint, because in
     <head> there is no body to put it on yet. Same class, same declarations,
     two elements — so the chrome the wizard takes away is already gone in the
     first frame instead of disappearing a few frames later. */
  body.wiz-active .bottom-nav,
  html.wiz-active .bottom-nav { display: none; }

  /* 36px of "Home > Get a Quote" above a numbered wizard that already says
     where you are, on a screen fighting for every pixel. */
  body.wiz-active .crumbs,
  body.wiz-active .breadcrumbs,
  html.wiz-active .crumbs,
  html.wiz-active .breadcrumbs { display: none; }
  body.wiz-active .quote-page,
  html.wiz-active .quote-page { padding-bottom: 28px; }

  /* Each step is one raised card. */
  [data-slot] {
    background: #fff;
    border: 0;
    border-radius: 18px;
    padding: 18px 16px;
    box-shadow:
      0 1px 2px rgba(14, 39, 64, .06),
      0 12px 28px -14px rgba(14, 39, 64, .22);
  }
  [data-slot] > .q-step { margin-bottom: 12px; }

  /* ---- Progress: the pill, the dots, the bar ---- */
  .wiz-progress {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    background: #fff;
    border-radius: 14px;
    padding: 12px 14px 14px;
    box-shadow: 0 1px 2px rgba(14, 39, 64, .06);
  }
  .wiz-progress__pill {
    display: inline-flex; align-items: center; gap: 10px;
    font-weight: 800; font-size: 1.02rem; color: var(--navy-900);
  }
  .wiz-progress__num {
    display: grid; place-items: center;
    width: 30px; height: 30px;
    border-radius: 999px;
    background: var(--orange-cta); color: #fff;
    font-size: .9rem; font-weight: 800;
  }
  .wiz-progress__dots { display: inline-flex; gap: 7px; }
  .wiz-progress__dot {
    width: 7px; height: 7px; border-radius: 999px;
    background: #D8DEE6;
  }
  .wiz-progress__dot.is-done { background: var(--orange-cta); }
  .wiz-progress__bar {
    grid-column: 1 / -1;
    display: block; height: 6px; border-radius: 999px;
    background: #E8ECF1; overflow: hidden;
  }
  .wiz-progress__fill {
    display: block; height: 100%;
    border-radius: 999px;
    background: var(--orange-cta);
    transition: width .25s ease;
  }

  /* ---- The intro ---- */
  .wiz-intro { display: grid; gap: 12px; }
  .wiz-intro__card {
    background: #fff; border-radius: 18px; padding: 20px 16px;
    box-shadow:
      0 1px 2px rgba(14, 39, 64, .06),
      0 12px 28px -14px rgba(14, 39, 64, .22);
  }
  .wiz-intro__phases {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: 20px;
  }
  .wiz-intro__phases li {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: center; gap: 14px;
  }
  .wiz-intro__num {
    display: grid; place-items: center;
    width: 28px; height: 28px; border-radius: 999px;
    background: var(--orange-cta); color: #fff;
    font-size: .88rem; font-weight: 800;
  }
  .wiz-intro__icon {
    display: grid; place-items: center;
    width: 44px; height: 44px; border-radius: 999px;
    background: #F2F5F8; color: var(--navy-800);
  }
  .wiz-intro__icon .icon { width: 21px; height: 21px; }
  .wiz-intro__text { display: grid; gap: 2px; min-width: 0; }
  .wiz-intro__text strong { font-size: 1.05rem; color: var(--navy-900); }
  .wiz-intro__text span { font-size: .88rem; color: var(--muted); }
  .wiz-intro__clock {
    display: flex; align-items: center; gap: 10px;
    margin: 0; padding: 14px 16px;
    background: #fff; border-radius: 14px;
    box-shadow: 0 1px 2px rgba(14, 39, 64, .06);
    font-size: .95rem; color: var(--navy-800);
  }
  .wiz-intro__clock .icon { width: 20px; height: 20px; color: var(--navy-800); }
  .wiz-intro__actions { display: grid; gap: 10px; margin-top: 4px; }
  .wiz-intro__actions .secondary-button { text-decoration: none; }

  /* ---- The answer already given, on the next screen ---- */
  .wiz-chip {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 12px;
    width: 100%; margin: 0 0 14px; padding: 12px 14px;
    background: #FDF1E7; border: 0; border-radius: 14px;
    font: inherit; text-align: left; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .wiz-chip__icon {
    display: grid; place-items: center;
    width: 38px; height: 38px; border-radius: 999px;
    background: #fff; color: var(--orange-cta);
  }
  .wiz-chip__icon .icon { width: 19px; height: 19px; }
  .wiz-chip__text { display: grid; gap: 1px; min-width: 0; }
  .wiz-chip__label { font-size: .78rem; font-weight: 700; color: var(--muted); }
  .wiz-chip__value {
    font-size: .95rem; font-weight: 700; color: var(--navy-900);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .wiz-chip__chev { color: var(--navy-800); }
  .wiz-chip__chev .icon { width: 18px; height: 18px; }

  /* ---- Small print on the review screen ---- */
  .wiz-nopay {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin: 0; font-size: .88rem; color: var(--muted);
  }
  .wiz-nopay .icon { width: 16px; height: 16px; }

  /* ---- Calendar --------------------------------------------------------
     Built by script into its mount; while the script is absent the native
     date row stays and nothing here exists. has-wiz-* on the slot is what
     retires the native row — CSS alone must never hide the only control. */
  [data-slot].has-wiz-cal .dt-row { display: none; }
  [data-slot].has-wiz-times .dt-row { display: none; }

  .wiz-cal { display: grid; gap: 10px; }
  .wiz-cal__head {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 8px;
  }
  .wiz-cal__title { text-align: center; font-weight: 800; color: var(--navy-900); }
  .wiz-cal__nav {
    display: grid; place-items: center;
    width: 40px; height: 40px; border-radius: 999px;
    background: #F2F5F8; border: 0; color: var(--navy-800);
    font: inherit; cursor: pointer;
  }
  .wiz-cal__nav:disabled { opacity: .35; cursor: default; }
  .wiz-cal__nav .icon { width: 18px; height: 18px; }
  .wiz-cal__grid {
    display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 2px; text-align: center;
  }
  .wiz-cal__dow {
    font-size: .68rem; font-weight: 800; letter-spacing: .06em;
    color: var(--muted); text-transform: uppercase; padding: 4px 0;
  }
  .wiz-cal__day {
    display: grid; place-items: center;
    height: 42px; border: 0; border-radius: 999px;
    background: none; font: inherit; font-size: .95rem;
    color: var(--navy-900); cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .wiz-cal__day:disabled { color: #B9C2CC; cursor: default; }

  /* A DAY WE CANNOT SELL IS CROSSED OUT.
     Greyed alone was too quiet — a closed Sunday read as an ordinary day the
     customer had simply not tapped yet. The line says decided, not dim.
     line-through rather than a strikethrough character so the number stays
     one number to a screen reader, which is told the reason separately. */
  .wiz-cal__day.is-shut {
    text-decoration: line-through;
    text-decoration-thickness: 2px;
    text-decoration-color: #9AA7B4;
  }

  /* TODAY IS MARKED, NOT CHOSEN. A ring, in the same orange the chosen day
     fills with, so the two read as the same family without competing: the
     outline is where you are, the fill is what you picked. */
  .wiz-cal__day.is-today {
    box-shadow: inset 0 0 0 2px var(--orange-cta);
    font-weight: 800;
  }
  .wiz-cal__day.is-today.is-shut { box-shadow: inset 0 0 0 2px #D8B2A0; }

  .wiz-cal__day.is-selected {
    background: var(--orange-cta); color: #fff; font-weight: 800;
  }
  /* Chosen beats today: the fill already carries the ring's colour, and two
     rings on one circle is just noise. */
  .wiz-cal__day.is-selected.is-today { box-shadow: none; }

  /* ---- Quick picks ---- */
  .wiz-quick { display: grid; gap: 8px; }
  .wiz-quick__title { font-size: .85rem; font-weight: 800; color: var(--navy-900); }
  /* AS MANY COLUMNS AS THERE ARE CHIPS, which is not always two.
     This was hard-wired to 1fr 1fr from when both chips always showed. Now
     that the weekend chip hides itself on a week with no bookable weekend,
     the one chip left kept half the row and squeezed its own date into an
     ellipsis — "Mon, Aug …" — which is the one thing the chip exists to say.
     A hidden chip is display:none and so is not a grid item at all, so
     letting the columns be implicit gives one chip the whole width and two
     chips a half each, with nothing to keep in step by hand. */
  .wiz-quick__row { display: grid; gap: 10px; }
  .wiz-quick__chip {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: center; gap: 10px;
    padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px;
    background: #fff; font: inherit; text-align: left; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .wiz-quick__ic {
    display: grid; place-items: center;
    width: 34px; height: 34px; border-radius: 999px;
    background: #F2F5F8; color: var(--navy-800);
  }
  .wiz-quick__ic .icon { width: 17px; height: 17px; }
  .wiz-quick__text { display: grid; gap: 1px; min-width: 0; }
  .wiz-quick__text strong { font-size: .86rem; color: var(--navy-900); }
  .wiz-quick__text em {
    font-style: normal; font-size: .78rem; color: var(--muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* ---- Time windows as cards ---- */
  .wiz-times { display: grid; gap: 10px; }
  .wiz-times__note { margin: 0; font-size: .9rem; color: var(--muted); }
  .wiz-times__opt {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 12px;
    min-height: 56px; padding: 0 16px;
    border: 1.5px solid var(--line); border-radius: 14px;
    background: #fff; font: inherit; font-size: 1rem;
    color: var(--navy-900); text-align: left; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .wiz-times__opt .icon { width: 19px; height: 19px; color: var(--navy-800); }
  .wiz-times__check { display: none; }
  .wiz-times__opt.is-selected {
    border-color: var(--orange-cta);
    background: #FDF1E7;
    font-weight: 700;
  }
  .wiz-times__opt.is-selected .wiz-times__check {
    display: grid; place-items: center;
    width: 26px; height: 26px; border-radius: 999px;
    background: var(--orange-cta); color: #fff;
  }
  .wiz-times__opt.is-selected .wiz-times__check .icon {
    width: 14px; height: 14px; color: #fff;
  }

  /* ---- Room the wizard needs more than the page does ---------------------
     The install band is 56px of advertising on top of a form somebody is
     already filling in — they are not going to install an app mid-quote, and
     the offer is still there before and after. The step screens are the one
     place on this site fighting for every pixel. */
  body.wiz-active .install-band,
  html.wiz-active .install-band { display: none; }

  /* An error message appears on maybe one field in twenty. Reserving a line
     for the other nineteen cost 60px on the details step alone. The step is
     one question tall now, so a message pushing the layout down a line is a
     smaller cost than the room it was holding empty — and it only ever
     happens on the screen the customer is already looking at. */
  .form-field .field-error:empty,
  .form-section > .field-error:empty { display: none; }

  /* ---- One chrome, every step -------------------------------------------
     Back beside Continue rather than under it, and the two afterthoughts on
     one line. Repeated across eight screens, the old stack cost more room
     than any single question. */
  .form-actions--row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 10px;
    align-items: center;
  }
  /* Source order is Continue then Back, because Continue must be first for a
     screen reader and for a keyboard. Painted the other way round, because a
     thumb expects forward on the right. */
  .form-actions--row [data-step-next] { grid-column: 2; grid-row: 1; }
  .form-actions--row [data-step-back] {
    grid-column: 1; grid-row: 1;
    width: auto; min-width: 0; padding: 0 16px;
  }
  .form-actions--row [data-step-back][hidden] { display: none; }
  /* With Back hidden, Continue takes the whole row rather than half of it. */
  .form-actions--row:has([data-step-back][hidden]) { grid-template-columns: minmax(0, 1fr); }
  .form-actions--row:has([data-step-back][hidden]) [data-step-next] { grid-column: 1; }

  /* A STRIP, NOT A CARD. It was a white panel with its own padding and
     shadow wrapped around one line of text and a bar — card chrome spent on
     chrome. Stripped back it says exactly the same thing in 40px instead of
     74, on all eight screens. */
  .wiz-progress {
    background: none; box-shadow: none;
    padding: 6px 2px 0;
    gap: 8px;
  }
  .wiz-progress__num { width: 24px; height: 24px; font-size: .78rem; }
  .wiz-progress__pill { font-size: .9rem; gap: 8px; }
  .wiz-progress__bar { height: 5px; }

  /* The icon is a signpost, not the subject. */
  .q-step__icon { width: 40px; height: 40px; }
  .q-step__icon .icon { width: 20px; height: 20px; }
  .q-step { gap: 12px; }
  [data-slot] > .q-step { margin-bottom: 10px; }

  .q-notes { padding: 11px 13px; }
  .q-notes__summary { font-size: .92rem; }

  .acct--slim { padding: 10px 12px; }
  .acct--slim .acct__free { margin: 0; font-size: .85rem; }

  /* 18px between every block was set when a step was a section of a long
     page. On a screen that holds exactly one question, six of those gaps is
     a field's worth of nothing. */
  .form-section { gap: 12px; }
  [data-slot] > * + * { margin-top: 0; }

  /* The page reserved 96px at the bottom for a tab bar the wizard hides, and
     20px between each of its three blocks. Both were sized for a scrolling
     form; neither is affordable on a screen that must not scroll. */
  body.wiz-active .quote-page,
  html.wiz-active .quote-page {
    /* Room for the pinned bar, so the last thing on a screen is never left
       underneath it. The bar's height is fixed — two buttons and one line of
       foot — so this can be a constant rather than something measured. */
    padding-bottom: calc(var(--wiz-bar) + env(safe-area-inset-bottom, 0px));
  }

  /* ---- The bar stays where the thumb left it -----------------------------
     It used to sit at the end of the content, so it moved with the content:
     high on a short screen, below the fold on the step with a long list of
     times, and in a different place on almost every one of the eight. The
     owner, having had the automatic advance removed for the same reason:
     "I want the back and continue button to remain pinned at the bottom of
     the screen".

     One position, every step, so the thumb learns it once. Cancel and the
     padlock ride along, because they were already inside this block and
     leaving them behind in the scroll would strand them. */
  body.wiz-active .form-actions--row,
  html.wiz-active .form-actions--row {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 60;
    margin: 0;
    padding: 10px 16px calc(8px + env(safe-area-inset-bottom, 0px));
    background: var(--surface);
    border-top: 1px solid var(--line);
  }
  body.wiz-active .quote-form,
  html.wiz-active .quote-form { gap: 10px; }

  .q-foot {
    grid-column: 1 / -1;
    display: flex; align-items: center; justify-content: center;
    gap: 8px; flex-wrap: wrap;
    margin: 6px 0 0; font-size: .8rem; color: var(--muted);
  }
  .q-foot .icon { width: 14px; height: 14px; }
  .q-foot__safe { display: inline-flex; align-items: center; gap: 5px; }
  .q-foot__sep { opacity: .5; }

  /* ---- Things that fold rather than fill ---- */
  .q-notes__summary,
  .q-review-fold__summary,
  .q-estimate__fold .q-estimate__nexth {
    cursor: pointer; list-style: none;
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
  }
  .q-notes__summary::-webkit-details-marker,
  .q-review-fold__summary::-webkit-details-marker,
  .q-estimate__fold summary::-webkit-details-marker { display: none; }

  .q-notes {
    background: #fff; border: 1.5px solid var(--line); border-radius: 12px;
    padding: 13px 14px;
  }
  .q-notes__summary { font-weight: 600; color: var(--navy-800); }
  .q-notes[open] .q-notes__summary { margin-bottom: 10px; }

  .q-review-fold {
    background: #fff; border: 1.5px solid var(--line); border-radius: 12px;
    padding: 13px 14px;
  }
  .q-review-fold__summary { font-weight: 700; color: var(--navy-900); }
  .q-review-fold[open] .q-review-fold__summary { margin-bottom: 8px; }
  .q-review-fold[open] .icon { transform: rotate(180deg); }

  .q-estimate__fold summary { font-weight: 800; }

  /* The count lives in the progress bar up top; repeating it on the card
     would be the two-numbering-systems mistake again. */
  .q-step__n { display: none; }

  /* ---- Rows: leading icon, label and control ---- */
  .form-field--row {
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr);
    align-items: center;
    column-gap: 12px; row-gap: 0;
    padding: 12px 14px;
  }
  .row-lead { grid-row: span 2; display: flex; align-items: center; justify-content: center; color: var(--navy-800); }
  .row-lead .icon { width: 22px; height: 22px; }
  .row-lead--pickup { color: var(--orange-cta); }
  .form-field--row > label { grid-column: 2; font-size: .95rem; }
  .form-field--row .address-field,
  .form-field--row .input-shell { grid-column: 2; }
  .form-field--row .field-status,
  .form-field--row .field-error,
  .form-field--row .geo-cancel,
  .form-field--row .geo-proposal { grid-column: 2; }
  /* EVERY child of this row must name its column. The grid is
     `26px minmax(0, 1fr)` — a leading icon and the content — and a child
     that does not say otherwise auto-flows into that 26px icon column.
     The "used before" row did exactly that: its heading wrapped one word
     per line and its chips were crushed to a two-character sliver. */
  .form-field--row .q-recent { grid-column: 2; }
  /* An <input> cannot wrap, so a long located address ends mid-word. The
     proposal panel above is where it is read in full before being accepted;
     this just makes the committed value degrade to an ellipsis rather than a
     hard cut through a character. */
  .form-field--row .input-shell > input { text-overflow: ellipsis; }
  /* The shell is the row itself now, so it carries no second border. */
  .form-field--row .input-shell {
    border: 0; border-radius: 0; padding: 0; min-height: 32px; background: transparent;
  }
  .form-field--row .input-shell > input { padding: 0; font-size: 16px; }
  .form-field--row .input-shell:focus-within { outline: 2px solid var(--focus); outline-offset: 4px; }
  .form-field--row.is-invalid .input-shell { background: transparent; }
  .form-field--row .field-status:empty { display: none; }
  .form-field--row .field-error:empty { display: none; }
  .form-field--row .location-button { margin-right: -6px; }

  /* Each row is now its own card, so the wrappers only set the rhythm. */
  .loc-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
  .date-time-grid { grid-template-columns: 1fr; gap: 12px; }

  /* ---- Items ---- */
  .qcat-zone { gap: 12px; }
  .qcat__grid { margin-bottom: .5rem; }

  /* ---- Photos ----
     The label and the button text are a notch down from the form default so
     "(at least 1)" and "Add from phone" each hold one line at 360px. */
  .qcat-zone .photo-upload { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .qcat-zone > .form-field .field-label { font-size: .9rem; }
  .qcat-zone .photo-btn {
    flex-direction: row; gap: .45rem; min-height: 56px; padding: .6rem .4rem;
    font-size: .78rem; color: var(--orange-cta);
    border-color: #E3B49B; background: #fff;
  }
  .qcat-zone .photo-btn .icon { width: 19px; height: 19px; }

  /* ---- Loads ---- */
  .form-field--loads { padding: 14px; }
  .choice-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .choice__box { flex-direction: row; display: flex; align-items: center; justify-content: center; gap: .5rem; min-height: 50px; font-size: .9rem; }
  .choice__icon { width: 20px; height: 20px; flex: none; color: var(--navy-800); }
  .choice input:checked + .choice__box .choice__icon { color: var(--orange-cta); }
  .load-help { text-align: center; }
  .load-help summary { justify-content: center; color: var(--navy-800); text-decoration: underline; }

  /* ---- Tip + submit ---- */
  .form-tip { border: 1px solid #D7E3F1; }
  .form-actions { gap: 10px; }
  /* Squared-off rather than a pill, matching the mockup. The fill stays on
     the CTA orange: the brighter brand orange gives white text about 3.0:1,
     which fails AA for a control label. */
  .primary-button, .secondary-button { border-radius: 12px; }

  /* ---- Address cards, corrected from rendered pixels --------------------
     Deliberately the LAST word in this media block. The row grid's children
     all pin themselves to column 2 of a 26px+1fr grid from the era when the
     row WAS the card; those rules appear above, and equal-specificity CSS is
     won by whoever speaks last. This block speaks last.

     The lead pin is gone (the header carries the icon now), the grid is one
     honest column, and the input gets its box back — a field a customer
     must type into has to look like a field. */
  .form-field--row { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
  .form-field--row .row-lead { display: none; }
  .form-field--row > label,
  .form-field--row > .q-step,
  .form-field--row > .wiz-chip,
  .form-field--row .address-field,
  .form-field--row .input-shell,
  .form-field--row .field-status,
  .form-field--row .field-error,
  .form-field--row .geo-cancel,
  .form-field--row .geo-proposal,
  .form-field--row .q-recent { grid-column: 1; }
  .form-field--row .input-shell {
    border: 1.5px solid var(--line);
    border-radius: 12px;
    padding: 0 12px;
    min-height: 54px;
    background: #fff;
    margin-top: 4px;
  }
  .form-field--row .input-shell:focus-within {
    outline: none;
    border-color: var(--orange-cta);
  }

  /* These are <button>s, and buttons are Title Cased site-wide — the same
     trap the step headers hit. Their labels are sentences. */
  .wiz-chip, .wiz-quick__chip, .wiz-times__opt { text-transform: none; }

  /* ---- Motion -----------------------------------------------------------
     Classy rather than busy: things arrive from the direction they belong to,
     quickly, and settle. A sheet rises from the bottom because that is where
     a thumb is; a popup scales up a hair from centre because it interrupts;
     a scrim fades because a hard black rectangle appearing in one frame reads
     as a crash.

     Durations are short on purpose — 180 to 260ms. Anything slower is a
     customer waiting for an app to finish having an opinion, which is the
     opposite of the "super easy" this is for.

     Every one of these is switched off wholesale under prefers-reduced-motion
     at the end of this block. Motion is decoration here; nothing depends on
     it to be usable. */

  @keyframes bttFade { from { opacity: 0; } to { opacity: 1; } }
  @keyframes bttRise {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes bttSheetUp {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  @keyframes bttPop {
    from { opacity: 0; transform: translate(-50%, -50%) scale(.94); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  }

  /* Each question arrives, rather than replacing the last one in a jump. The
     wizard swaps one screen for another with no page load, so without this
     the change can be missed entirely. */
  /* BACKWARDS, NOT BOTH, and the difference is a bug not a preference.

     A transformed element becomes the containing block for every
     position:fixed descendant. `both` keeps the final keyframe applied for
     ever — and animating to `transform: none` does not save you, because the
     browser interpolates `none` as the IDENTITY MATRIX and that is what the
     fill leaves behind: matrix(1,0,0,1,0,0), which is not `none` and still
     establishes a containing block. Measured, after assuming otherwise.

     So every question screen stayed permanently transformed, and the item
     sheet — fixed to the bottom of the VIEWPORT — began resolving against
     the step instead, landing at top:-278px on an 844px screen with half of
     it above the top edge.

     `backwards` applies the first keyframe before the animation starts and
     NOTHING after it ends, so the element returns to having no transform at
     all. Identical to look at; the sheet works. */
  /* Not-yet-ready. Grey, but still listening — see paintNext. */
  .primary-button.is-idle {
    background: #DDE3E9;
    color: #77828E;
    box-shadow: none;
  }
  .primary-button.is-idle .primary-button__arrow { opacity: .5; }
  .primary-button.is-idle:active { background: #D3DAE1; transform: none; }

  /* ---- Direction ---------------------------------------------------------
     A wizard that plays the same animation going back as going forward is a
     slideshow, not an app. Forward, the next question comes in from the
     right; Back, the previous one comes in from the left — the direction a
     thumb would push a stack of cards. wizardShow writes the direction onto
     the form the moment it knows it, before it knows anything else.

     THE EASE is out-quint: leaves quickly, arrives slowly, never overshoots.
     A bounce on a form field is exactly the foolishness that was ruled out. */
  @keyframes bttInFwd {
    from { opacity: 0; transform: translate3d(26px, 0, 0); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes bttInBack {
    from { opacity: 0; transform: translate3d(-26px, 0, 0); }
    to   { opacity: 1; transform: none; }
  }
  /* FROM ABOVE, AND THAT IS NOT A TASTE DECISION.
     A transform contributes to the scrollable overflow area, so content
     nudged DOWNWARDS on a screen that already fills the viewport makes the
     page briefly scrollable — a vertical scrollbar flicks in, every column
     narrows by its width, and the whole page twitches sideways and back.
     Measured: 15px, twice, on a stagger of nine. Overflow above the top edge
     is never scrollable, so settling downwards into place costs nothing.
     The same class of bug as the jitter this animation was added alongside. */
  @keyframes bttSettle {
    from { opacity: 0; transform: translate3d(0, -9px, 0); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes bttNumBump {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.2); }
    100% { transform: scale(1); }
  }

  [data-slot]:not(.wiz-off) { animation: bttInFwd .26s cubic-bezier(.16, 1, .3, 1) backwards; }
  .quote-form[data-dir="back"] [data-slot]:not(.wiz-off) { animation-name: bttInBack; }

  /* THE PORCH DOES NOT SLIDE, for two reasons and the second one is a bug.
     You did not navigate forward to this screen, you arrived at it, and a
     sideways entrance says otherwise. And it is the one screen that animates
     while the document is still streaming in, where a horizontal transform
     meets a viewport whose width is still settling — measured as the header
     twitching 15px sideways and back, twice. Opacity asks nothing of the
     layout, so it cannot. */
  .wiz-intro { animation: bttFade .32s ease-out backwards; }

  /* ---- ...and the contents settle just behind it -------------------------
     The screen arrives as one card and the things on it land a beat later,
     in the order you read them. Four beats of 45ms is 135ms of stagger —
     shorter than the slide it sits under, so the two read as one movement
     rather than as a queue of separate animations.

     Everything here moves opacity and transform only. Nothing in this block
     can shift the layout, which matters: the page was just made to arrive
     with a CLS of zero and it has to stay that way. */
  [data-slot]:not(.wiz-off) > *,
  .wiz-intro__phases li,
  .wiz-intro__clock,
  .wiz-intro__actions > * {
    animation: bttSettle .22s cubic-bezier(.16, 1, .3, 1) backwards;
  }
  /* THE FIRST THING HAS NO DELAY. An earlier pass started every child at
     45ms and the card arrived as an empty white rectangle for a beat before
     anything was in it — which reads as loading, not as arriving. The head of
     the question is on screen in the same frame as the card that holds it,
     and only what follows it follows it. */
  [data-slot]:not(.wiz-off) > *:nth-child(2),
  .wiz-intro__phases li:nth-child(2) { animation-delay: .04s; }
  [data-slot]:not(.wiz-off) > *:nth-child(3),
  .wiz-intro__phases li:nth-child(3) { animation-delay: .08s; }
  [data-slot]:not(.wiz-off) > *:nth-child(n + 4) { animation-delay: .11s; }
  .wiz-intro__clock { animation-delay: .12s; }
  .wiz-intro__actions > *:nth-child(1) { animation-delay: .16s; }
  .wiz-intro__actions > *:nth-child(2) { animation-delay: .19s; }

  /* The review is the one screen that is a list, so it assembles like one —
     the answers landing in the order they were given. Ten rows at 26ms is
     quicker than it sounds and it is the last thing before the price. */
  .q-review__row { animation: bttSettle .24s cubic-bezier(.16, 1, .3, 1) backwards; }
  .q-review__row:nth-child(1) { animation-delay: .03s; }
  .q-review__row:nth-child(2) { animation-delay: .056s; }
  .q-review__row:nth-child(3) { animation-delay: .082s; }
  .q-review__row:nth-child(4) { animation-delay: .108s; }
  .q-review__row:nth-child(5) { animation-delay: .134s; }
  .q-review__row:nth-child(6) { animation-delay: .16s; }
  .q-review__row:nth-child(n + 7) { animation-delay: .186s; }

  /* The store rail runs sideways, so its cards arrive sideways. */
  .nearby-card { animation: bttInFwd .28s cubic-bezier(.16, 1, .3, 1) backwards; }
  .nearby-card:nth-child(1) { animation-delay: .03s; }
  .nearby-card:nth-child(2) { animation-delay: .07s; }
  .nearby-card:nth-child(3) { animation-delay: .11s; }
  .nearby-card:nth-child(4) { animation-delay: .15s; }
  .nearby-card:nth-child(n + 5) { animation-delay: .19s; }

  /* The step number counts. A digit that changes without moving is a digit
     nobody notices changing. */
  .wiz-progress__num.is-bump {
    animation: bttNumBump .34s cubic-bezier(.2, .8, .3, 1);
  }

  /* The progress bar fills rather than jumping to its new width. */
  .wiz-progress__fill { transition: width .32s cubic-bezier(.4, 0, .2, 1); }
  .wiz-progress__dot { transition: background-color .2s ease, transform .2s ease; }
  .wiz-progress__dot.is-done { transform: scale(1.15); }

  /* ---- Finding stores near you ------------------------------------------
     Two waits back to back — the phone fixing a position, then the search —
     and neither is ours to hurry. The button used to just go dead, which on
     a phone is indistinguishable from a tap that missed.

     A radar rather than a spinner: a spinner says "working", these rings say
     "looking outwards from here", which is what is actually happening. Three
     of them, 2.4s apart-ish, so it reads as unhurried rather than frantic —
     it will be on screen for several seconds and a fast animation would make
     those seconds feel longer, not shorter. */
  @keyframes bttRadar {
    0%   { transform: scale(.35); opacity: 0; }
    18%  { opacity: .55; }
    100% { transform: scale(1); opacity: 0; }
  }

  .q-finding[hidden] { display: none; }
  .q-finding__scrim {
    position: fixed; inset: 0; z-index: 130;
    background: rgba(14, 39, 64, .58);
    animation: bttFade .18s ease-out both;
  }
  .q-finding__card {
    position: fixed; z-index: 131;
    left: 50%; top: 50%; transform: translate(-50%, -50%);
    display: grid; justify-items: center; gap: 4px;
    width: min(80vw, 320px);
    padding: 26px 20px 18px;
    background: #fff; border-radius: 18px;
    box-shadow: 0 26px 60px -22px rgba(14, 39, 64, .55);
    text-align: center;
    animation: bttPop .22s cubic-bezier(.2, .8, .3, 1) both;
  }
  .q-finding__radar {
    position: relative;
    display: grid; place-items: center;
    width: 92px; height: 92px; margin-bottom: 6px;
  }
  .q-finding__ring {
    position: absolute; inset: 0;
    border-radius: 999px;
    border: 2px solid var(--orange-cta);
    animation: bttRadar 2.4s cubic-bezier(.2, .6, .3, 1) infinite;
  }
  .q-finding__ring:nth-child(2) { animation-delay: .8s; }
  .q-finding__ring:nth-child(3) { animation-delay: 1.6s; }
  .q-finding__pin {
    display: grid; place-items: center;
    width: 46px; height: 46px; border-radius: 999px;
    background: #FDEDE3; color: var(--orange-cta);
  }
  .q-finding__pin .icon { width: 22px; height: 22px; }
  .q-finding__t { font-size: 1.05rem; color: var(--navy-900); }
  .q-finding__s { font-size: .88rem; color: var(--muted); }
  .q-finding__cancel {
    margin-top: 14px; padding: 10px 20px; min-height: 44px;
    border: 1px solid var(--line); border-radius: 10px;
    background: #fff; color: var(--navy-800);
    font: inherit; font-weight: 700; font-size: .9rem;
    cursor: pointer; -webkit-tap-highlight-color: transparent;
  }
  .q-finding__cancel:active { background: var(--surface-muted); }

  /* The page behind does not scroll while it is up. */
  body.finding-open { overflow: hidden; }

  /* ---- The boundary, once ---- */
  .q-boundary-scrim {
    position: fixed; inset: 0; z-index: 120;
    background: rgba(14, 39, 64, .58);
    animation: bttFade .18s ease-out both;
  }
  .q-boundary.is-popup {
    position: fixed; z-index: 121;
    left: 50%; top: 50%;
    width: min(92vw, 420px);
    margin: 0; padding: 24px 20px 20px;
    background: #fff; border-radius: 18px;
    border-left: 5px solid var(--orange-cta);
    box-shadow: 0 26px 60px -22px rgba(14, 39, 64, .55);
    animation: bttPop .22s cubic-bezier(.2, .8, .3, 1) both;
  }
  .q-boundary__ok { margin-top: 18px; }

  /* The short version that rides the review step. */
  .q-boundary--mini { font-size: .9rem; line-height: 1.45; }

  /* ---- The item sheet ---- */
  .qcat-open {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; gap: 12px;
    width: 100%; margin: 0; padding: 15px 16px;
    background: #fff; border: 1.5px solid var(--line); border-radius: 14px;
    font: inherit; text-align: left; text-transform: none; cursor: pointer;
    transition: border-color .18s ease, box-shadow .18s ease;
  }
  .qcat-open:active { transform: translateY(1px); }
  .qcat-open:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
  .qcat-open__text { display: grid; gap: 3px; min-width: 0; }
  .qcat-open__label { font-weight: 700; color: var(--navy-900); }
  .qcat-open__sum {
    font-size: .88rem; color: var(--muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .qcat-open__sum:empty { display: none; }
  .qcat-open[hidden] { display: none; }

  /* Collapsed by default once the opener exists: the grid is still in the
     DOM, still wired, simply not occupying the step. */
  /* Any sheet-backed control collapses the same way — the picker and the
     calendar both. Scoped to .qcat first, which is why the calendar stayed
     full height on the step and the day opener did nothing visible. */
  .is-collapsed { display: none; }

  /* ---- ONE bottom sheet, used by two things ----------------------------
     The item picker and the nearby stores. Two copies of a modal is two sets
     of scrim, escape, scroll-lock and teardown to keep in step, and they
     drift; this cannot. The element is styled into a sheet where it already
     lives, never moved or rebuilt, so whatever was wired to it stays wired. */
  .btt-scrim {
    position: fixed; inset: 0; z-index: 110;
    background: rgba(14, 39, 64, .55);
    animation: bttFade .18s ease-out both;
  }
  /* `.is-sheet` alone loses to any later single-class rule on the same
     element — .nearby__stage { position: relative } sits 500 lines below
     this and was quietly winning, so the sheet resolved against the step and
     hung 229px off the bottom of the screen with its Close button below the
     fold. Measured, not guessed.

     Doubling the class raises specificity above any single-class rule
     without depending on where in the file it happens to appear, which is
     the property that actually needs to hold. */
  .is-sheet,
  .is-sheet.is-sheet {
    display: block;
    position: fixed; z-index: 111;
    left: 0; right: 0; bottom: 0;
    max-height: 86vh; overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    background: #fff;
    border-radius: 20px 20px 0 0;
    padding: 20px 16px calc(20px + var(--safe-b, 0px));
    box-shadow: 0 -18px 50px -18px rgba(14, 39, 64, .45);
    animation: bttSheetUp .26s cubic-bezier(.2, .8, .3, 1) both;
  }
  /* The grab handle every bottom sheet has, so it reads as one. */
  .is-sheet::before {
    content: ""; display: block;
    width: 40px; height: 4px; margin: 0 auto 16px;
    border-radius: 999px; background: #D8DEE6;
  }
  /* Sticky, with a band of its own. Without the backdrop the list scrolls
     visibly under a floating button and the two read as one broken layer;
     the negative margins let the band reach the sheet's padded edges. */
  .btt-sheet__done {
    position: sticky; bottom: 0; z-index: 1;
    margin: 16px -16px calc(-20px - var(--safe-b, 0px));
    width: calc(100% + 32px);
    border-radius: 0;
    box-shadow: 0 -14px 18px -8px #fff;
  }
  body.sheet-open { overflow: hidden; }

  /* ---- Stores, inside the sheet ----
     The rail is a horizontal carousel on the step, which is right for a
     glance and wrong for a list you are choosing from. In the sheet it
     becomes what it should be: a vertical list you scroll with a thumb,
     with the arrows retired because scrolling is the gesture now. */
  .nearby__stage.is-sheet .nearby__viewport {
    overflow: visible;
    scroll-snap-type: none;
  }
  .nearby__stage.is-sheet .nearby__list {
    display: grid;
    grid-auto-flow: row;
    grid-auto-columns: auto;
    gap: 10px;
    overflow: visible;
  }
  /* COMPACT IN A LIST, because a card designed to be glanced at sideways is
     the wrong shape stacked. The rail's card is a tall poster: logo on top,
     then name, category, address, distance. Two and a half of those fill a
     phone, so choosing between eight means scrolling — the exact thing this
     sheet exists to reduce.

     Laid on its side it says the same things in a third of the height: logo
     left, everything else right, distance on the end. */
  .nearby__stage.is-sheet .nearby-card {
    width: auto;
    min-width: 0;
    scroll-snap-align: none;
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr) auto;
    grid-template-rows: auto auto auto;
    align-items: center;
    column-gap: 12px;
    padding: 11px 13px;
    text-align: left;
  }
  /* Placed explicitly, because the card has no wrapper around its text —
     logo, name, kind, addr, foot and picked are all direct children, and a
     bare three-column grid would deal them out in reading order. */
  .nearby__stage.is-sheet .nearby-card__logo {
    grid-column: 1; grid-row: 1 / -1;
    width: 44px; height: 44px; margin: 0;
  }
  .nearby__stage.is-sheet .nearby-card__name { grid-column: 2; grid-row: 1; }
  .nearby__stage.is-sheet .nearby-card__kind { grid-column: 2; grid-row: 2; font-size: .68rem; }
  .nearby__stage.is-sheet .nearby-card__addr { grid-column: 2; grid-row: 3; }
  .nearby__stage.is-sheet .nearby-card__foot {
    grid-column: 3; grid-row: 1 / -1;
    display: grid; gap: 2px; justify-items: end; text-align: right;
  }
  .nearby__stage.is-sheet .nearby-card__picked {
    grid-column: 2 / -1; grid-row: 4; margin-top: 4px;
  }
  .nearby__stage.is-sheet .nearby-card__name,
  .nearby__stage.is-sheet .nearby-card__addr {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .nearby__stage.is-sheet .nearby-card__dist { margin: 0; white-space: nowrap; }
  .nearby__stage.is-sheet .nearby__arrow { display: none; }

  /* A chosen category should acknowledge the tap. */
  .qcat__card { transition: transform .14s ease, border-color .18s ease, background-color .18s ease; }
  .qcat__card:active { transform: scale(.97); }

  @media (prefers-reduced-motion: reduce) {
    /* Off wholesale. Nothing above is load-bearing. */
    [data-slot]:not(.wiz-off),
    [data-slot]:not(.wiz-off) > *,
    .wiz-intro,
    .wiz-intro__phases li,
    .wiz-intro__clock,
    .wiz-intro__actions > *,
    .q-review__row,
    .nearby-card,
    .wiz-progress__num.is-bump,
    .q-boundary.is-popup,
    .q-boundary-scrim,
    .q-finding__scrim,
    .q-finding__card,
    .is-sheet,
    .btt-scrim { animation: none; }
    /* The rings are the one thing here that must keep SOMETHING, or the card
       says "finding" beside a still picture. They hold at their widest and
       fade in place instead of travelling. */
    .q-finding__ring { animation: bttFade 1.4s ease-in-out infinite alternate; }
    .wiz-progress__fill,
    .qcat-open,
    .qcat__card { transition: none; }
    .wiz-progress__dot.is-done,
    .qcat__card:active,
    .qcat-open:active { transform: none; }
  }
}


/* ==========================================================================
   PHASE 3 — customer quote corrections.
   ========================================================================== */

/* ---- Preferred date / time as action rows --------------------------------
   These used to read as passive labelled fields, so people did not realise
   they were meant to tap them. They are now unmistakably controls: a full
   width row, an icon, a prompt, a chevron.

   The trick that makes it honest is that the REAL control is still the real
   control — the native <input type="date"> and the <select> are stretched
   transparently across the whole row rather than being replaced by something
   that only looks like them. Tapping anywhere hits the actual element, so the
   platform picker opens by itself, `required` still blocks submission, and
   the disabled state is the element's own rather than a class we remembered
   to add.
   -------------------------------------------------------------------------- */
.dt-field { display: grid; gap: 6px; }

/* `.dt-field > .dt-row` rather than `.dt-row`, because the row IS a <label>
   and `.form-field > label` already sets `display: block` at specificity
   (0,1,1). A bare `.dt-row` is (0,1,0) and loses, which silently stacked the
   icon, text and chevron vertically into a 112px block. Two classes beats one
   class plus an element, so this wins without !important. */
.dt-field > .dt-row {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  /* Comfortably past the 44px floor, with room for two lines of text at
     large font sizes without the row collapsing. */
  min-height: 60px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.dt-field > .dt-row:hover { border-color: var(--navy, #0A2540); }

/* The ring is drawn on the ROW because the real input inside it is invisible;
   without this a keyboard user would tab to a control with no visible focus
   at all. */
.dt-field > .dt-row:focus-within {
  border-color: var(--orange-cta, #F26B21);
  box-shadow: 0 0 0 3px rgba(242, 107, 33, .22);
}
.dt-field > .dt-row:active { background: #F7F9FC; }

.dt-row__icon { flex: none; display: flex; color: var(--navy, #0A2540); }
.dt-row__icon .icon { width: 22px; height: 22px; }

.dt-row__body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.dt-row__label { font-size: .78rem; font-weight: 700; color: var(--muted); }
.dt-row__value {
  font-size: 1rem; font-weight: 700; color: var(--ink, #0E2740);
  overflow-wrap: anywhere;
}
/* The prompt reads as a prompt, the answer reads as an answer. */
.dt-row__value.is-empty { font-weight: 600; color: #7A8A99; }

.dt-row__go { flex: none; display: flex; color: #93A1AF; }
.dt-row__go .icon { width: 20px; height: 20px; }

/* Loading, while the server works out which times are actually free. */
.dt-row__spin {
  flex: none; width: 18px; height: 18px;
  border: 2px solid var(--orange-cta, #F26B21); border-right-color: transparent;
  border-radius: 50%; animation: dt-spin .7s linear infinite;
}
@keyframes dt-spin { to { transform: rotate(360deg); } }

/* Disabled has to be obvious, not just slightly greyer — the time row spends
   most of its life here and a customer needs to see WHY nothing happens. */
.dt-field > .dt-row.is-disabled {
  background: #F4F6F8;
  border-color: #E3E9EF;
  cursor: not-allowed;
}
.dt-row.is-disabled .dt-row__icon,
.dt-row.is-disabled .dt-row__go { color: #AFBBC7; }
.dt-row.is-disabled .dt-row__value { color: #909EAC; }

/* The real control: present, focusable, and covering the row. Opacity rather
   than visibility or display, both of which would take it out of the
   accessibility tree and stop it being focusable. */
.dt-row__input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0;
  opacity: 0;
  cursor: inherit;
  -webkit-appearance: none; appearance: none;
  background: transparent;
  font: inherit; color: transparent;
}
.dt-row__input:disabled { cursor: not-allowed; }

@media (prefers-reduced-motion: reduce) {
  .dt-field > .dt-row { transition: none; }
  .dt-row__spin { animation-duration: 2s; }
}

/* Bottom navigation is fixed at the foot of the mobile shell, so the form has
   to end above it — otherwise the last control sits underneath and cannot be
   tapped. This is also what keeps a sticky bar from ever meeting it. */
/* ---------------------------------------------------------------------------
   Online scheduling temporarily unavailable.

   Sits directly above the date and time rows because that is what it
   qualifies. Informational, not an error: this is not the customer's fault
   and nothing they did has failed, so it borrows the muted card treatment
   rather than the red one.
   --------------------------------------------------------------------------- */
.dt-notice {
  margin: 0 0 14px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--line, #d8e0e8);
  background: var(--wash, #f4f7f9);
  color: var(--ink, #1d2b3a);
  font-size: 15px;
  line-height: 1.5;
}

/* ---------------------------------------------------------------------------
   Load count — full-width rows, not a two-up grid.

   The grid gave each option half a phone's width and .choice__box centred its
   children in a column, so "I'm not sure" broke to one word per line with its
   helper text crushed into a four-word column beside it. The label was
   unreadable and the box was twice the height of its neighbours.

   A row gives the text the whole width. The icon sits left at a fixed size,
   the label takes the rest, and the hint wraps onto its own line aligned under
   the label rather than fighting it for space.

   Scoped to the loads field: .choice-grid is shared, and the category picker
   genuinely wants a grid.
   --------------------------------------------------------------------------- */
.form-field--loads .choice-grid {
  grid-template-columns: 1fr;
  gap: 8px;
}

.form-field--loads .choice__box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  place-items: initial;          /* undo the grid centring */
  gap: 0 12px;
  min-height: 56px;
  padding: 12px 14px;
  text-align: left;
}

.form-field--loads .choice__icon {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
}

/* The label takes what is left of the row. */
.form-field--loads .choice__box > span:not(.choice__hint) {
  flex: 1 1 auto;
  min-width: 0;
}

/* Its own line, indented to sit under the label rather than the icon. */
.form-field--loads .choice__hint {
  flex: 1 0 100%;
  margin: 2px 0 0 34px;
  text-align: left;
  font-weight: 500;
  line-height: 1.35;
}

@media (min-width: 900px) {
  /* Still rows on a desktop — four half-width boxes across a wide form is
     more scanning, not less. Capped so the line length stays readable. */
  .form-field--loads .choice-grid { max-width: 520px; }
}

/* ---------------------------------------------------------------------------
   The load estimate card.

   The system's answer to the question the customer used to be asked. Quiet
   confidence styling by state: high reads settled, moderate reads provisional,
   review reads like a person will look at it — never like an error, because
   nothing the customer did is wrong.
   --------------------------------------------------------------------------- */
.q-estimate {
  margin: 4px 0 14px;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1.5px solid var(--line, #d8e0e8);
  background: #fff;
}
.q-estimate__h {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--navy-900, #12304e);
}
.q-estimate__body {
  margin: 0 0 6px;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--navy-900, #12304e);
}
.q-estimate__detail {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted, #5a6b7b);
}
.q-estimate[data-confidence="high"]     { border-color: #9CC5A5; background: #F4FAF5; }
.q-estimate[data-confidence="moderate"] { border-color: #E4C98E; background: #FDF9F0; }
.q-estimate[data-confidence="review"]   { border-color: #C9D4E2; background: #F5F8FB; }

/* ---- Question rows inside a category card ---- */
.qcat__q {
  display: block;
  width: 100%;
  margin-top: 8px;
}
.qcat__q-label {
  display: block;
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted, #5a6b7b);
}
.qcat__dimrow {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}
.qcat__dim {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding: 8px 10px;
  border: 1.5px solid #CBD5E1;
  border-radius: 10px;
  font-size: 15px;
}
.qcat__dim--name { margin-top: 2px; }

/* ---------------------------------------------------------------------------
   The review price and the approval.

   The figure is the biggest thing on the screen because it is the thing being
   agreed to. The checkbox names that exact figure and load count; the brand
   button below it stays visually identical, only gated.
   --------------------------------------------------------------------------- */
.q-price__big {
  margin: 4px 0 6px;
  font-size: 40px;
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--navy-900, #12304e);
  text-align: center;
}

.q-approve {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 18px 0 4px;
  padding: 14px 16px;
  border: 1.5px solid var(--line, #d8e0e8);
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  font-size: 15px;
  line-height: 1.5;
  color: var(--navy-900, #12304e);
}
.q-approve input {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  accent-color: var(--orange-cta, #c84a0d);
}
.q-approve:has(input:checked) {
  border-color: var(--orange-cta, #c84a0d);
  background: rgba(200, 74, 13, 0.05);
}

/* ---------------------------------------------------------------------------
   The install band.

   A permanent horizontal invitation at the very top of the screen — never
   inside the installed app, which is what the display-mode query is for.
   On the public shell it is a link to the customer app, where installation
   can actually happen; on the customer shell it is a button that triggers
   the native prompt or the instruction sheet.
   --------------------------------------------------------------------------- */
.install-band {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* Second layer under the border-box reset: this band spans the viewport,
     so it must never be the thing that makes a page scroll sideways —
     whatever a future stylesheet does to its padding. */
  max-width: 100%;
  min-height: 44px;
  padding: 10px 14px;
  border: 0;
  background: #12304E;
  color: #fff;
  font-size: 13.5px;
  line-height: 1.35;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.install-band__icon { flex: 0 0 auto; font-size: 16px; }
.install-band > span:nth-child(2) { flex: 1; min-width: 0; }
.install-band__cta {
  flex: 0 0 auto;
  padding: 6px 14px;
  border-radius: 999px;
  background: #F26B21;
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  white-space: nowrap;
}

@media (display-mode: standalone) {
  .install-band { display: none; }
}

/* ---------------------------------------------------------------------------
   /install — the public how-to-install page. Two cards, two gestures, no
   account. The band lands here whenever the browser cannot fire a native
   prompt.
   --------------------------------------------------------------------------- */
.inst { padding: 24px 0 40px; max-width: 560px; }
.inst__h { margin: 0 0 10px; font-size: 26px; line-height: 1.15; font-weight: 800;
  letter-spacing: -0.01em; color: var(--navy-900, #12304e); }
.inst__lead { margin: 0 0 20px; font-size: 16px; line-height: 1.55; color: var(--muted, #5a6b7b); }
.inst__card { margin: 0 0 14px; padding: 16px 18px; border: 1.5px solid var(--line, #d8e0e8);
  border-radius: 14px; background: #fff; }
.inst__plat { margin: 0 0 8px; font-size: 17px; font-weight: 700; color: var(--navy-900, #12304e); }
.inst__steps { margin: 0; padding-left: 22px; font-size: 15px; line-height: 1.8;
  color: var(--navy-900, #12304e); }
.inst__note { margin: 18px 0 0; font-size: 14px; line-height: 1.55; color: var(--muted, #5a6b7b); }

/* ---------------------------------------------------------------------------
   Nearby pickup locations — the store rail above the pickup address.

   One horizontal row that owns its overflow: cards scroll inside the
   viewport, the page never gains a horizontal scrollbar. Discoverability is
   layered — a partially visible next card (scroll padding), a right-edge
   fade while more remains, arrows on desktop, a one-time swipe hint on
   phones — so no single cue carries the whole job.
   --------------------------------------------------------------------------- */
/* min-width: 0 is load-bearing. The section is a grid item of .form-section,
   and a grid item's automatic minimum size counts the card row's full width
   EVEN THROUGH the scroll container — intrinsic sizing ignores overflow. So
   the item refused to shrink below ~1400px and dragged the whole page wide;
   the inner scroller, given that width, had nothing left to scroll. Verified
   by harness: page scrollWidth 1456 -> 360 with this one line. */
/* Addresses used before — a scrolling rail of chips under each address
   field, shown only while that field is empty.

   No heading of its own: the glyph inside each chip carries the meaning,
   which keeps a secondary shortcut from spending a whole line of a form
   that is already long on a phone.

   Same overflow discipline as the store rail: min-width 0 so the row can
   shrink below its contents, and the scrolling happens in here rather than
   in the page. */
.q-recent { margin: 10px 0 2px; min-width: 0; max-width: 100%; }
.q-recent__row {
  display: flex; gap: 8px; min-width: 0; max-width: 100%;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; padding: 1px 1px 3px;
}
.q-recent__row::-webkit-scrollbar { display: none; }

.q-recent__chip {
  flex: 0 0 auto; max-width: 15.5rem;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 38px; padding: 7px 14px 7px 11px;
  border: 1px solid var(--line); border-radius: 999px;
  /* A filled pill rather than an outline: it has to read as a control
     among a column of plain text, not as a label. */
  background: var(--surface-muted); color: var(--navy-900);
  font: inherit; font-size: 13.5px; font-weight: 600; line-height: 1.2;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.q-recent__glyph {
  flex: 0 0 auto; width: 15px; height: 15px;
  color: var(--muted);
}
/* The address itself is the part that may be long; the glyph never shrinks
   and the ellipsis lands on the text. */
.q-recent__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --orange-cta, not the brand orange: this sits against a pale fill at
   normal text size, which is the bar --orange-500 does not clear. */
.q-recent__chip:hover { background: var(--surface); border-color: var(--orange-cta); }
.q-recent__chip:hover .q-recent__glyph { color: var(--orange-cta); }
.q-recent__chip:active { background: #EDF1F5; }
.q-recent__chip:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .q-recent__chip { transition: none; }
}

.nearby { margin: 0 0 18px; min-width: 0; max-width: 100%; }
.nearby__stage { min-width: 0; }
.nearby__viewport { max-width: 100%; }
.nearby__heading { font-size: 1.02rem; font-weight: 800; color: var(--navy-900); margin: 0; }
.nearby__sub { margin: 2px 0 10px; font-size: .86rem; color: var(--muted); }

.nearby__locate { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 10px; }

/* NO LOCATION ON A DESKTOP — the owner's call: "we have the qr code now,
   remove that from desktop." A mouse-driven machine mostly has no GPS, so
   the browser answers with an IP guess or a denial, and the phone hand-off
   is the honest route to real location anyway.

   Same capability grammar as the hand-off gate, minus the width clause: this
   is a fact about the MACHINE, not the window. A narrow laptop window still
   books (the form comes back below 900px) but is not offered a location
   feature the hardware cannot honour. Typing the pickup address is untouched
   either way. */
@media (pointer: fine) and (hover: hover) {
  .nearby__locate { display: none; }
}
.nearby__locate-btn {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px; padding: 0 14px;
  border: 1.5px solid var(--line); border-radius: 999px; background: #fff;
  font: inherit; font-size: .9rem; font-weight: 700; color: var(--navy-800);
  cursor: pointer;
}
.nearby__locate-btn:hover { border-color: var(--navy-800); }
.nearby__locate-btn:disabled { opacity: .6; cursor: default; }
.nearby__locate-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
.nearby__locate-note { font-size: .78rem; color: var(--muted); }

.nearby__stage { position: relative; }

/* The scroller. scroll-padding keeps a tapped-into-view card fully visible;
   the trailing padding guarantees the next card peeks at every width. */
.nearby__viewport {
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  scroll-padding: 0 20px;
}
.nearby__viewport::-webkit-scrollbar { display: none; }
.nearby__viewport.has-more {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent);
}

.nearby__list {
  display: flex; gap: 10px;
  margin: 0; padding: 2px 28px 6px 2px;   /* right padding = the peek */
  list-style: none;
}

.nearby-card {
  flex: 0 0 168px; width: 168px;
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px;
  border: 1.5px solid var(--line); border-radius: 14px; background: #fff;
  cursor: pointer; scroll-snap-align: start;
  position: relative;
}
.nearby-card:hover { border-color: var(--navy-800); }
.nearby-card:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.nearby-card.is-selected { border-color: var(--orange-cta); border-width: 2px; padding: 11.5px; background: #FFF7F2; }

.nearby-card__logo {
  width: 44px; height: 44px; margin-bottom: 4px;
  border: 1px solid var(--line); border-radius: 10px; background: #fff;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.nearby-card__logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.nearby-card__initial {
  font-size: 1.25rem; font-weight: 800; color: var(--navy-800);
}
.nearby-card__name {
  font-size: .92rem; font-weight: 800; color: var(--navy-900); line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nearby-card__kind { font-size: .74rem; font-weight: 700; color: var(--orange-cta); text-transform: uppercase; letter-spacing: .03em; }
.nearby-card__addr {
  font-size: .8rem; color: var(--muted); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nearby-card__foot { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 4px; }
.nearby-card__dist { font-size: .8rem; font-weight: 700; color: var(--navy-800); }
.nearby-card__open { font-size: .74rem; font-weight: 700; color: var(--muted); }
.nearby-card__open.is-open { color: #1E7B34; }
.nearby-card__picked {
  display: block; margin-top: 6px; font-size: .76rem; font-weight: 800; color: var(--orange-cta);
}
.nearby-card__picked::before { content: "\2713\00a0"; }

.nearby__arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%;
  border: 1.5px solid var(--line); background: #fff; color: var(--navy-900);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; z-index: 2;
  box-shadow: 0 2px 10px rgba(14, 39, 64, .12);
}
.nearby__arrow svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2.2; }
.nearby__arrow:disabled { opacity: .35; cursor: default; }
.nearby__arrow--prev { left: -10px; }
.nearby__arrow--next { right: -10px; }

.nearby__hint { margin: 4px 0 0; font-size: .78rem; color: var(--muted); text-align: right; }

/* Ghost cards while the single search runs. Never shown beside results. */
.nearby__skeleton { display: flex; gap: 10px; padding: 2px 2px 6px; }
.nearby__skeleton span {
  flex: 0 0 168px; height: 148px; border-radius: 14px;
  background: linear-gradient(100deg, #EFF2F5 40%, #F7F9FA 50%, #EFF2F5 60%);
  background-size: 200% 100%;
  animation: nearby-shimmer 1.2s linear infinite;
}
@keyframes nearby-shimmer { to { background-position: -200% 0; } }

@media (prefers-reduced-motion: reduce) {
  .nearby__skeleton span { animation: none; }
  .nearby__viewport { scroll-behavior: auto; }
}

/* Desktop: same compact row inside the form column; arrows appear (they are
   d-only in markup), the swipe hint does not (m-only). */
@media (min-width: 900px) {
  .nearby__list { padding-right: 40px; }
}

/* House style: buttons wear Title Case (see customer.css for the same rule
   on the portal). Applied in CSS so every template stays sentence case
   underneath and no grep-guarded string changes. */
button, .btn { text-transform: capitalize; }

/* ==========================================================================
   "You load it. We drive it. You unload it."

   Was a drawn animation; the owner replaced it with photographs of the real
   work. The captions live in the artwork, so there is nothing to lay over
   it and nothing to animate — the whole component is one responsive image
   that stops growing once it has room to breathe.
   ========================================================================== */
.ldu { margin: 0; min-width: 0; max-width: 100%; }
.ldu__img {
  display: block; width: 100%; height: auto;
  max-width: 1040px; margin: 0 auto;
}

/* ==========================================================================
   Size chips that say what the size IS.
   "Small / Medium / Large" is our vocabulary, not the customer's — and a
   tester proved it in seconds. It also is not cosmetic: every load count
   comes from the profile behind the chosen chip, so a guessed size is a
   guessed estimate. Each chip now carries a bar drawn in proportion to the
   real item and a caption in plain feet, both read out of the catalog the
   packer measures.

   The bar width is a class rather than an inline style because the CSP
   forbids inline style — seven buckets, snapped server-side.
   ========================================================================== */
.qcat__chip--sized {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 3px; padding: 8px 12px; min-width: 92px; text-align: left;
}
.qcat__chip-name { display: block; line-height: 1.2; }
.qcat__chip-size {
  display: block; font-size: 11px; font-weight: 600; line-height: 1.2;
  color: var(--muted); white-space: nowrap;
  /* Buttons are capitalized site-wide, which is right for a label and
     wrong for a measurement: "About 4 Ft Across" reads like a title. */
  text-transform: none;
}
.qcat__chip--sized[aria-pressed="true"] .qcat__chip-size { color: inherit; opacity: .8; }

/* The proportional bar. A rule of a fixed track with a filled portion reads
   as "this much of the biggest one" without needing a legend. */
.qcat__gauge {
  display: block; width: 46px; height: 5px; border-radius: 999px;
  background: var(--line); overflow: hidden;
}
.qcat__gauge-fill {
  display: block; height: 100%; border-radius: 999px;
  background: var(--orange-cta);
}
.qcat__chip--sized[aria-pressed="true"] .qcat__gauge { background: rgba(255, 255, 255, .35); }
.qcat__chip--sized[aria-pressed="true"] .qcat__gauge-fill { background: #fff; }

.qcat__gauge-fill[data-w="40"]  { width: 40%; }
.qcat__gauge-fill[data-w="50"]  { width: 50%; }
.qcat__gauge-fill[data-w="60"]  { width: 60%; }
.qcat__gauge-fill[data-w="70"]  { width: 70%; }
.qcat__gauge-fill[data-w="80"]  { width: 80%; }
.qcat__gauge-fill[data-w="90"]  { width: 90%; }
.qcat__gauge-fill[data-w="100"] { width: 100%; }

/* ==========================================================================
   The boundary statement.
   "You load it. We drive it. You unload it." — a transport service, not a
   moving company, and the driver does not lift. Given a quiet tone but a
   visible one: an orange rule and a tinted panel, because this is the one
   sentence a customer must not skim past and then discover at the kerb.
   ========================================================================== */
.hero__boundary,
.q-boundary {
  margin: 14px 0 18px; padding: 12px 14px;
  border-left: 4px solid var(--orange-cta);
  border-radius: 0 10px 10px 0;
  background: var(--surface-muted);
  font-size: 14.5px; line-height: 1.5; color: var(--navy-900);
  min-width: 0; max-width: 100%;
}
.hero__boundary strong,
.q-boundary strong { display: block; font-weight: 800; margin-bottom: 2px; }

.q-boundary { margin: 0 0 16px; font-size: 13.5px; }

/* The lede above the animation on the marketing page. */
.section__lede {
  /* Centred, because the heading above it is. Left-aligned under a centred
     title reads as a mistake, which is what it was. */
  max-width: 62ch; margin: -6px auto 26px;
  font-size: 16px; line-height: 1.6; color: var(--muted);
  text-align: center;
  /* No orphan: the last line had a single word ("yours.") sitting alone.
     text-wrap: pretty asks the browser to pull a word down rather than
     strand one, and the non-breaking space in the markup guarantees at
     least two words on the final line where pretty is unsupported. */
  text-wrap: pretty;
}
.section__lede strong { color: var(--navy-900); font-weight: 800; }

/* ==========================================================================
   Mobile website menu.

   A hamburger in the app bar, for the WEBSITE only. In the installed app
   the bottom navigation is always on screen and this bar is the whole top
   orientation, so hiding destinations behind a tap would cost something and
   buy nothing — it is switched off under (display-mode: standalone), the
   same test the install band uses.

   It also carries the two destinations the bottom bar has no room for:
   Services and Safety live nowhere else on a phone.
   ========================================================================== */
@media (max-width: 899.98px) {
  .app-bar__actions { display: flex; align-items: center; gap: 2px; }

  .app-bar__burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;              /* full-size target, no smaller */
    padding: 0; border: 0; background: none; color: var(--navy-900);
    cursor: pointer;
  }
  .app-bar__burger-icon { width: 24px; height: 24px; }
  /* One button, two glyphs: the close cross replaces the bars while open, so
     the control that opened the menu is the control that shuts it. */
  .app-bar__burger-icon--close { display: none; }
  .app-bar__burger[aria-expanded="true"] .app-bar__burger-icon--open { display: none; }
  .app-bar__burger[aria-expanded="true"] .app-bar__burger-icon--close { display: inline-block; }

  .app-menu { position: fixed; inset: 0; z-index: 70; }
  .app-menu[hidden] { display: none; }

  /* Tapping away closes it — expected of every drawer, and the reason the
     scrim is a real element rather than a shadow. */
  .app-menu__scrim { position: absolute; inset: 0; background: rgba(10, 27, 51, .45); }

  .app-menu__panel {
    position: absolute; top: 0; right: 0; bottom: 0;
    width: min(82vw, 320px);
    display: flex; flex-direction: column; gap: 8px;
    padding: calc(var(--safe-t) + 14px) 18px calc(var(--safe-b) + 18px);
    background: #fff; box-shadow: -12px 0 32px rgba(10, 27, 51, .18);
    overflow-y: auto;
  }

  .app-menu__list { list-style: none; margin: 0 0 10px; padding: 0; }
  .app-menu__link {
    display: flex; align-items: center; min-height: 48px;
    padding: 0 4px; border-bottom: 1px solid var(--line);
    font-size: 1.02rem; font-weight: 600; color: var(--navy-900);
    text-decoration: none;
  }
  .app-menu__link[aria-current="page"] { color: var(--orange-cta); }
  .app-menu__link:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
}

/* The app has its own navigation, permanently on screen. */
@media (display-mode: standalone) {
  .app-bar__burger, .app-menu { display: none !important; }
}

/* ==========================================================================
   DIALOGS — one primitive, two jobs: finding a location, and abandoning a
   quote. Both need the same things (a scrim, a panel that owns focus, a
   scroll lock) and neither is worth its own machinery.
   ========================================================================== */
.dlg { position: fixed; inset: 0; z-index: 200; display: flex;
  align-items: center; justify-content: center; padding: 18px; }
.dlg[hidden] { display: none; }
.dlg__scrim { position: absolute; inset: 0; background: rgba(8, 21, 38, .55);
  animation: dlg-fade 160ms ease-out both; }
.dlg__panel {
  position: relative; width: 100%; max-width: 400px; max-height: 90vh;
  overflow-y: auto; box-sizing: border-box;
  background: #fff; border-radius: 18px; padding: 26px 22px 22px;
  box-shadow: 0 24px 60px rgba(8, 21, 38, .3);
  animation: dlg-rise 200ms cubic-bezier(.2, .8, .3, 1) both;
}
.dlg__panel:focus { outline: none; }
.dlg__title { margin: 0 0 8px; font-size: 20px; line-height: 1.25;
  font-weight: 800; color: var(--navy-900); text-align: center; }
.dlg__text { margin: 0 0 18px; font-size: 15px; line-height: 1.55;
  color: var(--muted); text-align: center; }
.dlg__acts { display: grid; gap: 10px; }
.dlg__btn {
  min-height: 50px; padding: 12px 18px; border-radius: 12px;
  border: 1.5px solid var(--line); background: #fff;
  font: inherit; font-size: 16px; font-weight: 700; color: var(--navy-900);
  cursor: pointer;
}
.dlg__btn--primary { background: var(--orange-cta); border-color: var(--orange-cta); color: #fff; }
.dlg__btn--danger  { background: #b3261e; border-color: #b3261e; color: #fff; }
.dlg__btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- The beacon. Two rings leaving a pin, forever, while the device
   thinks. This is the whole point of the dialog: motion says "working"
   before any sentence is read. ---- */
.geo-find { text-align: center; }
.geo-find__beacon {
  position: relative; display: grid; place-items: center;
  width: 112px; height: 112px; margin: 4px auto 18px;
}
.geo-find__ring {
  position: absolute; width: 34px; height: 34px; border-radius: 50%;
  border: 2.5px solid var(--orange-cta);
  animation: geo-ping 2s cubic-bezier(.2, .7, .4, 1) infinite;
}
.geo-find__ring:nth-child(2) { animation-delay: 1s; }
.geo-find__pin { position: relative; width: 42px; height: 42px; color: var(--navy-900); }
.geo-find__acc { margin: 10px 0 0; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }

@keyframes geo-ping {
  0%   { transform: scale(.6); opacity: .9; }
  80%  { transform: scale(2.9); opacity: 0; }
  100% { transform: scale(2.9); opacity: 0; }
}
@keyframes dlg-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes dlg-rise {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

/* The found address, offered rather than assumed. */
.geo-found__addr { margin: 0 0 6px; font-size: 17px; line-height: 1.4;
  font-weight: 700; color: var(--navy-900); text-align: center; }
.geo-found__acc { margin: 0 0 4px; font-size: 13px; color: var(--muted); text-align: center; }
.geo-found__warn { margin: 10px 0 0; padding: 10px 12px; border-radius: 10px;
  background: #fdf3e7; color: #8a4b12; font-size: 13.5px; line-height: 1.5; text-align: left; }
.geo-found__warn + .geo-found__warn { margin-top: 8px; }
.geo-found .dlg__acts { margin-top: 18px; }

/* The quiet way out of the quote. Present on every step, never competing
   with Continue. */
.q-abandon {
  /* Inside .form-actions now, so it sits with Continue and Back rather than
     below the legal line. Still quiet: leaving is available, never urged. */
  display: block; margin: 4px auto 0; padding: 10px 16px;
  min-height: 44px; border: 0; background: none;
  font: inherit; font-size: 14px; font-weight: 600;
  color: var(--muted); text-decoration: underline;
  text-underline-offset: 3px; cursor: pointer;
}
.q-abandon:hover { color: #b3261e; }
.q-abandon:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 8px; }

/* Nothing pulses or slides for anyone who asked for stillness; the beacon
   simply rests, and the dialog appears without travelling. */
@media (prefers-reduced-motion: reduce) {
  .dlg__scrim, .dlg__panel, .geo-find__ring { animation: none; }
  .geo-find__ring { opacity: .35; transform: scale(1.8); }
}
/* The page behind a dialog must not scroll. On iOS a scrollable body steals
   the gesture and the panel sits still while the page slides underneath. */
body.is-dlg-open { overflow: hidden; }

/* ==========================================================================
   NEXT, WITHOUT HUNTING FOR IT.
   Every selection used to leave the customer where they stood, scrolling to
   find what was still empty. That is gone: nothing carries the customer
   anywhere any more, so there is no destination to light on arrival, and
   the glow that lit it is gone with it. Continue is the only way forward.
   ========================================================================== */

/* The estimate card: the number, then what happens next.
   Two paragraphs of explanation became one line plus three short steps —
   less text, and it answers the question a bare figure always raises. */
.q-estimate__figure {
  margin: 2px 0 4px;
  font-size: 1.6rem; line-height: 1.2; font-weight: 800;
  color: var(--navy-900); letter-spacing: -.015em;
}
.q-estimate__nexth {
  margin: 16px 0 6px;
  font-size: .74rem; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.q-estimate__next {
  margin: 0; padding: 0 0 0 1.15em;
  font-size: .95rem; line-height: 1.5; color: var(--navy-900);
}
.q-estimate__next li { margin: 0 0 5px; padding-left: 2px; }
.q-estimate__next li:last-child { margin-bottom: 0; }

/* The floating nudge. Offered, never imposed: it appears only when the next
   empty thing is off-screen, and it sits clear of the primary button. */

@keyframes jump-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  }

/* Desktop has no bottom bar to clear. */
@media (min-width: 900px) {
  }
