/* ═════════════════════════════════════════════════════════════
   QUOTE FORM  (Figma: desktop 1572:4928, mobile 1566:4580,
                in page 1566:4198 / 1566:3972)

   The site's master form: a five-step flow that lives inline on the
   contact page and is summoned as a modal by every "get free quote"
   button elsewhere. Both mounts render the same DOM — see
   initQuoteForm() in main.js, which builds it from one QUOTE_STEPS
   array so the five pages can never drift apart.

   Every colour here is an existing token: Figma's #FFFCF2 is
   --offwhite, #DAEBF3 is --light-blue, and the blue/navy pair are
   --blue/--navy. Nothing new was introduced.

   Type is in FIXED px, not the vw-scaled tokens the rest of the site
   uses. Those exist so headings track the viewport; this card is
   max-width:720px, so its interior does not grow with the viewport
   and vw type would drift away from the drawing. The whole scale is
   re-anchored once at 640px, which is where Figma's mobile board is
   drawn.
   ═════════════════════════════════════════════════════════════ */

.quote{
  /* Every dimension Figma varies between the two boards, declared once
     so the mobile block below only has to restate the numbers.

     Trimmed ~18% from the 1920 artboard on purpose. Drawn at Figma's
     values the tallest step ran 1056px, which overflows a 963px viewport
     and gets worse on a laptop — the card is a modal on four of the five
     pages, so it has to fit the screen, not the board. Proportions and
     the type hierarchy are unchanged; only the scale moved. */
  --q-pad:40px;         --q-gap:24px;        --q-radius:28px;
  --q-head:34px;        --q-head-ls:-.68px;  --q-sub:18px;
  --q-step:13px;        --q-pct:21px;
  --q-card-pad:22px;    --q-card-radius:18px;
  --q-row-pad:14px;     --q-row-radius:12px; --q-row-label:17px;
  --q-banner-h:172px;   --q-banner-radius:16px;
  --q-treat-h:165px;
  /* A row is as tall as its glyph, not its text: 32px of icon inside a
     12px chip set the height everywhere and the label just sat in it.
     These two are the compaction lever. */
  --q-ico:24px;         --q-chip-pad:8px;

  max-width:720px;
  /* Queried below for the treat labels. It has to be the CARD, not the
     viewport: the card is a fluid grid column on the contact page but a
     fixed 720 inside the modal, so the same viewport means two different
     amounts of room. */
  container-type:inline-size;
  background:var(--offwhite);
  border:2px solid var(--light-blue);
  border-radius:var(--q-radius);
  padding:var(--q-pad);
  box-shadow:0 16px 20px rgba(21,26,90,.1);
  color:var(--navy);
}

/* One step at a time. Height is not animated — the steps differ by
   hundreds of pixels and a height transition on a card this tall reads
   as a lurch, so only opacity moves. */
.quote__step{display:flex; flex-direction:column; gap:var(--q-gap)}
.quote__step.is-entering{animation:quote-in .32s var(--ease) both}
@keyframes quote-in{ from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none} }
@media (prefers-reduced-motion:reduce){ .quote__step.is-entering{animation:none} }

/* ── Progress ────────────────────────────────────────────────── */
.quote__progress{display:flex; flex-direction:column; gap:10px}
.quote__progress-row{
  display:flex; align-items:center; justify-content:space-between;
  white-space:nowrap;
}
.quote__count{
  font-size:var(--q-step); font-weight:700; text-transform:uppercase;
  opacity:.6; margin:0;
}
/* Brush Script, like every other accent number on the site */
.quote__pct{
  font-family:var(--font-script,'Brush Script Std',cursive);
  font-style:italic;
  font-size:var(--q-pct); line-height:.7; letter-spacing:-.02em;
  color:var(--blue); margin:0;
}
.quote__track{
  height:6px; border-radius:3px;
  background:var(--light-blue); overflow:hidden;
}
.quote__fill{
  height:100%; width:var(--pct,20%);
  background:var(--blue);
  transition:width .4s var(--ease-inout);
}
@media (prefers-reduced-motion:reduce){ .quote__fill{transition:none} }

