/* ==========================================================================
   /quote — the 9-step wizard.  Built same-to-same from Figma R4YlVFqXtGfx3rlvWSMro8,
   screens 7064:2469 / 3970 / 4062 / 4141 / 4292 / 4376 / 4454 / 4547 / 4631.

   Linked FROM app/quote/page.tsx, never from the layout (quote.css's bare
   `.step{display:none}` is why that rule exists).  Namespace is `.qz-` —
   verified zero hits across base/home/home-sections/home-mobile/motion.

   Every length is rem = figma_px / 16, against base.css's fluid root
   (calc(100vw/120) above 768, calc(100vw/26.75) below).  Never write px here.
   ========================================================================== */

/* ---- tokens local to this page ---------------------------------------- */
.qz {
  --qz-card-line: rgba(17, 17, 17, 0.06);   /* option card, resting  (Figma stroke #111 @ .06) */
  --qz-card-line-h: rgba(17, 17, 17, 0.12); /* option card, hover                        @ .12 */
  --qz-chip: #f5f6f0;
  --qz-ghost: rgba(17, 17, 17, 0.08);       /* disabled CTA ground */
  --dur-zoom: 700ms;                        /* re-declared per CLAUDE.md, not imported */

  /* Step change, and the sidebar stepper that tracks it (user, 2026-08-06:
     "transition or slow kro", then "step transition ki trah steps ki b
     transition smooth kro"). Figma's own value is 300ms, so both of these are
     authored — the file only ever specifies DISSOLVE / EASE_OUT / 300ms.

     NOT --ease-premium. That token is an expo-out: it spends ~80% of the travel
     in the first fifth of the duration, so raising the duration on it lengthens
     the tail without making the movement look slower — the same trap the
     homepage's sheen hit ("achanak se guzar jati hai"). This is a quad ease-out,
     which actually uses the 600ms. */
  --qz-dur-step: 600ms;
  --qz-ease-step: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* ---- shell ------------------------------------------------------------- */
.qz {
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Figma's frame is 1920x1010 — a viewport-height screen, not a document — so the
   shell is 100vh (user, 2026-08-06: "make this 100vh"). 1010 stays as the FLOOR:
   the panes' content is laid out against Figma's 930 of absolute offsets, so
   below 1010 the screen scrolls rather than crushing them. At exactly 1010 tall
   this is pixel-identical to the fixed height it replaces.

   What that costs: anything Figma pins to the bottom of a pane has to be
   bottom-anchored rather than sitting at a Figma `top`, or it floats mid-screen
   on a taller window. Those four are .qz-rule-bot / .qz-side-bot in the sidebar
   and .qz-cant / .qz-dots in the main pane. Figma states the intent explicitly
   on the reviews row — Frame 12 is `constraints=CENTER/BOTTOM`. */
.qz-shell {
  width: 100%;
  height: 100vh;
  padding: 2.5rem;                          /* 40 */
  display: flex;
  gap: 2.5rem;                              /* 540 - 40 - 460 */
}

/* ---- the scale is HEIGHT-bound here, unlike every other page -------------
   User, 2026-08-06: "quote k sare pages ko 100vh kro. page pe scroll ni hona
   chahiye. andr wale section ko usi trah 100vh k according adjust honge height
   wise." The panes' content is 930 of absolute Figma offsets, so a shell that is
   merely 100vh would overflow the moment the window is shorter than 1010 — the
   content itself has to shrink. Everything here is already rem, so the one knob
   that does that is the root.

   base.css scales the root off viewport WIDTH (1920 canvas). This takes the
   MIN of that and a height-driven scale on Figma's own 1010 frame, so the design
   fits whichever axis is binding and the page never scrolls:
     1920x1010 -> min(16, 16)        = 16      identical to the width-only scale
     1920x800  -> min(16, 12.67)     = 12.67   height-bound, content is exactly 800
     1920x1200 -> min(16, 19.01)     = 16      width-bound; the spare height is
                                               absorbed by the centred search card
                                               and the bottom-anchored footer
   63.125 = 1010 / 16.

   This does NOT touch base.css — CLAUDE.md forbids editing a shared rule there,
   and it must not leak off this route. `html:has(.qz)` is a new selector in the
   page's own sheet, and at (0,1,1) it outranks base.css's bare `html` (0,0,1)
   without depending on load order. It is deliberately gated to >= 768: below
   that base.css switches to the 428 mobile canvas, and /quote has no mobile
   layout yet, so that band stays untouched for whoever builds it. 767.99 rather
   than 768 keeps it an exact complement of base.css's `max-width: 767.98px` —
   no gap, no overlap. */
@media (min-width: 767.99px) {
  html:has(.qz) {
    font-size: min(calc(100vw / 120), calc(100vh / 63.125));
  }
}

/* ==========================================================================
   SIDEBAR — Frame 101 @40,40 460x930
   ========================================================================== */
.qz-side {
  flex: none;
  width: 28.75rem;                          /* 460 */
  background: var(--qz-chip);
  border-radius: var(--radius);
  padding: 0.75rem;                         /* 12 */
}
.qz-side-card {
  position: relative;
  height: 100%;                             /* 906 */
  background: var(--white);
  border-radius: var(--radius);
}

.qz-side-card > * { position: absolute; }

.qz-logo { left: 2.5rem; top: 2.5rem; width: 7.3125rem; height: 3.75rem; }  /* @92,92 117x60 */
.qz-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }

.qz-strap {                                  /* @233,102 215x40 */
  left: 11.3125rem; top: 3.125rem; width: 13.4375rem;
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;                  /* -0.16 */
  color: var(--ink-70);
}

/* Vector 7 / Vector 8 — 436 long, weight 2, strokeDashes [8,8], and the stroke
   is itself a vertical GRADIENT (transparent -> #e2fa5a @52.4038% -> transparent),
   so the dash is painted through a mask rather than as a flat colour. */
.qz-rule {
  left: 0; width: 27.25rem; height: 0.125rem;
  background: linear-gradient(to bottom,
    rgba(226, 250, 90, 0) 0%, #e2fa5a 52.4038%, rgba(226, 250, 90, 0) 100%);
  -webkit-mask-image: repeating-linear-gradient(to right, #000 0 0.5rem, transparent 0.5rem 1rem);
          mask-image: repeating-linear-gradient(to right, #000 0 0.5rem, transparent 0.5rem 1rem);
}
.qz-rule-top { top: 8.75rem; }               /* @52,192 -> card-rel 0,140 */
/* bottom-anchored for the 100vh shell: 906 - 797 - 2 = 107. It pairs with the
   reviews row below it (Figma's own CENTER/BOTTOM) as the sidebar's footer. */
.qz-rule-bot { top: auto; bottom: 6.6875rem; }   /* card-rel 0,797 at 906 tall */

/* ---- question list — Group 24 @92,232 195x577 -------------------------- */
/* ---- EQUAL gaps between rows (user, 2026-08-06: "steps k darmyan marjin equal
   kro") ------------------------------------------------------------------------
   Figma's own row tops are 0 / 111 / 194 / 277 / 360 / 443 / 526, so its FIRST
   gap is 111 and the other five are 83 — the extra 28 under Question 01 is
   visible the moment more than one row is ticked. The rows are in flow now with
   one pitch instead of seven hardcoded tops.

   The pitch keeps both ends exactly where Figma has them: 526 / 6 = 87.667, so
   row 1 still starts at 0 and row 7 still starts at 526, and the list still
   measures its declared 577 (7 x 51 + 6 x 36.667). The connector's length is
   derived from the same variable, so the line and the gap can never drift
   apart — which is the one way this would actually look broken. */
.qz-qlist {
  --qz-row-pitch: 5.4791667rem;              /* 526/6 = 87.667 */
  left: 2.5rem; top: 11.25rem;               /* card-rel 40,180 */
  width: 12.1875rem; height: 36.0625rem;     /* 195x577 */
  margin: 0; padding: 0; list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--qz-row-pitch) - 3.1875rem); /* pitch - the 51 row = 36.667 */
}
.qz-q { position: relative; flex: none; height: 3.1875rem; }  /* rows are 51 tall */

/* ---- the fade follows the current row -----------------------------------
   User, 2026-08-06: "jis step pe user ho us ko fade na dikha … jo step hogay
   hai or jis pe hai wo fade ni dkhane jo next steps rehte honge wo fade
   dikhane." Done rows and the current row are full strength; only what is still
   ahead fades.

   Figma cannot answer this, because Figma's stepper never advances — it draws
   Q01-done / Q02-current on all nine screens, so its one static white gradient
   (Rectangle 18) never has to cover an active row. Ours does advance, and by
   step 3 the gradient was washing out the row the user is actually on.

   So the mechanism moves from one gradient over the list to per-row opacity,
   re-anchored to the current row. The VALUES are still Figma's own: its
   gradient runs 0 -> 577 over the list, so a row at y sat at 1 - y/577, and the
   row tops 111/194/277/360/443/526 give exactly the six steps below. They are
   simply measured from the current row now instead of from the top. */
.qz-q {
  opacity: 1;
  transition: opacity var(--qz-dur-step) var(--qz-ease-step);
}
/* The sibling chains live inside :where() so they contribute NO specificity.
   Written plainly they run up to (0,8,0) — eight class components — and the row
   hover below, at (0,2,0), silently lost to them: the box would darken but a
   nearly-invisible row would refuse to come forward. With :where() every rule
   here is a flat (0,1,0), they still target different rows so they never fight
   each other, and `:hover` outranks all of them. */
.qz-q:where(.is-current + .qz-q) { opacity: 0.808; }
.qz-q:where(.is-current + .qz-q + .qz-q) { opacity: 0.664; }
.qz-q:where(.is-current + .qz-q + .qz-q + .qz-q) { opacity: 0.52; }
.qz-q:where(.is-current + .qz-q + .qz-q + .qz-q + .qz-q) { opacity: 0.376; }
.qz-q:where(.is-current + .qz-q + .qz-q + .qz-q + .qz-q + .qz-q) { opacity: 0.232; }
.qz-q:where(.is-current + .qz-q + .qz-q + .qz-q + .qz-q + .qz-q + .qz-q) { opacity: 0.088; }
/* Figma's own row tops: 0 / 111 / 194 / 277 / 360 / 443 / 526 */
/* Figma's own row tops were 0 / 111 / 194 / 277 / 360 / 443 / 526 as seven
   hardcoded `top`s here. They are gone: the rows are flex items on one pitch, so
   the gaps are equal by construction and inserting or reordering a row can no
   longer produce a wrong-but-plausible layout. */

/* The stepper animates on the same clock as the step change (user, 2026-08-06:
   "step transition ki trah steps ki b transition smooth kro"), so a row filling
   in reads as part of the same movement instead of snapping under it. Only paint
   properties move — background, the ring, the check's opacity — so no row ever
   changes size and Figma's 51-tall / 40-box geometry is untouched. */
/* Each row is a button now. Absolute at inset 0 so its box IS the row's box and
   the two children below keep their Figma offsets unchanged; UA styles zeroed
   because `box-sizing: border-box` is global and a default button border would
   eat into the 51-tall row. */
.qz-q-btn {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  padding: 0; margin: 0; border: 0; background: none; appearance: none;
  font: inherit; color: inherit; text-align: left;
  cursor: pointer;
}
/* Figma wires nothing on these rows, so the hover is authored — and restrained,
   because now that it IS a click it has to read as one: a faded upcoming row
   comes forward to full strength and the box picks up a touch of ink. Nothing
   changes size. */
.qz-q:hover { opacity: 1; }
.qz-q:hover .qz-qbox { background: var(--ink-08); }
.qz-q.is-done:hover .qz-qbox { background: var(--ink); }   /* done stays black */
.qz-q-btn:focus-visible {
  outline: 0.1875rem solid var(--ink);
  outline-offset: 0.125rem;
  border-radius: var(--radius-sm);
}

.qz-qbox {
  position: absolute; left: 0; top: 0;
  width: 2.5rem; height: 2.5rem;             /* 40x40 */
  border-radius: var(--radius-sm);
  background: var(--qz-chip);
  display: grid; place-items: center;
  transition:
    background-color var(--qz-dur-step) var(--qz-ease-step),
    box-shadow var(--qz-dur-step) var(--qz-ease-step);
}
.qz-qbox img {                               /* the 24x24 check frame */
  width: 1.5rem; height: 1.5rem; display: block;
  opacity: 0;
  transition: opacity var(--qz-dur-step) var(--qz-ease-step);
}

.qz-q.is-done .qz-qbox { background: var(--ink); }
.qz-q.is-done .qz-qbox img { opacity: 1; }
.qz-q.is-current .qz-qbox { box-shadow: inset 0 0 0 0.0625rem var(--ink); }   /* #111 weight 1 */

/* Vector 8 — the 2px ink connector that links a completed row to the next one.
   Figma draws it only for Q01 -> Q02; with a live stepper it follows the run of
   completed rows. Length is the gap between this box's bottom and the next top. */
/* Always rendered, and DRAWN DOWNWARD with scaleY when the row completes — a
   `content` swap cannot transition, so the pseudo has to exist on every row and
   animate instead of appearing. `translateX(-50%)` is preserved and the scale
   composes with it; origin `top` is what makes the line grow from the box it
   leaves rather than from the one it arrives at. */
.qz-q:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 1.25rem;                             /* the box's centre */
  top: 2.5rem;
  width: 0.125rem;
  /* box bottom -> the next box's top, off the SAME pitch as the flex gap, so one
     value governs both. Figma's own two lengths (71 under row 1, 43 elsewhere)
     were the uneven-gap artefact and are gone with it. */
  height: calc(var(--qz-row-pitch) - 2.5rem);  /* pitch - the 40 box = 47.667 */
  background: var(--ink);
  transform: translateX(-50%) scaleY(0);
  transform-origin: top;
  transition: transform var(--qz-dur-step) var(--qz-ease-step);
}
.qz-q.is-done:not(:last-child)::after { transform: translateX(-50%) scaleY(1); }

.qz-qmeta { position: absolute; left: 3.25rem; top: 0; }   /* x52 */
.qz-qnum {
  display: block;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;
  color: var(--ink-70);
}
.qz-qname {
  display: block;
  margin-top: 0.25rem;                       /* 24 - 20 */
  font-family: var(--font-display);
  font-weight: 600;
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1.6625rem;                    /* 26.6 */
  letter-spacing: 0;
  /* the canonical dark->lime paint; the angle/end-stop are per text-box width */
  background-image: linear-gradient(84.3deg, #111111 70.6%, #e2fa5a 135.7%);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  width: max-content;
}
.qz-q:nth-child(2) .qz-qname { background-image: linear-gradient(81.74deg, #111111 70.61%, #e2fa5a 134.11%); }
.qz-q:nth-child(3) .qz-qname { background-image: linear-gradient(84.93deg, #111111 70.59%, #e2fa5a 136.01%); }
.qz-q:nth-child(4) .qz-qname { background-image: linear-gradient(82.03deg, #111111 70.6%,  #e2fa5a 134.31%); }
.qz-q:nth-child(5) .qz-qname { background-image: linear-gradient(86.76deg, #111111 70.59%, #e2fa5a 136.72%); }
.qz-q:nth-child(6) .qz-qname { background-image: linear-gradient(86.81deg, #111111 70.59%, #e2fa5a 136.73%); }
.qz-q:nth-child(7) .qz-qname { background-image: linear-gradient(83.84deg, #111111 70.6%,  #e2fa5a 135.45%); }

/* Rectangle 18 — Figma's white fade over the list. SUPERSEDED as of 2026-08-06
   by the per-row opacity above, which carries the same ramp but re-anchored to
   the current row; leaving both on would compound them. The element stays in the
   markup rather than being deleted so restoring Figma's static version is
   deleting `display: none` — the same reasoning as the quickbar's hidden spans. */
.qz-qfade {
  display: none;
  left: 2.5rem; top: 11.25rem;
  width: 22.5rem; height: 36.0625rem;        /* 360x577 */
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #ffffff 100%);
  pointer-events: none;
}

/* ---- trustpilot row — Frame 12 @85,891 370x24 -------------------------- */
.qz-side-bot {
  /* Frame 12 is constraints=CENTER/BOTTOM, so this is bottom-anchored, not
     placed at Figma's y839: 906 - 839 - 24 = 43. */
  left: 2.0625rem; top: auto; bottom: 2.6875rem;   /* card-rel 33,839 at 906 tall */
  width: 23.125rem; height: 1.5rem;          /* 370x24 */
  display: flex; align-items: center; gap: 0.75rem;
}
.qz-rev {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1.25rem;
  line-height: 1.525rem;                     /* 24.4 */
  letter-spacing: -0.01rem;
  color: var(--ink);
}
.qz-rev b { font-weight: 600; font-style: italic; }   /* InstrumentSans-SemiBoldItalic */

/* image 110 is a 1600x680 source shown in a 94x24 box under Figma's STRETCH
   transform [[0.5791,0,0.2121],[0,0.3428,0.2133]]. These percentages are the
   homepage's own .tp-logo crop, copied (never edited) because home.css is not
   linked on this route. */
.qz-tp {
  position: relative; display: block;
  width: 5.875rem; height: 1.5rem;
  overflow: hidden;
  flex: none;
}
.qz-tp img {
  position: absolute;
  left: -36.63%; top: -62.21%;
  width: 172.68%; height: 291.68%;
  max-width: none;
}
/* The 125x24 stars are the 428 frame's addition — desktop's Frame 15 is 94 wide
   and holds the wordmark alone.  Kept in the markup and hidden here so the
   mobile block only has to turn it back on.  See the MOBILE section. */
.qz-tp-stars { display: none; }

/* ==========================================================================
   MAIN PANE — Frame 102 @540,40 1340x930
   ========================================================================== */
/* `overflow: clip` is Figma's own — Frame 102 carries clipsContent — and it is
   also load-bearing now that the steps slide: an upcoming step waits 3rem to the
   RIGHT of the pane, which without this pushes the document ~48px wide, raises a
   horizontal scrollbar, and that scrollbar then steals 15px of client height and
   raises a vertical one too. `clip` and not `hidden` so this never becomes a
   scroll container. Nothing in the design paints outside the pane: the search
   card's 40px shadow sits at y395-535 inside a 930 box, and the dots are flush
   with the bottom edge. */
.qz-main { position: relative; flex: 1 1 auto; min-width: 0; overflow: clip; }

/* --- back link — @540,40 87x40 ------------------------------------------ */
.qz-back {
  position: absolute; left: 0; top: 0;
  display: inline-flex; align-items: center; gap: 0.625rem;
  height: 2.5rem;
  padding: 0; margin: 0; border: 0; background: none; appearance: none;
  font: inherit; cursor: pointer;
  border-radius: var(--radius);
}
.qz-back-chip {
  flex: none;
  width: 2.5rem; height: 2.5rem;             /* 40x40 */
  border-radius: var(--radius-sm);
  background: var(--qz-chip);
  display: grid; place-items: center;
}
/* Arrow Icon Wrapper carries rotation = PI, so this is the shared arrow.svg flipped. */
.qz-back-chip img { width: 1.75rem; height: 1.75rem; display: block; transform: scaleX(-1); }
.qz-back-label {
  font-family: var(--font-ui);
  font-weight: 500;                          /* Geist-Medium */
  font-size: 1rem;
  line-height: 1.3rem;                       /* 20.8 */
  letter-spacing: -0.01rem;                  /* -0.16 */
  color: var(--ink);
}

/* --- help line — @1482,40, right-aligned to the pane edge ---------------- */
.qz-help {
  position: absolute; right: 0; top: 0; margin: 0;
  font-family: var(--font-ui);
  font-weight: 400;                          /* Geist-Regular */
  font-size: 1rem;
  line-height: 1.3rem;                       /* 20.8 */
  letter-spacing: -0.01rem;
  color: var(--ink-70);
  white-space: pre;                          /* Figma's runs carry their own spacing */
}
/* The lime chat FAB belongs to the 428 frame only — the desktop screen has the
   `.qz-help` line instead and draws no FAB.  See the MOBILE section. */
.qz-fab { display: none; }

/* copy that differs between the 1920 and 428 frames — see the MOBILE section */
.qz-m-only { display: none; }

/* styleOverrideTable makes every link Geist-Medium 500, UNDERLINE, full-ink */
.qz-help a, .qz-cant a {
  color: var(--ink);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 0.125rem;
}

/* --- stage -------------------------------------------------------------- */
/* Steps CROSSFADE AND SLIDE (user, 2026-08-06: "1st step float left kre gi or
   fade out and 2nd step content coming from right with fade in"). Figma's own
   transition on every wired node is DISSOLVE / EASE_OUT / 300ms, so the duration
   and the fade are Figma's; only the directional travel is authored.

   Two things make this work without a line of JS or any new state:

   1. `position: absolute; inset: 0` — NOT `display: none`, which cannot
      transition. It also has to be absolute for a second reason: `translate` on
      an element makes it the containing block for its absolutely-positioned
      descendants, and the children here are absolute. Sized to `inset: 0` the
      step's box is identical to .qz-main's, so every child's `left: 50%` /
      `top: …` / `bottom: …` resolves to exactly the same place as before.
   2. The direction comes from a sibling test, so it is correct going forward AND
      back. A step that has the active step somewhere AFTER it has been passed, so
      it sits to the LEFT; everything else is still upcoming and waits on the
      RIGHT. Advancing therefore floats the old step out left while the new one
      arrives from the right; pressing back mirrors it for free.

   `visibility` + `transition-behavior: allow-discrete` is what keeps the leaving
   step painted for the whole 300ms (and out of the a11y tree and tab order once
   it is gone) — the same mechanism FaqAccordion uses on the homepage. */
.qz-step {
  position: absolute;
  inset: 0;
  opacity: 0;
  translate: 3rem 0;                         /* upcoming: waiting to the right */
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--qz-dur-step) var(--qz-ease-step),
    translate var(--qz-dur-step) var(--qz-ease-step),
    visibility var(--qz-dur-step) var(--qz-ease-step);
  transition-behavior: allow-discrete;
}
/* already passed -> float out to the left. :not(.is-active) is belt-and-braces:
   the active step can never match `~ .is-active` anyway, but :has() takes its
   argument's specificity, so this selector outranks .qz-step.is-active. */
.qz-step:not(.is-active):has(~ .qz-step.is-active) {
  translate: -3rem 0;
}
.qz-step.is-active {
  opacity: 1;
  translate: 0 0;
  visibility: visible;
  /* The step's own box fills the WHOLE pane (inset: 0), so it lies on top of
     .qz-back and .qz-help, which are earlier siblings. Left as `auto` it swallows
     their clicks and hovers — the back button stops working and the top-right
     links stop reacting, with nothing wrong in either of them. So the box itself
     stays transparent to the pointer and only its real content takes events. */
  pointer-events: none;
}
.qz-step.is-active > * { pointer-events: auto; }
.qz-step > * { position: absolute; left: 50%; transform: translateX(-50%); }

/* headings — HostGrotesk-SemiBold 40/48 ls -1.28, centred.
   Each step puts its heading at its own y; 191 is the common one. */
.qz-h {
  margin: 0;
  top: 11.9375rem;                           /* 191 — D03 D05 D06 D07 D09 */
  width: 28.25rem;                           /* 452 */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 2.5rem;
  line-height: 3rem;
  letter-spacing: -0.08rem;                  /* -1.28 */
  text-align: center;
  color: var(--ink);
}
.qz-h-narrow { width: 26rem; }               /* 416 — D03 / D04 */
/* D01's heading hangs off the centred search card: 48 gap + the card's own 30
   half-height = 78 above the pane's midline. At 930 that is bottom 543, i.e.
   top 291 — Figma's value. (The other steps stay top-anchored for now.) */
.qz-h-address { top: auto; bottom: calc(50% + 4.875rem); }   /* 291 — D01 */
.qz-h-map     { top: 9.5rem; }               /* 152 — D02 */
.qz-h-floors  { top: 10.4375rem; }           /* 167 — D04 */
.qz-h-kwh     { top: 18.375rem; }            /* 294 — D08 */

/* helper copy, bottom-anchored so its baseline block ends at pane-rel 890 —
   which under the 100vh shell means a real `bottom`, not Figma's y850. Doing it
   this way also makes .qz-helper-3 redundant: a 3-line variant grows upward from
   the same bottom edge and lands on Figma's own 830 by itself. */
.qz-helper {
  margin: 0;
  width: 28.25rem;
  top: auto; bottom: 2.5rem;                 /* 930 - 890 = 40 */
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;
  text-align: center;
  color: var(--ink-70);
}
.qz-helper-narrow { width: 26rem; }
/* .qz-helper-3 kept as a marker only — the bottom anchor above already places
   the 3-line variant on Figma's 830. Setting a `top` here now would fight the
   `bottom` and stretch the box instead of moving it. */
.qz-helper-3 { top: auto; }                  /* 830 — the 3-line variants */

.qz-cant {
  margin: 0;
  /* bottom-anchored for the 100vh shell: 930 - 869 - 21 = 40 */
  top: auto; bottom: 2.5rem;                 /* pane-rel y869 at 930 tall */
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.3rem;                       /* 20.8 */
  letter-spacing: -0.01rem;
  color: var(--ink-70);
  /* Figma's string is "can’t find:   Start Chat   or   Call: 000 000 000" —
     three spaces around each link. HTML would collapse them. */
  white-space: pre;
}

/* --- progress dots — @…,962, always centred on the pane -----------------
   Figma lights EVERY dot up to and including the current step: 40 wide at
   #111 @.6, upcoming 20 wide at #111 @.08, gap 4, height 8, r10.            */
.qz-dots {
  /* flush with the pane's bottom edge in Figma (922 + 8 = 930), so bottom: 0
     is both the literal value and what the 100vh shell needs. */
  position: absolute; top: auto; bottom: 0;  /* pane-rel y922 at 930 tall */
  left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 0.25rem;
}
.qz-dots span {
  display: block;
  width: 1.25rem; height: 0.5rem;            /* 20x8 */
  border-radius: 0.625rem;
  background: rgba(17, 17, 17, 0.08);
}
.qz-dots span.on { width: 2.5rem; background: rgba(17, 17, 17, 0.6); }

/* ==========================================================================
   OPTION CARDS  (the "radio buttons")
   ========================================================================== */
.qz-grid {
  display: flex;
  gap: 1rem;                                 /* itemSpacing 16 */
  top: 20.9375rem;                           /* 335 */
}
.qz-grid-2 { top: 19.4375rem; }              /* 311 — D04 row 1 */
.qz-grid-2b { top: 29.5625rem; }             /* 473 — D04 row 2 */

.qz-opt {
  position: relative;
  flex: none;
  width: 17.0625rem; height: 17.0625rem;     /* 273x273 */
  padding: 0; margin: 0; border: 0; appearance: none; font: inherit; cursor: pointer;
  background: var(--white);
  border-radius: var(--radius);
  overflow: hidden;                          /* clipsContent — the photo is full-bleed */
  text-align: left;
}
/* Figma's 2px stroke is INSIDE-aligned, but children still sit at the frame's own
   origin. A real CSS `border` would move every absolutely-positioned child 2px in
   (they resolve against the padding box), so the stroke is painted as an overlay
   instead — the pill lands at exactly 20,20 and the product shot at 77,88. */
.qz-opt::after {
  content: "";
  position: absolute; inset: 0;
  border: 0.125rem solid var(--qz-card-line);
  border-radius: inherit;
  pointer-events: none;
}
.qz-opt-sm { width: 17rem; height: 9.125rem; }        /* 272x146 — D04 */
.qz-opt-w1 { width: 19.8125rem; }                     /* 317 — D09 card 1 */
.qz-opt-w2 { width: 23.5rem; }                        /* 376 — D09 card 2 */

/* The full-bleed lime wash every option card carries.
   GRADIENT_LINEAR handles [[0.5,0],[0.5,1]] -> straight down. Stops are
   #e2fa5a alpha 1 @0 -> #e2fa5a alpha 0 @1, and the PAINT itself carries
   `opacity: 0.2` — three separate opacities, exactly the trap CLAUDE.md flags.
   The paint opacity multiplies the stop alpha, so the top is 0.2, NOT 1: at
   full alpha the wash is 5x too strong and swamps the photo underneath, which
   is what it was doing until 2026-08-06. */
.qz-opt-tint {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(226, 250, 90, 0.2) 0%, rgba(226, 250, 90, 0) 100%);
}
.qz-opt-photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* D03's house shot is the one photo Figma offsets: RECTANGLE 7064:4125 sits at
   rel(0,27) at its full 273x273 inside a 273-tall card, so it is pushed down 27
   and its bottom 27 is clipped away. The flat shot (7064:4118) is flush at 0. */
.qz-opt-photo-low {
  top: 1.6875rem;                            /* 27 */
  bottom: auto;
  height: 17.0625rem;                        /* 273 — unchanged, so 27 is clipped */
}

/* the pill — @20,20, h46, r12 */
.qz-opt-pill {
  position: absolute; left: 1.25rem; right: 1.25rem; top: 1.25rem;
  height: 2.875rem;                          /* 46 */
  background: var(--white);
  border-radius: var(--radius);
  display: flex; align-items: center;
  padding-left: 0.625rem;                    /* chip inset 10 */
  gap: 0.5rem;                               /* 42 - 10 - 24 */
}
.qz-opt-chip {
  flex: none;
  width: 1.5rem; height: 1.5rem;             /* 24x24 */
  border-radius: var(--radius-sm);
  background: var(--qz-chip);
  display: grid; place-items: center;
}
.qz-opt-chip img { width: 1.25rem; height: 1.25rem; display: block; opacity: 0; }
.qz-opt-label {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 1.25rem;
  line-height: 1.625rem;                     /* 26 */
  letter-spacing: -0.01rem;                  /* -0.16 */
  color: var(--ink);
}

/* --- card body artwork --------------------------------------------------- */
.qz-opt-art { position: absolute; left: 1.25rem; top: 5.5rem; width: 14.5625rem; height: 10.3125rem; }  /* @20,88 233x165 */
.qz-opt-art img { width: 100%; height: 100%; display: block; }

.qz-opt-dot {                                /* D06 — Ellipse 25, 120x120 @77,110 */
  position: absolute; left: 50%; transform: translateX(-50%);
  top: 6.875rem;
  width: 7.5rem; height: 7.5rem;
  border-radius: 50%;
}
.qz-opt-dot-no { background: #ff5630; }
.qz-opt-dot-yes { background: #00ab55; }

.qz-opt-beds {                               /* D04 — 40x40 bed icons @y86, centred, pitch 52 */
  position: absolute; left: 50%; transform: translateX(-50%);
  top: 5.375rem;
  display: flex; align-items: center; gap: 0.75rem;   /* 52 - 40 */
  color: #7f7f7a;                            /* the icon ships as currentColor */
}
.qz-opt-beds img { width: 2.5rem; height: 2.5rem; display: block; }
.qz-opt-plus {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 1.5rem;
  line-height: 1;
  color: currentColor;
}

.qz-opt-prod {                               /* D09 — 165x165 product shots @y88 */
  position: absolute; top: 5.5rem;
  width: 10.3125rem; height: 10.3125rem;
  object-fit: contain;
}
.qz-opt-prod-1 { left: 4.8125rem; }          /* 77 */
.qz-opt-prod-a { left: 1.25rem; }            /* 20 */
.qz-opt-prod-b { left: 11.9375rem; }         /* 191 */

/* ==========================================================================
   STEP 1 — address search
   ========================================================================== */
/* Frame 113 is the ONE node on this screen that Figma pins CENTER/CENTER, and
   its box confirms it: 435 + 60/2 = 465 = 930/2 exactly. So under the 100vh
   shell the search card is the anchor, and the heading and the CTA hang off it
   at Figma's own 48 gaps (see .qz-h-address / .qz-cta-address). At a 1010-tall
   window all three land on 291 / 435 / 543 — Figma's numbers to the pixel. */
.qz-search {
  top: 50%;                                  /* 435 + 30 = 465 = 930/2 */
  transform: translate(-50%, -50%);           /* .qz-step > * only sets translateX */
  width: 28.25rem; height: 3.75rem;          /* 452x60 */
  background: var(--white);
  border-radius: var(--radius);
  /* DROP_SHADOW #000000 a0.08 radius=40 offset=0,0 — the only real shadow on
     this screen (the logo's is white-on-white and paints nothing). */
  box-shadow: 0 0 2.5rem rgba(0, 0, 0, 0.08);
  display: flex; align-items: center;
  padding: 0 1.25rem;
}
.qz-search input {
  flex: 1 1 auto; min-width: 0;
  border: 0; background: none; outline: none; appearance: none;
  font-family: var(--font-sans);             /* InstrumentSans-Regular */
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;
  color: var(--ink);
}
.qz-search input::placeholder { color: var(--ink-70); }
.qz-search img { flex: none; width: 2rem; height: 2rem; display: block; }

/* ==========================================================================
   STEP 2 — pin your roof
   ========================================================================== */
.qz-map-sub {
  top: 13.5rem;                              /* 216 */
  margin: 0;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.3125rem;
  color: var(--ink-70);
  white-space: nowrap;
}
.qz-map {
  top: 17.8125rem;                           /* 285 */
  width: 51.25rem; height: 22.5rem;          /* 820x360 */
  background: var(--white);
  border-radius: var(--radius);
  overflow: hidden;
}
/* image 143 sits 1160x478 at pane-rel 142,209 — i.e. it overhangs the card and is clipped */
.qz-map img {
  position: absolute; left: -7.375rem; top: -4.75rem;
  width: 72.5rem; height: 29.875rem;
  max-width: none;
  display: block;
  object-fit: cover;
}

/* ==========================================================================
   STEP 8 — kWh slider  (Group 25 @848,478 725x54, pane-rel 308,438)
   ========================================================================== */
.qz-slider { top: 27.375rem; width: 45.3125rem; height: 3.375rem; }
.qz-slider-cap {
  position: absolute; top: 0;
  height: 2rem;                              /* 32 */
  display: grid; place-items: center;
  padding: 0 0.5rem;
  background: var(--white);
  border: 0.125rem solid var(--qz-chip);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;
  color: var(--ink-70);
  white-space: nowrap;
}
.qz-slider-cap-min { left: 0; width: 7.4375rem; }     /* 119 */
.qz-slider-cap-max { right: 0; width: 8.625rem; }     /* 138 */

.qz-slider-track {
  position: absolute; left: 7.9375rem; top: 0.75rem;  /* 975-848=127, 490-478=12 */
  width: 28.25rem; height: 0.5rem;                    /* 452x8 */
}
.qz-slider-track input[type="range"] {
  -webkit-appearance: none; appearance: none;
  position: absolute; left: 0; top: -0.5rem;
  width: 100%; height: 1.5rem;
  margin: 0; background: none; outline: none; cursor: pointer;
}
.qz-slider-rail {
  position: absolute; inset: 0;
  border-radius: 0.25rem;
  background: rgba(17, 17, 17, 0.12);
  overflow: hidden;
}
.qz-slider-fill {
  position: absolute; left: 0; top: 0; height: 100%;
  border-radius: 0.25rem;
  background: linear-gradient(to right, #e2fa5a 0%, #869435 100%);
}
.qz-slider-track input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 1.5rem; height: 1.5rem;             /* 24x24 */
  border-radius: var(--radius-sm);
  background: var(--white);
  border: 0.1875rem solid var(--ink);        /* weight 3 */
  cursor: pointer;
}
.qz-slider-track input[type="range"]::-moz-range-thumb {
  width: 1.5rem; height: 1.5rem;
  border-radius: var(--radius-sm);
  background: var(--white);
  border: 0.1875rem solid var(--ink);
  cursor: pointer;
}
.qz-slider-tick {
  position: absolute; top: 2.125rem;         /* 512 - 478 */
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;                      /* 19.52 */
  letter-spacing: -0.01rem;
  color: var(--ink-70);
  white-space: nowrap;
}
.qz-slider-tick-a { left: 12.125rem; }       /* 1042 - 848 = 194 */
.qz-slider-tick-b { left: 26.4375rem; }      /* 1271 - 848 = 423 */

/* ==========================================================================
   THE "NEXT" / "SHOW MY PRICES" CTA
   Figma's resting fill is #111 at opacity .08 with an ink label — a disabled
   look — and the hover overlay swaps it to solid #111 with a white label.
   ========================================================================== */
.qz-cta {
  top: 33.9375rem;                           /* 543 — D01 */
  display: inline-flex; align-items: center; gap: 0.624375rem;   /* itemSpacing 9.99 */
  height: 3.25rem;                           /* 52 */
  padding: 0.625rem 0.625rem 0.625rem 1rem;  /* 10/10/10/16 */
  border: 0; appearance: none; cursor: pointer;
  background: var(--qz-ghost);
  border-radius: var(--radius);
  font: inherit;
}
.qz-cta-map { top: 43.3125rem; }             /* 693 — D02 */
.qz-cta-kwh { top: 33.9375rem; }
/* D01 only: mirrors .qz-h-address off the centred search card — 30 + 48 = 78
   below the midline, which is 543 at 930. Scoped to step 1 on purpose; steps
   2-9 keep the plain top until they are done. */
.qz-cta-address { top: calc(50% + 4.875rem); }   /* 543 — D01 */
.qz-cta-label {
  font-family: var(--font-ui);
  font-weight: 600;                          /* Geist-SemiBold */
  font-size: 1rem;                           /* 16, NOT 20 — "Next" measures 36 wide */
  line-height: 1.6rem;                       /* 25.6 */
  letter-spacing: -0.01rem;
  color: var(--ink);
}
.qz-cta-chip {
  flex: none;
  width: 2rem; height: 2rem;                 /* 32x32 */
  border-radius: var(--radius-sm);
  background: var(--accent);
  display: grid; place-items: center;
}
.qz-cta-chip img { width: 1.75rem; height: 1.75rem; display: block; }

/* ==========================================================================
   INTERACTION LAYER
   Figma authors exactly three hover recipes (26 ON_HOVER overlays,
   all DISSOLVE / EASE_OUT / 300ms). Nothing below changes the box model.
   ========================================================================== */

/* --- Recipe A: the option card ------------------------------------------ */
.qz-opt::after,
.qz-opt-pill,
.qz-opt-chip,
.qz-opt-chip img,
.qz-opt-beds {
  transition:
    border-color var(--dur-hover) var(--ease-premium),
    background-color var(--dur-hover) var(--ease-premium),
    color var(--dur-hover) var(--ease-premium),
    opacity var(--dur-hover) var(--ease-premium);
}
/* HOVER ONLY — `.is-selected` was sharing these rules, which left the last card
   you picked permanently lit when you came back to the step (user, 2026-08-06:
   "hovered na rkho hover krne pe hover state dikhao"). Dropping it is also the
   more Figma-faithful reading: the file models this state with 15 ON_HOVER
   overlay frames and defines NO persistent selected state anywhere.

   The class and `aria-pressed` both stay in the markup, so the selection is
   still exposed to assistive tech and restoring the visual is re-adding
   `.qz-opt.is-selected` to these five selectors. */
.qz-opt:hover::after { border-color: var(--qz-card-line-h); }
.qz-opt:hover .qz-opt-pill { background: var(--accent); }
.qz-opt:hover .qz-opt-chip { background: var(--ink); }
.qz-opt:hover .qz-opt-chip img { opacity: 1; }
.qz-opt:hover .qz-opt-beds { color: var(--ink); }

/* ...and on top of Figma's recipe, the homepage's own card vocabulary (user,
   2026-08-06: "quote k pages pe buttons pe hover animation effects dalo like
   home page"): a translateY lift, one tight low shadow, and the photo zooming
   at --dur-zoom rather than --dur-hover — 220ms on a photo reads as a twitch.
   `transform` is free on this element: the grid is the `.qz-step > *` that owns
   translateX(-50%), not the card, and no option card is a motion reveal target.
   The zoom needs a clip box and the card already has Figma's own
   `overflow: hidden` (clipsContent). Line art (.qz-opt-art, .qz-opt-beds) is
   deliberately left un-zoomed — it is 2px stroke work and scaling it only
   softens it. */
.qz-opt { transition:
    transform var(--dur-hover) var(--ease-premium),
    box-shadow var(--dur-hover) var(--ease-premium); }
.qz-opt-photo, .qz-opt-prod { transition: transform var(--dur-zoom) var(--ease-premium); }
.qz-opt:hover {
  transform: translateY(-0.25rem);
  box-shadow: 0 1rem 2rem -1rem rgba(17, 17, 17, 0.28);
}
.qz-opt:hover .qz-opt-photo,
.qz-opt:hover .qz-opt-prod { transform: scale(1.06); }
.qz-opt:active {
  transform: translateY(0);
  box-shadow: 0 0.25rem 0.625rem -0.375rem rgba(17, 17, 17, 0.32);
  transition-duration: var(--dur-press);
}

.qz-opt:focus-visible {
  outline: 0.1875rem solid var(--ink);
  outline-offset: 0.125rem;
}

/* --- Recipe B: the CTA, plus the full homepage .btn treatment -----------
   Figma's own half is the fill swap: #111 @.08 with an ink label (a disabled
   look) -> solid #111 with a white one. Everything else here is base.css's
   `.btn` / `.btn-dark` recipe, copied rather than re-invented per CLAUDE.md
   rule 3 — the lift, one tight ink shadow with a white inner hairline, the
   1000ms specular pass, the arrow chip leading by 3px, and an :active that
   settles the lift at --dur-press.

   THE LIFT USES `translate`, NOT `transform`, and it has to: `.qz-step > *`
   already puts translateX(-50%) on this element (and .qz-cta-address stacks a
   second transform on top), so a hover `transform` would throw the button off
   centre by half its own width. The reveal layer also drives `translate` here
   (motion-spec.json, variant `btn`) but only while its animation runs —
   `animation-fill-mode: backwards` means it stops contributing once finished,
   after which this declaration is what applies. */
.qz-cta {
  overflow: hidden;                          /* clips the sheen to the r12 pill */
  isolation: isolate;                        /* keeps the z-index:-1 sheen inside */
  transition:
    background-color var(--dur-hover) var(--ease-premium),
    box-shadow var(--dur-hover) var(--ease-premium),
    translate var(--dur-hover) var(--ease-premium);
}
/* the band and @keyframes btn-sheen both live in base.css, which is global here,
   so this is the same light source as every homepage pill */
.qz-cta::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0; bottom: 0; left: -55%;
  width: 46%;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(100deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.1) 30%,
    rgba(255, 255, 255, 0.38) 50%,
    rgba(255, 255, 255, 0.1) 70%,
    rgba(255, 255, 255, 0) 100%);
  transform: skewX(-18deg);
}
.qz-cta:hover::before { animation: btn-sheen var(--dur-sheen) var(--ease-sheen) 80ms; }
.qz-cta-label { transition: color var(--dur-hover) var(--ease-premium); }
.qz-cta-chip { transition: box-shadow var(--dur-hover) var(--ease-premium); }
.qz-cta-chip img { transition: transform var(--dur-hover) var(--ease-premium); }
.qz-cta:hover {
  background: var(--ink);
  translate: 0 -0.125rem;
  box-shadow:
    0 0.875rem 1.875rem -0.875rem rgba(17, 17, 17, 0.7),
    inset 0 0 0 0.0625rem rgba(255, 255, 255, 0.08);
}
.qz-cta:hover .qz-cta-label { color: var(--white); }
.qz-cta:hover .qz-cta-chip { box-shadow: 0 0 0 0.1875rem rgba(226, 250, 90, 0.3); }
.qz-cta:hover .qz-cta-chip img { transform: translateX(0.1875rem); }
.qz-cta:active {
  translate: 0 0;
  box-shadow:
    0 0.1875rem 0.5rem -0.3125rem rgba(17, 17, 17, 0.7),
    inset 0 0 0 0.0625rem rgba(255, 255, 255, 0.08);
  transition-duration: var(--dur-press);
}
.qz-cta:active .qz-cta-chip img { transform: translateX(0.0625rem); }
.qz-cta:focus-visible { outline: 0.1875rem solid var(--ink); outline-offset: 0.125rem; }

/* --- STEP 1's address field ---------------------------------------------
   User, 2026-08-06: "quote k first step pe input ka hover effect dalo
   professional". Figma authors nothing here, so this is the homepage's
   depth-and-ring language applied to a field rather than a button — restrained
   on purpose: no colour flip, no movement of the text, nothing that reads as
   "click me" on something you type into.

     rest   Figma's own DROP_SHADOW #000 a.08 radius 40
     hover  the shadow deepens and gains a 1px ink hairline, the card lifts 2px,
            the 32px magnifier grows 6%
     focus  the hairline becomes the brand's 2px lime ring — the same lime the
            homepage uses for :focus-visible on dark grounds — and the elevation
            goes one step further again

   `translate`, not `transform`: this element already carries
   translate(-50%,-50%) for Figma's CENTER/CENTER pin. Same reveal caveat as
   .qz-cta above. The whole card is `cursor: text` and is a <label>, so the box
   and the state it shows are the same target. */
.qz-search {
  cursor: text;
  transition:
    box-shadow var(--dur-hover) var(--ease-premium),
    translate var(--dur-hover) var(--ease-premium);
}
.qz-search img { transition: transform var(--dur-hover) var(--ease-premium); }
.qz-search:hover {
  translate: 0 -0.125rem;
  box-shadow:
    0 0.75rem 2.5rem rgba(0, 0, 0, 0.12),
    inset 0 0 0 0.0625rem rgba(17, 17, 17, 0.1);
}
.qz-search:hover img { transform: scale(1.06); }
/* focus wins over hover — it is declared later and both are (0,2,0) */
.qz-search:focus-within {
  translate: 0 -0.125rem;
  box-shadow:
    0 0.875rem 2.75rem rgba(0, 0, 0, 0.14),
    inset 0 0 0 0.125rem var(--accent);
}
.qz-search:focus-within img { transform: scale(1.06); }

/* --- the kWh slider thumb (step 8) -------------------------------------- */
.qz-slider-track input[type="range"]::-webkit-slider-thumb {
  transition: box-shadow var(--dur-hover) var(--ease-premium);
}
.qz-slider-track input[type="range"]:hover::-webkit-slider-thumb {
  box-shadow: 0 0 0 0.25rem rgba(226, 250, 90, 0.35);
}
.qz-slider-track input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 0.25rem rgba(226, 250, 90, 0.6);
}
.qz-slider-track input[type="range"]:hover::-moz-range-thumb {
  box-shadow: 0 0 0 0.25rem rgba(226, 250, 90, 0.35);
}
.qz-slider-track input[type="range"]:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 0.25rem rgba(226, 250, 90, 0.6);
}

/* --- back link + text links (not authored in Figma; homepage vocabulary) -
   No lift on the link itself — `translate` belongs to its `drop` reveal and a
   back link is a text link with a chip, not a pill. The chip carries the whole
   response: it takes the ink wash, one low shadow, and the arrow leads the way
   it travels (the asset is scaleX(-1)'d, so the nudge has to restate the flip
   or the arrow jumps back to pointing right). */
.qz-back-chip {
  transition:
    background-color var(--dur-hover) var(--ease-premium),
    box-shadow var(--dur-hover) var(--ease-premium);
}
.qz-back-chip img { transition: transform var(--dur-hover) var(--ease-premium); }
.qz-back:hover .qz-back-chip {
  background: var(--ink-08);
  box-shadow: 0 0.375rem 0.875rem -0.5rem rgba(17, 17, 17, 0.35);
}
.qz-back:hover .qz-back-chip img { transform: scaleX(-1) translateX(-0.1875rem); }
.qz-back:active .qz-back-chip { box-shadow: none; transition-duration: var(--dur-press); }
.qz-back:focus-visible { outline: 0.1875rem solid var(--ink); outline-offset: 0.125rem; }

/* The top-right help links and the "can't find" pair get the homepage's own
   text-link recipe verbatim (.pv-how a in home-sections.css): the rule they
   already have thickens 1 -> 2 and drops 2 -> 4 away from the baseline. Copied
   rather than re-invented per CLAUDE.md's rule 3, and the offset half is what
   makes it actually readable — thickness alone is nearly invisible at 16px.
   No lift, no colour change: these are text links, not buttons. */
.qz-help a, .qz-cant a {
  text-decoration-thickness: 0.0625rem;
  text-underline-offset: 0.125rem;
  transition:
    text-decoration-thickness var(--dur-hover) var(--ease-premium),
    text-underline-offset var(--dur-hover) var(--ease-premium);
}
.qz-help a:hover, .qz-cant a:hover {
  text-decoration-thickness: 0.125rem;
  text-underline-offset: 0.25rem;
}
.qz-help a:focus-visible, .qz-cant a:focus-visible {
  outline: 0.125rem solid var(--ink);
  outline-offset: 0.1875rem;
  border-radius: 0.1875rem;
}

@media (prefers-reduced-motion: reduce) {
  .qz-opt::after, .qz-opt-pill, .qz-opt-chip, .qz-opt-chip img, .qz-opt-beds,
  .qz-opt, .qz-opt-photo, .qz-opt-prod,
  .qz-cta, .qz-cta-label, .qz-cta-chip, .qz-cta-chip img,
  .qz-search, .qz-search img,
  .qz-back-chip, .qz-back-chip img {
    transition-duration: 0.01ms;
  }
  /* keep every colour and state change; drop the movement, the zoom and the light */
  .qz-opt:hover, .qz-opt:active { transform: none; }
  .qz-opt:hover .qz-opt-photo, .qz-opt:hover .qz-opt-prod { transform: none; }
  .qz-cta:hover, .qz-cta:active { translate: none; }
  .qz-cta:hover .qz-cta-chip img, .qz-cta:active .qz-cta-chip img { transform: none; }
  .qz-cta:hover::before { animation: none; }
  .qz-search:hover, .qz-search:focus-within { translate: none; }
  .qz-search:hover img, .qz-search:focus-within img { transform: none; }
  .qz-back:hover .qz-back-chip img { transform: scaleX(-1); }
  /* Keep the step change as a pure dissolve — which is Figma's own DISSOLVE
     transition anyway — and drop the authored slide. The stepper keeps its
     colour/state feedback and loses only the line-drawing movement. */
  .qz-step,
  .qz-step:not(.is-active):has(~ .qz-step.is-active) { translate: none; }
  .qz-q:not(:last-child)::after { transition-duration: 0.01ms; }
}

/* ==========================================================================
   MOBILE — M01 "Get a qoute" 7064:2548, canvas 428x926
   ==========================================================================
   The 428 frame is a DIFFERENT layout, not a squeezed 1920 one: there is no
   two-pane shell, the sidebar's seven question rows become seven 40x28 pills
   in one horizontal strip under the logo, the back link becomes a pill in the
   top-right, the help line is replaced by a lime chat FAB, and the nine
   progress dots are gone (the pills carry the position instead).

   Everything below is Figma's own frame coordinate.  Nothing is authored.

   SCOPE: step 1 only, per the instruction.  Steps 2-9 keep their desktop
   layout inside this frame and are the next piece of work; .qz-main's own
   `overflow: clip` is what keeps their wider content from widening the page.
   ========================================================================== */
@media (max-width: 767.98px) {
  /* ---- the canvas ------------------------------------------------------
     base.css already scales the root off the 428 canvas below 768
     (calc(100vw / 26.75)), which is what makes 1rem == 1 Figma mobile px.
     This takes the MIN of that and a HEIGHT scale on Figma's own 926 frame,
     exactly as the >=768 rule above does on the 1010 one, so the whole screen
     always fits and never scrolls:
       428x926 -> min(16, 16)        = 16       the design, 1:1
       390x844 -> min(14.58, 14.58)  = 14.58    fits both axes almost exactly
       360x800 -> min(13.46, 13.82)  = 13.46    width-bound; 11 of spare height
                                                above and below, and .qz centres
                                                it on the frame's own --bg
     57.875 = 926 / 16.  `html:has(.qz)` at (0,1,1) outranks base.css's bare
     `html` without editing it, and cannot leak off this route. */
  html:has(.qz) {
    font-size: min(calc(100vw / 26.75), calc(100vh / 57.875));
  }

  /* ---- the frame -------------------------------------------------------
     FRAME "Get a qoute" 7064:2548 — 428x926, #fefff9, clipsContent.  .qz is
     already a centring flex box, so the shell just becomes the frame. */
  .qz-shell {
    position: relative;
    width: 26.75rem;                           /* 428 */
    height: 57.875rem;                         /* 926 */
    padding: 0;
    display: block;
  }

  /* Both panes collapse ONTO the frame's own box, so every element below is
     positioned at its literal Figma frame coordinate rather than against a
     pane that no longer exists.  They overlap, and .qz-main is later in the
     DOM, so it has to be transparent to the pointer or it swallows every
     click on the logo and the pills underneath it — the same trap
     .qz-step.is-active already documents one level down. */
  .qz-side,
  .qz-main {
    position: absolute;
    inset: 0;
    width: auto;
    height: auto;
    pointer-events: none;
  }
  .qz-side { background: none; border-radius: 0; padding: 0; }
  .qz-side-card {
    position: absolute;
    inset: 0;
    height: auto;
    background: none;
    border-radius: 0;
  }
  .qz-side-card > *,
  .qz-main > * { pointer-events: auto; }

  /* ---- not drawn on the 428 frame -------------------------------------
     .qz-strap, both rules, the row meta, the help line and the nine dots
     have no node in M01.  .qz-rev is dropped from Frame 15 too — mobile
     keeps only the Trustpilot lockup. */
  .qz-strap,
  .qz-rule,
  .qz-qmeta,
  .qz-help,
  .qz-dots,
  .qz-rev { display: none; }

  /* ---- logo — image 141 @20,40 117x60 ---------------------------------- */
  .qz-logo { left: 1.25rem; top: 2.5rem; }     /* 20,40 — the size is desktop's */

  /* ---- back — Arrow Icon Wrapper 7064:2551 @325,54 83x32 ---------------
     One #f5f6f0 r8 pill carrying both the arrow and the label, where desktop
     has a bare 40x40 chip beside a loose label.  HUG, pad [2,6,2,12] under a
     180deg rotation, i.e. 6 before the arrow and 12 after the text, gap 0 —
     6 + 28 + 37 + 12 = Figma's 83.  Anchored from the RIGHT so the pill's own
     edge stays on 408 (a 20 inset) while it hugs its text. */
  .qz-back {
    left: auto; right: 1.25rem;                /* 428 - 325 - 83 = 20 */
    top: 3.375rem;                             /* 54 */
    height: 2rem;                              /* 32 */
    gap: 0;
    padding: 0 0.75rem 0 0.375rem;             /* 12 right, 6 left */
    background: var(--qz-chip);
    border-radius: var(--radius-sm);
  }
  /* the chip is gone — the pill carries the ground now, so this is just the
     28x28 arrow box.  The scaleX(-1) flip stays; it is Figma's 180deg. */
  .qz-back-chip {
    width: 1.75rem; height: 1.75rem;           /* 28x28 */
    background: none;
    border-radius: 0;
  }

  /* ---- the stepper — seven 40x28 pills @20,134, pitch 58 ---------------
     Same seven rows, same three states, laid on the other axis: 7064:2576 is
     the done pill (#111111 + the lime check), 7064:2580 the current one
     (#f5f6f0 + a 1px INSIDE #111 stroke) and 2581..2585 are plain #f5f6f0 —
     which is exactly what .qz-q.is-done / .is-current / the bare .qz-qbox
     already paint, so not one state rule changes.

     M01 fades NOTHING: its five upcoming pills are all the same #f5f6f0, so
     the per-row opacity ramp is switched off here.  (It exists because
     desktop's Rectangle 18 gradient had to follow the current row.) */
  .qz-qlist {
    --qz-row-pitch: 3.625rem;                  /* 58 — 40 pill + 18 gap */
    left: 1.25rem; top: 8.375rem;              /* 20,134 */
    width: 24.25rem; height: 1.75rem;          /* 388x28 */
    flex-direction: row;
    gap: calc(var(--qz-row-pitch) - 2.5rem);   /* pitch - the 40 pill = 18 */
  }
  .qz-q {
    width: 2.5rem; height: 1.75rem;            /* 40x28 */
    opacity: 1;
  }
  .qz-qbox { height: 1.75rem; }                /* 40 wide is already desktop's */
  /* quote-check.svg is a 40-box whose check sits at (13,15) 15x10 — Figma's
     own numbers for the DESKTOP 40x40 wrapper.  Rendered at 40 in this 40x28
     pill and pulled up 6, the check lands on (13,9) 15x10, which is
     7064:2579's frame coordinate exactly (its 24x24 frame is at 8,2 and the
     vector at 5,7 inside it).
     ABSOLUTE, not the box's own `place-items: center`: browsers apply SAFE
     overflow alignment in the block axis, so an item taller than its track is
     clamped to the start edge rather than centred — measured, the img sat at
     y0 instead of the -6 that centring implies. */
  .qz-qbox img {
    position: absolute;
    left: 0; top: -0.375rem;                   /* -6 */
    width: 2.5rem; height: 2.5rem;             /* the 40-box viewBox, 1:1 */
  }

  /* Vector 8 / Vector 9 — the connector, now horizontal.  Figma draws it as
     two lines through the whole strip (an ink one x39..94 and a #111 @.08 one
     x97..392) with the opaque pills painted over the top, so the only parts
     ever seen are the six 18px gaps: the first is ink because row 1 is done,
     the rest are @.08.  Per-gap is therefore pixel-identical to Figma's two
     nodes AND keeps following the run of completed rows the way desktop's
     does.  Both `transform`s are restated to `none` — the desktop rules use
     scaleY to draw the line downward and would otherwise still apply. */
  .qz-q:not(:last-child)::after {
    left: 2.5rem;                              /* the pill's right edge */
    top: 0.8125rem;                            /* (28 - 2) / 2 = 13 */
    width: calc(var(--qz-row-pitch) - 2.5rem); /* the 18 gap, off the same knob */
    height: 0.125rem;                          /* weight 2 */
    background: rgba(17, 17, 17, 0.08);        /* Vector 9 */
    transform: none;
    transition: background-color var(--qz-dur-step) var(--qz-ease-step);
  }
  .qz-q.is-done:not(:last-child)::after {
    background: var(--ink);                    /* Vector 8 */
    transform: none;
  }

  /* ---- step 1 ----------------------------------------------------------
     Frame 113 is CENTER/CENTER here too, and its box proves it again:
     433 + 30 = 463 = 926/2.  So the card stays the anchor and the heading,
     the can't-find line and the CTA hang off it at Figma's own offsets. */
  .qz-search { width: 24.25rem; }              /* 388 — @20,433, 60 tall */

  /* TEXT 7064:2573 @49,305 330x80 — HostGrotesk-SemiBold 32/40, ls -1.28.
     305 + 80 = 385 = 463 - 78, and 78 is the card's 30 half-height plus the
     same 48 gap desktop has, so `bottom: calc(50% + 4.875rem)` carries over
     from .qz-h-address untouched. */
  .qz-h {
    width: 20.625rem;                          /* 330 */
    font-size: 2rem;                           /* 32 */
    line-height: 2.5rem;                       /* 40 */
  }

  /* TEXT 7064:2574 @67,505 295x18 — Geist-Regular 14/18.2.  Desktop anchors
     this to the pane's bottom; here it belongs to the centred card, 42 below
     its middle (12 under the card's own bottom edge). */
  .qz-cant {
    top: calc(50% + 2.625rem);                 /* 463 + 42 = 505 */
    bottom: auto;
    font-size: 0.875rem;                       /* 14 */
    line-height: 1.1375rem;                    /* 18.2 */
  }

  /* Link - Solid Black 7064:2565 @162,572 103.99x52 — the button itself is
     byte-identical to desktop's (52 tall, pad 10/10/10/16, gap 9.99, 32 chip),
     so only the offset moves: 572 = 463 + 109. */
  .qz-cta-address { top: calc(50% + 6.8125rem); }   /* 109 */

  /* ---- Frame 15 7064:2556 @20,870 225x24 ------------------------------- */
  .qz-side-bot {
    left: 1.25rem;                             /* 20 */
    top: auto; bottom: 2rem;                   /* 926 - 870 - 24 = 32 */
    width: 14.0625rem; height: 1.5rem;         /* 225x24 */
    gap: 0.375rem;                             /* 6, not desktop's 12 */
  }
  /* image 111, 125x24 under STRETCH tf [[1,0,0],[0,0.41339,0.58268]] ->
     height 1/0.41339 = 241.94%, top -0.58268/0.41339 = -140.95%.  The same
     crop /quote/results and the homepage both use, expressed in % of the box
     so it rides the fluid scale. */
  .qz-tp-stars {
    position: relative; display: block;
    width: 7.8125rem; height: 1.5rem;          /* 125x24 */
    overflow: hidden;
    flex: none;
  }
  .qz-tp-stars img {
    position: absolute;
    left: 0; top: -140.95%;
    width: 100%; height: 241.94%;
    max-width: none;
  }

  /* ---- Arrow Icon Wrapper 7064:2586 @368,862 40x40 ---------------------
     368 + 40 = 408, so its 20 right inset mirrors the Trustpilot row's 20 on
     the left.  The glyph is quote-chat.svg, generated from 7064:2589/:2590's
     own strokeGeometry — a chat bubble with a smile, NOT the WhatsApp mark the
     results page uses. */
  .qz-fab {
    display: grid; place-items: center;
    position: absolute;
    left: 23rem;                               /* 368 */
    top: auto; bottom: 1.5rem;                 /* 926 - 862 - 40 = 24 */
    width: 2.5rem; height: 2.5rem;             /* 40x40 */
    padding: 0; margin: 0; border: 0; appearance: none;
    background: var(--accent);
    border-radius: var(--radius-sm);
    cursor: pointer;
  }
  .qz-fab img { width: 1.5rem; height: 1.5rem; display: block; }   /* the 24x24 frame */

  /* ======================================================================
     STEP 2 — M02 "Get a qoute" 7064:2591, 428x926
     ======================================================================
     Every piece of furniture — logo, back pill, the seven stepper pills, the
     Trustpilot row, the FAB — is at byte-identical coordinates to M01, so
     none of it is restated.  Only the step's own four nodes move.

     And the anchor is the same trick again: Frame 1707481525 is CENTER/CENTER
     and its box proves it, 333 + 130 = 463 = 926/2.  So the map card is the
     midline and everything else hangs off it at Figma's own offsets — which
     also makes the two rhythms below identical to step 1's: the can't-find
     line sits 12 under the card's bottom edge on both screens, and the CTA 79
     under it on both. */
  .qz-h-map {
    top: calc(50% - 15.4375rem);               /* 463 - 247 = 216 */
    bottom: auto;
  }
  /* TEXT 7064:2622 @66,264 296x21 — Geist-Regular 16/20.8, #111 @.7.  Same
     string and same 296 box as desktop; only the offset differs. */
  .qz-map-sub {
    top: calc(50% - 12.4375rem);               /* 463 - 199 = 264 */
    line-height: 1.3rem;                       /* 20.8 */
  }
  /* Frame 1707481525 @20,333 388x260, #fff r12, clipsContent */
  .qz-map {
    top: 50%;
    transform: translate(-50%, -50%);          /* .qz-step > * only sets translateX */
    width: 24.25rem; height: 16.25rem;         /* 388x260 */
  }
  /* image 143 — the SAME 1160x478 source as desktop, cropped differently:
     card-rel (-334,-126) here against desktop's (-118,-76). */
  .qz-map img {
    left: -20.875rem; top: -7.875rem;          /* -334, -126 */
  }
  /* Link - Solid Black 7064:2601 @162,672 — 672 = 463 + 209, i.e. the card's
     130 half-height plus the same 79 step 1 has under its own card. */
  .qz-cta-map { top: calc(50% + 13.0625rem); } /* 209 */
  /* TEXT 7064:2610 @67,605 — 605 = 463 + 142, the card's 130 plus the same 12
     step 1 has.  Scoped to the step rather than given a modifier class, because
     .qz-cant is a motion reveal target and CLAUDE.md's rule 7 says React must
     never own a target's className. */
  .qz-s2 .qz-cant { top: calc(50% + 8.875rem); }   /* 142 */

  /* ======================================================================
     STEP 3 — M03 "Get a qoute" 7064:2631, 428x926
     ======================================================================
     Furniture identical to M01/M02 again, so nothing above is restated.  Two
     things are genuinely different from the desktop screen, not just smaller:

     1. THE HELPER MOVES ABOVE THE CARDS.  Desktop runs heading -> cards ->
        helper (which it bottom-anchors); the 428 frame runs heading 273..353,
        helper 365..405, then the grid at 453.  Both are absolute, so this is
        one `top`.
     2. The cards are FILL, not fixed: Frame 1707481510 is 388 wide with a 16
        gap, so (388 - 16) / 2 = Figma's own 186.  `flex: 1 1 0` reproduces
        that without hardcoding the width.

     The grid is NOT on the midline here (453 + 100 = 553, not 463) even though
     Figma marks it CENTER/CENTER, so these are literal tops — which is exact
     either way, since the mobile shell is a fixed 926-unit canvas. */
  .qz-s3 .qz-h { top: 17.0625rem; }            /* TEXT 7064:2650 @49,273 330x80 */
  .qz-s3 .qz-helper {
    top: 22.8125rem;                           /* TEXT 7064:2665 @20,365 388x40 */
    bottom: auto;
    width: 24.25rem;                           /* 388 */
  }
  .qz-s3 .qz-grid {
    top: 28.3125rem;                           /* Frame 1707481510 @20,453 388x200 */
    width: 24.25rem;                           /* 388, FIXED */
  }
  .qz-s3 .qz-opt {
    flex: 1 1 0;                               /* sizing=FILL -> (388-16)/2 = 186 */
    width: auto;
    height: 12.5rem;                           /* 200 */
  }
  /* the pill is 162 wide at rel(12,12) — 186 - 12 - 12, so it is still a pair
     of insets, and its 24 chip at (10,11) and 20/26 label at x42 are byte-
     identical to desktop's */
  .qz-s3 .qz-opt-pill { left: 0.75rem; right: 0.75rem; top: 0.75rem; }
  /* RECTANGLE 7064:2653 — 200x200 at rel(0,0) in a 186-wide card, so it
     overhangs 14 to the right and is clipped.  Its lime wash is a second paint
     in the SAME fill list, i.e. it rides this rect; reproducing it at the
     card's own inset is pixel-identical because the gradient is vertical. */
  .qz-s3 .qz-opt-photo {
    left: 0; top: 0; right: auto; bottom: auto;
    width: 12.5rem; height: 12.5rem;           /* 200x200 */
  }
  /* RECTANGLE 7064:2660 — 186x186 at rel(0,34), so 20 of it is clipped off the
     bottom.  Its wash is a SEPARATE 186x200 rect (7064:2661) at (0,0), which
     is exactly what .qz-opt-tint's `inset: 0` already is. */
  .qz-s3 .qz-opt-photo-low {
    top: 2.125rem;                             /* 34 */
    width: 11.625rem; height: 11.625rem;       /* 186x186 */
  }

  /* ======================================================================
     STEP 4 — M04 "Get a qoute" 7064:3855, 428x926
     ======================================================================
     Same shape as M03: helper above the cards, two FILL cards per row on the
     388 grid with a 16 gap, so (388 - 16) / 2 = Figma's 186 again.  Two rows,
     16 apart: 431 + 114 + 16 = 561.

     The bed icons shrink with everything else — 32x32 on a 40 pitch (an 8 gap)
     at y70, where desktop has 40x40 on 52 at y86.  quote-floor.svg's viewBox is
     40, so rendering it at 32 reproduces 7064:3879..3883's own numbers exactly
     (5.33 / 26.67 / 12 / 13.33 are the desktop values x0.8). */
  .qz-s4 .qz-h { top: 15.6875rem; }            /* TEXT 7064:3874 @49,251 330x80 */
  .qz-s4 .qz-helper {
    top: 21.4375rem;                           /* TEXT 7064:3961 @20,343 388x40 */
    bottom: auto;
    width: 24.25rem;                           /* 388 */
  }
  .qz-s4 .qz-grid-2  { top: 26.9375rem; width: 24.25rem; }   /* 7064:3875 @20,431 */
  .qz-s4 .qz-grid-2b { top: 35.0625rem; width: 24.25rem; }   /* 7064:3904 @20,561 */
  .qz-s4 .qz-opt {
    flex: 1 1 0;                               /* FILL -> 186 */
    width: auto;
    height: 7.125rem;                          /* 114 */
  }
  .qz-s4 .qz-opt-pill { left: 0.75rem; right: 0.75rem; top: 0.75rem; }
  /* `width: max-content` is load-bearing, and the bug it fixes is subtle: the
     row is absolutely positioned at `left: 50%` with `translateX(-50%)`, so its
     shrink-to-fit width is capped at the space LEFT of the right edge — 186/2 =
     93.  Two icons (72) fit, three (112) do not, so the box was clamped to 93,
     the translate moved it by 46.5 instead of 56, and the "Three" card's icons
     landed on 46.5 / 86.5 / 126.5 against Figma's 37 / 77 / 117.  With
     max-content the box is its content again and all four cards centre.
     (Measured, and the same arithmetic says the locked 1920 render has it too:
     3x40 + 2x12 = 144 against a 272/2 = 136 cap.  Reported, not touched.) */
  .qz-s4 .qz-opt-beds {
    top: 4.375rem;                             /* 70 */
    gap: 0.5rem;                               /* 40 pitch - the 32 icon */
    width: max-content;
  }
  .qz-s4 .qz-opt-beds img { width: 2rem; height: 2rem; }     /* 32x32 */

  /* The "Four+" card is the one row Figma does NOT centre: its icons start at
     12 and its plus sits 2 after the last one rather than the row's 8, so the
     whole group runs 12..182 where a centred one would run 8..178.  Left-
     anchoring it and pulling the plus in by 6 reproduces 12 / 52 / 92 / 132 and
     the plus at 166 exactly.  The other three cards stay centred and land on
     Figma's own 77 / 57 / 37 by themselves. */
  .qz-s4 .qz-opt-beds:has(.qz-opt-plus) {
    left: 0.75rem;                             /* 12 */
    transform: none;
  }
  /* FRAME 7064:3930 @166,78 16x16 — a VECTOR cross (two #7f7f7a weight-2
     strokes, arms 9.33 long), not desktop's text "+".  The text node stays in
     the markup for the locked 1920 render and is zeroed here. */
  .qz-s4 .qz-opt-plus {
    position: relative;
    flex: none;
    width: 1rem; height: 1rem;                 /* 16x16 */
    margin-left: -0.375rem;                    /* the row's 8 gap -> Figma's 2 */
    font-size: 0;
  }
  .qz-s4 .qz-opt-plus::before,
  .qz-s4 .qz-opt-plus::after {
    content: "";
    position: absolute;
    background: #7f7f7a;
  }
  .qz-s4 .qz-opt-plus::before {                /* VECTOR 7064:3932 rel(8,3.33) 0x9.33 */
    left: 0.4375rem; top: 0.208125rem;
    width: 0.125rem; height: 0.583125rem;
  }
  .qz-s4 .qz-opt-plus::after {                 /* VECTOR 7064:3933 rel(3.33,8) 9.33x0 */
    left: 0.208125rem; top: 0.4375rem;
    width: 0.583125rem; height: 0.125rem;
  }

  /* ======================================================================
     STEP 5 — M05 "Get a qoute" 7064:2674, 428x926
     ======================================================================
     Same two-FILL-card grid, 205 tall this time, and the card body is the
     chart panel rather than a photo: Frame 7064:2700 @12,74 162x115, a white
     r12 box with a GRADIENT_LINEAR stroke (#00ab55 -> #f9fdfb on the left
     card, #ff5630 -> #fffaf9 on the right) wrapping an area + line.

     The whole panel is ONE asset, and the 162x115 mobile version already
     shipped with the wizard extraction — quote-chart-m-half.svg /
     -m-allday.svg, generated from these very nodes.  It is selected by a
     <picture><source media> on the same 767.98 boundary, so no CSS or class
     juggling is involved and the desktop <img> is untouched. */
  .qz-s5 .qz-h { top: 16.25rem; }              /* TEXT 7064:2693 @49,260 330x80 */
  .qz-s5 .qz-helper {
    top: 22rem;                                /* TEXT 7064:2713 @20,352 388x60 */
    bottom: auto;
    width: 24.25rem;                           /* 388 */
  }
  .qz-s5 .qz-grid { top: 28.75rem; width: 24.25rem; }        /* 7064:2694 @20,460 */
  .qz-s5 .qz-opt {
    flex: 1 1 0;                               /* FILL -> 186 */
    width: auto;
    height: 12.8125rem;                        /* 205 */
  }
  .qz-s5 .qz-opt-pill { left: 0.75rem; right: 0.75rem; top: 0.75rem; }
  .qz-s5 .qz-opt-art {
    left: 0.75rem; top: 4.625rem;              /* 12,74 */
    width: 10.125rem; height: 7.1875rem;       /* 162x115 */
  }
  /* Figma puts the right card's panel on 74.5, not 74 — half a unit, and its
     own, so it is reproduced rather than tidied away. */
  .qz-s5 .qz-grid > .qz-opt:last-child .qz-opt-art { top: 4.65625rem; }   /* 74.5 */

  /* ======================================================================
     STEP 6 — M06 "Get a qoute" 7064:2731, 428x926   (the branch step)
     ======================================================================
     Same 388x205 grid as M05, and the card body is Figma's plain colour dot:
     ELLIPSE 7064:2748 / :2754, 114x114 at rel(36,74) — and 36 is (186-114)/2,
     so the existing centring places it and only the size and top move.

     The heading is the one on this screen that is NOT 330 wide: 7064:2741 is
     authored at the full 388 because the string is longer. */
  .qz-s6 .qz-h {
    top: 16.25rem;                             /* TEXT 7064:2741 @20,260 388x80 */
    width: 24.25rem;                           /* 388, not the usual 330 */
  }
  .qz-s6 .qz-helper {
    top: 22rem;                                /* TEXT 7064:2755 @20,352 388x60 */
    bottom: auto;
    width: 24.25rem;
  }
  .qz-s6 .qz-grid { top: 28.75rem; width: 24.25rem; }        /* 7064:2742 @20,460 */
  .qz-s6 .qz-opt {
    flex: 1 1 0;                               /* FILL -> 186 */
    width: auto;
    height: 12.8125rem;                        /* 205 */
  }
  .qz-s6 .qz-opt-pill { left: 0.75rem; right: 0.75rem; top: 0.75rem; }
  .qz-s6 .qz-opt-dot {
    top: 4.625rem;                             /* 74 */
    width: 7.125rem; height: 7.125rem;         /* 114x114 */
  }

  /* ======================================================================
     STEP 7 — M07 "Get a qoute" 7064:2764, 428x926
     ======================================================================
     The one step that RESTRUCTURES rather than just rescaling.  Desktop puts
     three cards in one row; the 428 frame keeps Low and Average as a pair
     (Frame 7064:2793 @20,422 388x205) and lifts High out into a standalone
     388x139 card below it (Frame 7064:2812 @20,642) whose body is laid out
     side by side instead of stacked:

       pill  7064:2818 @12,12  174x115   <- a full-height panel, not a 46 bar
       chart 7064:2814 @202,12 174x115   <- and quote-chart-m-high.svg is 174
                                            wide where the other two are 162

     Wrapping the existing flex row reproduces that with no markup change: the
     third card takes `flex: 0 0 100%` and pushes itself onto a second line.
     The row gap is Figma's own 15 (422 + 205 = 627, card 3 at 642), which is
     one less than the 16 column gap — its number, not a tidy-up. */
  .qz-s7 .qz-h {
    top: 13.875rem;                            /* TEXT 7064:2783 @20,222 388x80 */
    width: 24.25rem;                           /* 388 */
  }
  .qz-s7 .qz-helper {
    top: 19.625rem;                            /* TEXT 7064:2784 @20,314 388x60 */
    bottom: auto;
    width: 24.25rem;
  }
  .qz-s7 .qz-grid {
    top: 26.375rem;                            /* 7064:2793 @20,422 */
    width: 24.25rem;                           /* 388 */
    flex-wrap: wrap;
    column-gap: 1rem;                          /* 16 */
    row-gap: 0.9375rem;                        /* 15 — Figma's own */
  }
  .qz-s7 .qz-opt {
    flex: 1 1 0;                               /* FILL -> 186 */
    width: auto;
    height: 12.8125rem;                        /* 205 */
  }
  .qz-s7 .qz-opt-pill { left: 0.75rem; right: 0.75rem; top: 0.75rem; }
  .qz-s7 .qz-opt-art {
    left: 0.75rem; top: 4.625rem;              /* 12,74 */
    width: 10.125rem; height: 7.1875rem;       /* 162x115 */
  }
  /* Average's panel is on 74.5, exactly as M05's right card is */
  .qz-s7 .qz-opt:nth-child(2) .qz-opt-art { top: 4.65625rem; }   /* 74.5 */

  /* --- High: the full-width card ---------------------------------------
     `:last-child` rather than `:nth-child(3)` so reordering the three options
     cannot silently produce a wrong-but-plausible layout. */
  .qz-s7 .qz-opt:last-child {
    flex: 0 0 100%;                            /* 388 — forces the wrap */
    height: 8.6875rem;                         /* 139 */
  }
  /* the pill becomes a 174x115 panel; `align-items: center` then lands the 24
     chip on Figma's y45 and the 26 label on its y44 by itself */
  .qz-s7 .qz-opt:last-child .qz-opt-pill {
    left: 0.75rem; right: auto; top: 0.75rem;  /* 12,12 */
    width: 10.875rem; height: 7.1875rem;       /* 174x115 */
  }
  .qz-s7 .qz-opt:last-child .qz-opt-art {
    left: 12.625rem; top: 0.75rem;             /* 202,12 */
    width: 10.875rem; height: 7.1875rem;       /* 174x115 */
  }

  /* ======================================================================
     STEP 8 — M08 "Get a qoute" 7064:2821, 428x926
     ======================================================================
     The slider survives the move almost intact — same 8-tall r4 rail, same
     #e2fa5a -> #869435 gradient fill, same 24x24 white r8 handle with its
     weight-3 ink stroke — but the 428 frame DROPS the two bordered
     "Min: 400 kWh" / "Min: 25000 kWh" caps entirely and keeps only the two
     tick labels, which move BELOW the rail (y518) instead of sitting on it.

     Figma draws two handles here (Rectangle 20 @119 and 21 @350) exactly as
     the desktop screen does (@226 and @457) — the build reduces both to one
     <input type="range"> thumb, which is an EXISTING deviation on the locked
     1920 render, not one this layout introduces.  Kept consistent. */
  .qz-s8 .qz-h {
    top: 18rem;                                /* TEXT 7064:2840 @20,288 388x80 */
    width: 24.25rem;                           /* 388 */
  }
  .qz-s8 .qz-helper {
    top: 23.75rem;                             /* TEXT 7064:2841 @20,380 388x60 */
    bottom: auto;
    width: 24.25rem;
  }
  /* the group is re-anchored on the handles' own top edge (488), which is what
     makes every child below a literal Figma offset */
  .qz-slider {
    top: 30.5rem;                              /* 488 */
    width: 24.25rem;                           /* 388 -> left 20 */
    height: 3.125rem;                          /* 488..538 */
  }
  .qz-slider-cap { display: none; }            /* no caps on the 428 frame */
  .qz-slider-track {
    left: 0; top: 0.5rem;                      /* Rectangle 19 @20,496 */
    width: 24.25rem; height: 0.5rem;           /* 388x8 */
  }
  .qz-slider-tick { top: 1.875rem; }           /* 518 - 488 */
  .qz-slider-tick-a { left: 4.1875rem; }       /* TEXT 7064:2850 @87  -> 67 */
  .qz-slider-tick-b { left: 18.5rem; }         /* TEXT 7064:2851 @316 -> 296 */
  .qz-cta-kwh { top: 36.625rem; }              /* 7064:2856 @162,586 */

  /* ======================================================================
     STEP 9 — M09 "Get a qoute" 7064:2864, 428x926
     ======================================================================
     Both option cards go full width and stack, 388x174 on a 16 gap
     (408 + 174 + 16 = 598), and the pill becomes a 174x150 panel again —
     the same shape M07's "High" card uses.  The label is authored at 120, so
     it wraps to 2 lines on card 1 and 3 on card 2, and `align-items: center`
     lands both on Figma's own y49 / y36 and the chip on y63 without a rule. */
  .qz-s9 .qz-h {
    top: 13.875rem;                            /* TEXT 7064:2883 @20,222 388x80 */
    width: 24.25rem;                           /* 388 */
  }
  .qz-s9 .qz-helper {
    top: 19.625rem;                            /* TEXT 7064:2884 @20,314 388x60 */
    bottom: auto;
    width: 24.25rem;
  }
  .qz-s9 .qz-grid {
    top: 25.5rem;                              /* 7064:2893 @20,408 */
    width: 24.25rem;                           /* 388 */
    flex-wrap: wrap;
    row-gap: 1rem;                             /* 598 - 408 - 174 */
  }
  .qz-s9 .qz-opt {
    flex: 0 0 100%;                            /* 388 — undoes .qz-opt-w1 / -w2 */
    width: auto;
    height: 10.875rem;                         /* 174 */
  }
  .qz-s9 .qz-opt-pill {
    left: 0.75rem; right: auto; top: 0.75rem;  /* 12,12 */
    width: 10.875rem; height: 9.375rem;        /* 174x150 */
  }
  .qz-s9 .qz-opt-label { width: 7.5rem; }      /* 120 — this is what forces the wrap */
  /* image 123 is 150x150 on both cards; card 2 adds image 124, the 99x99
     battery, at rel(278,63) */
  .qz-s9 .qz-opt-prod {
    top: 0.75rem;                              /* 12 */
    width: 9.375rem; height: 9.375rem;         /* 150x150 */
  }
  .qz-s9 .qz-opt-prod-1 { left: 13.5rem; }     /* 7064:2898 @216 */
  .qz-s9 .qz-opt-prod-a { left: 12.125rem; }   /* 7064:2904 @194 */
  .qz-s9 .qz-opt-prod-b {
    left: 17.375rem; top: 3.9375rem;           /* 7064:2905 @278,63 */
    width: 6.1875rem; height: 6.1875rem;       /* 99x99 */
  }

  /* the 428 frame's own copy where it differs from the 1920 one */
  .qz-d-only { display: none; }
  .qz-m-only { display: inline; }
}