/* ── Heading ─────────────────────────────────────────────────── */
.quote__head{display:flex; flex-direction:column; gap:8px}
.quote__title{
  font-family:var(--font-display,'PP Neue Montreal Mono',ui-monospace,monospace);
  font-weight:700; text-transform:uppercase;
  font-size:var(--q-head); line-height:1; letter-spacing:var(--q-head-ls);
  margin:0;
}
.quote__sub{font-size:var(--q-sub); line-height:1.3; opacity:.8; margin:0}

.quote__banner{
  width:100%; height:var(--q-banner-h);
  border-radius:var(--q-banner-radius);
  object-fit:cover; object-position:center;
}

/* ── Options ─────────────────────────────────────────────────── */
.quote__options{display:flex; flex-direction:column; gap:9px}
.quote__options--cards{gap:14px}

/* Real <button>s: these are the step's controls, so they must be
   reachable and operable from the keyboard without any ARIA patching. */
.quote__opt{
  display:flex; align-items:center; justify-content:space-between;
  gap:14px; width:100%;
  text-align:left; font:inherit; color:inherit;
  background:#fff;
  border:1px solid var(--light-blue);
  border-radius:var(--q-row-radius);
  padding:var(--q-row-pad);
  cursor:pointer;
  transition:border-color .2s var(--ease),
             box-shadow   .2s var(--ease);
}
/* The selected border is 2px and the resting one 1px. Padding is
   trimmed by the difference so the row does not jump 1px when picked. */
.quote__opt[aria-checked="true"]{
  border:2px solid var(--blue);
  padding:calc(var(--q-row-pad) - 1px);
}
.quote__opt:hover{border-color:var(--blue)}
.quote__opt:focus-visible{outline:2px solid var(--blue); outline-offset:3px}

.quote__opt-main{display:flex; align-items:center; gap:12px; min-width:0}
.quote__opt-label{font-weight:700; font-size:var(--q-row-label); margin:0}
.quote__opt-sub{font-size:14px; opacity:.6; margin:0}
.quote__opt-text{display:flex; flex-direction:column; gap:2px; min-width:0}

/* ── Icons ───────────────────────────────────────────────────────
   Figma exports a separate file per colour state — the selected and
   unselected glyphs are byte-identical apart from the stroke. Masking
   one file and painting it with `color` collapses that to a single
   asset per glyph and makes the state change a plain colour change,
   which is what a radio state should be. */
.quote__ico{
  flex:0 0 auto;
  width:var(--q-ico); height:var(--q-ico);
  background-color:currentColor;
  -webkit-mask:var(--ico) center / contain no-repeat;
          mask:var(--ico) center / contain no-repeat;
}
.quote__opt .quote__ico{color:var(--navy)}
.quote__opt[aria-checked="true"] .quote__ico{color:var(--blue)}

/* The chip behind the step-1 and step-4 glyphs */
.quote__chip{
  flex:0 0 auto;
  display:grid; place-items:center;
  background:var(--offwhite);
  border:1px solid var(--light-blue);
  border-radius:10px; padding:var(--q-chip-pad);
}
.quote__opt[aria-checked="true"] .quote__chip{
  background:var(--light-blue); border-color:transparent;
}

/* Selection indicator. Unselected is a bare 2px ring — Figma ships it
   as an SVG circle, but a border draws the identical shape with no
   request, so only the three real glyphs are files. */
.quote__mark{
  flex:0 0 auto;
  width:18px; height:18px; border-radius:50%;
  border:2px solid var(--light-blue);
}
/* Tied to the glyph size: at 32px against a 24px icon it was the tallest
   thing in the row and set its height on its own. */
.quote__opt[aria-checked="true"] .quote__mark{
  width:var(--q-ico); height:var(--q-ico); border:0;
  background-color:var(--blue);
  -webkit-mask:var(--mark) center / contain no-repeat;
          mask:var(--mark) center / contain no-repeat;
}

/* ── Step 1 — the three big connect cards ────────────────────── */
.quote__opt--card{
  flex-direction:column; align-items:stretch; gap:14px;
  border-radius:var(--q-card-radius);
  padding:var(--q-card-pad);
}
.quote__opt--card[aria-checked="true"]{
  padding:calc(var(--q-card-pad) - 1px);
  box-shadow:0 8px 12px rgba(56,71,195,.07);
}
.quote__opt--card .quote__opt-top{
  display:flex; align-items:center; justify-content:space-between;
}
.quote__opt--card .quote__opt-label{font-size:21px}
.quote__opt--card .quote__opt-sub{font-size:15px; line-height:1.35}

/* ── Step 3 — "Something else" reveals a note field ───────────── */
.quote__opt--fill{flex-direction:column; align-items:stretch; gap:16px}
.quote__opt--fill .quote__opt-top{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
}
/* Collapsed until its row is picked, so the step keeps its drawn height */
.quote__note{
  display:block; width:100%;
  font:inherit; font-size:16px; color:var(--navy);
  background:var(--offwhite);
  border:1px solid var(--light-blue);
  border-radius:8px;
  padding:14px 16px;
  resize:none;
}
.quote__note::placeholder{color:var(--navy); opacity:.6}
.quote__note:focus-visible{outline:2px solid var(--blue); outline-offset:2px}

/* ── Step 6 — the property address ───────────────────────────── */
/* The flow's only typed answers. Shaped like the "Something else" note field
   above, but on white like the option rows, so they stand off the offwhite
   card; 16px text also stops iOS zooming the page on focus. City and ZIP
   share a row. :user-invalid, not :invalid, so an untouched required field
   is never red on arrival. Chrome paints autofilled fields its own pale
   blue, which the inset shadow covers back to white. */
.quote__fields{display:grid; grid-template-columns:1fr 1fr; gap:10px}
.quote__input{
  grid-column:1 / -1;
  display:block; width:100%; min-width:0;
  font:inherit; font-size:16px; color:var(--navy);
  background:var(--white);
  border:1px solid var(--light-blue);
  border-radius:8px;
  padding:14px 16px;
}
.quote__input:-webkit-autofill{
  -webkit-box-shadow:0 0 0 100px var(--white) inset;
  -webkit-text-fill-color:var(--navy);
}
.quote__input--half{grid-column:auto}
.quote__input::placeholder{color:var(--navy); opacity:.6}
.quote__input:focus-visible{outline:2px solid var(--blue); outline-offset:2px}
.quote__input:user-invalid{border-color:var(--error)}

/* ── Step 5 — treat cards ────────────────────────────────────── */
.quote__treats{display:flex; gap:16px; align-items:stretch}
.quote__treat{
  /* Row only. flex-basis is on the MAIN axis, so the moment these stack this
     has to become auto or basis-0 zeroes their HEIGHT — with overflow:hidden
     that clips the photo away and leaves two hairlines. */
  flex:1 1 0; min-width:0;
  display:flex; flex-direction:column;
  /* .quote__opt centres its children, which is right for a row of label +
     mark but wrong here: it sized the photo and the label row to their own
     content and left white gutters down both sides of the card. */
  align-items:stretch;
  justify-content:flex-start;
  padding:0; overflow:hidden;
  border-radius:var(--q-card-radius);
}
.quote__treat img{display:block; width:100%}
.quote__treat[aria-checked="true"]{padding:0; box-shadow:0 8px 24px rgba(56,71,195,.07)}
.quote__treat img{width:100%; height:var(--q-treat-h); object-fit:cover}
.quote__treat-row{
  display:flex; align-items:center; justify-content:space-between;
  gap:12px; padding:16px;
}
.quote__treat .quote__opt-label{font-size:18px}
/* The treats are the only options that sit SIDE BY SIDE, so the mark going
   20px→32px on selection dropped one label 11px below the other. The ring
   keeps its drawn 24px; only its layout box is padded out to 32 so the two
   rows always agree. The stacked option rows are left exactly as drawn —
   nothing sits beside them for the change in height to disagree with. */
.quote__treat .quote__mark{width:32px; height:32px; border:0; position:relative}
.quote__treat .quote__mark::before{
  content:''; position:absolute; inset:4px;
  border-radius:50%; border:2px solid var(--light-blue);
}
.quote__treat[aria-checked="true"] .quote__mark::before{content:none}
/* Side by side, "Empanadas 🥟" needs 177px of label beside a 32px mark, so a
   treat card has to be ≥249px and the row ≥522px. Below that no font size
   rescues it — at the narrow end the cards are ~155px each — so they stack,
   exactly as the mobile board already does.

   The number is the CONTENT box, which is what container-type:inline-size
   measures: the 720 card Figma draws queries as 596 once its 60px padding
   and border come off. Keyed to the card rather than the viewport because
   on the contact page it is a fluid grid column but in the modal a fixed
   720 — the same viewport leaves two different amounts of room. */
@container (max-width:540px){
  .quote__treats{flex-direction:column}
  .quote__treat{flex:0 0 auto}      /* see .quote__treat — basis-0 zeroes height here */
}

/* ── Navigation ──────────────────────────────────────────────── */
.quote__nav{
  display:flex; align-items:center; justify-content:space-between;
  gap:20px; padding-top:10px;
}
.quote__nav--end{justify-content:flex-end}
/* The last step keeps Back on the left and submit on the right, like every
   other step — Figma centres a lone submit there, but see main.js. */
/* Step 1 draws no controls, so its nav is an empty 20px of padding */
.quote__nav--end:not(:has(.btn:not([hidden]))){display:none}
/* Mobile draws a text back-link and a reassurance line instead of the
   two ovals; both are in the DOM at every width and swapped by CSS. */
.quote__back-sm,
.quote__secure{display:none}

/* A step cannot be left until it has an answer */
.quote [disabled]{opacity:.35; cursor:not-allowed}

/* ── Sent ────────────────────────────────────────────────────── */
.quote__done{display:flex; flex-direction:column; gap:16px; text-align:center}
.quote__done .quote__title{font-size:var(--q-head)}

/* ═════════════════════════════════════════════════════════════
   MODAL — the shell JS builds on pages that have no inline form.
   Mirrors the site menu's contract (hidden + .is-open, lenis stopped,
   focus trapped); FADE in main.js must match the duration here.
   ═════════════════════════════════════════════════════════════ */
.quote-modal{
  position:fixed; inset:0; z-index:120;
  display:grid; place-items:center;
  /* The full gutter (~70px) top and bottom cost more height than the card
     could spare; the side inset still matters, the vertical one only has
     to keep the card off the edge. */
  padding:24px var(--gutter);
  overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
}
.quote-modal__scrim{
  position:fixed; inset:0;
  background:rgba(21,26,90,.55);
  opacity:0; transition:opacity .4s var(--ease);
}
.quote-modal__panel{
  position:relative;
  width:100%; max-width:720px;
  margin:auto;
  opacity:0; transform:translateY(16px) scale(.98);
  transition:opacity .4s var(--ease),
             transform .4s var(--ease);
}
.quote-modal.is-open .quote-modal__scrim{opacity:1}
.quote-modal.is-open .quote-modal__panel{opacity:1; transform:none}
/* The panel already carries the card, so it must not nest a second one */
.quote-modal .quote{max-width:none; margin:0}

/* Parked on the card's top-right CORNER, outside it. Inside, it always
   landed on the progress row's "100% Complete" — that row spans the full
   width, so there is no free space at the top for it to occupy, and
   indenting the row to make room just knocked the text out of alignment
   with the bar beneath it. Out here nothing has to make room. */
.quote-modal__close{
  position:absolute; top:-16px; right:-16px; z-index:2;
  width:40px; height:40px;                 /* 40 keeps it a real touch target */
  display:grid; place-items:center;
  border-radius:50%;
  background:var(--light-blue); color:var(--navy);
  border:2px solid var(--offwhite);        /* separates it from the scrim behind */
  font-size:20px; line-height:1;
  transition:background-color .2s var(--ease);
}
.quote-modal__close:hover{background:var(--blue); color:var(--cream)}
.quote-modal__close:focus-visible{outline:2px solid var(--blue); outline-offset:3px}

@media (prefers-reduced-motion:reduce){
  .quote-modal__scrim,
  .quote-modal__panel{transition:none}
}

/* ═════════════════════════════════════════════════════════════
   Mid-band — the card keeps its desktop scale but 60px of padding is
   too much once it is near the viewport width.
   ═════════════════════════════════════════════════════════════ */
@media (max-width:1024px){
  .quote{--q-pad:30px; --q-gap:20px}
}

/* ═════════════════════════════════════════════════════════════
   MOBILE  (Figma 1566:4580 — drawn at 390)
   A separate board, not the desktop card scaled down.
   ═════════════════════════════════════════════════════════════ */
@media (max-width:640px){
  .quote{
    --q-pad:24px;        --q-gap:28px;
    --q-head:28px;       --q-head-ls:-.56px;  --q-sub:15px;
    --q-step:13px;       --q-pct:20px;
    --q-card-pad:20px;   --q-card-radius:16px;
    --q-row-pad:16px;    --q-row-radius:12px; --q-row-label:16px;
    --q-banner-h:156px;  --q-banner-radius:14px;   /* +20% */
    --q-treat-h:132px;                            /* +20% */
    --q-ico:28px;        --q-chip-pad:8px;
    padding:40px var(--q-pad);
    box-shadow:0 16px 40px rgba(21,26,90,.1);
  }
  .quote__progress{gap:12px}
  .quote__sub{line-height:1.4}
  .quote__options{gap:10px}
  .quote__options--cards{gap:16px}
  .quote__opt-main{gap:12px}
  .quote__opt-sub{font-size:12px}
  .quote__opt--card{gap:16px}
  .quote__opt--card .quote__opt-label{font-size:20px}
  .quote__opt--card .quote__opt-sub{font-size:14px}
  .quote__opt--fill{gap:12px}
  .quote__note{font-size:13px; padding:10px 12px}

  /* Treats stack rather than sit side by side */
  .quote__treats{flex-direction:column; gap:12px}
  .quote__treat{flex:0 0 auto}
  .quote__treat-row{padding:16px}
  .quote__treat .quote__opt-label{font-size:18px}

  /* Selecting a row advances, so there is no Next to draw — just the
     back link and the reassurance line Figma pairs it with. */
  .quote__nav{padding-top:12px}
  .quote__nav .btn{display:none}
  /* Only the SUBMIT oval, not every .btn — once the last step gained a Back
     the blanket rule surfaced it too, and mobile then had an oval BACK
     stacked above the "← Back" text link. Going back is the text link's job
     at this width. */
  .quote__nav--submit .btn--fill{display:inline-flex}
  .quote__back-sm{
    display:inline-block;
    font-weight:700; font-size:15px; text-transform:uppercase;
    opacity:.5; background:none; border:0; padding:0;
    color:inherit; cursor:pointer;
  }
  .quote__back-sm:focus-visible{outline:2px solid var(--blue); outline-offset:3px}
  .quote__secure{display:block; font-size:13px; font-weight:500; opacity:.4; margin:0}
  .quote__nav--submit{flex-direction:column; gap:16px}

  .quote-modal{padding:0}
  .quote-modal__panel{max-width:none; min-height:100%}
  /* Full-bleed here, so there is no outside for the close to sit in — it
     comes back inside, and the card takes a taller top pad so the progress
     row starts below it rather than under it. The extra height is free:
     this panel already runs the full viewport. */
  .quote-modal .quote{border-radius:0; border:0; min-height:100dvh; padding-top:72px}
  /* Fixed, not absolute: these steps are taller than a phone screen, so an
     absolutely-placed close scrolled away with the content and left no way
     out without scrolling back up. */
  .quote-modal__close{position:fixed; top:16px; right:16px; border-width:0}
}
