

.qz-addr-item + .qz-addr-item {
  border-top: 1px solid rgba(17, 17, 17, 0.08);
}

.qz-addr-btn:hover,
.qz-addr-btn:focus-visible {
  background: #f5f6f0;
}

.qz-addr-note-error {
  border-color: rgba(180, 35, 24, 0.35);
  background: #fff6f5;
  color: #8c1d16;
}

.qz-addr-picked b {
  font-weight: 600;
}

/* The design gives this line `white-space: nowrap`, which it can afford as an
   absolutely positioned label on a 1920 canvas. In the roof step it is a block
   in normal flow, and at 320 wide the sentence is wider than the pane — which,
   under `.qz-main`'s `overflow: clip`, is silently cut rather than scrolled. It
   wraps here, centred, so the narrowest phone still reads the whole line. */
.qz-roof .qz-map-sub {
  margin: 0;
  white-space: normal;
  text-align: center;
}

/* In a column flex the default `flex-shrink: 1` would squeeze these boxes and,
   with any clipping, swallow whatever sits lowest. Nothing here shrinks. */
.qz-roof > * { flex: none; width: 100%; max-width: 47.375rem; }
.qz-roof-map > * { width: 100%; }

.qz-field input::placeholder {
  color: var(--ink-70);
}

/* ==========================================================================
   SELECTED OPTION CARDS  (restores the EPC pre-fill visually)
   --------------------------------------------------------------------------
   quote-wizard.css line 881 drops `.qz-opt.is-selected` from the hover recipe
   on purpose — Figma models this state with ON_HOVER overlays only and defines
   no persistent selected look, so a card you picked stopped staying lit.

   That decision also makes an EPC pre-fill invisible: the backend fills in the
   property type and floors, the card correctly carries `.is-selected` and
   `aria-pressed="true"`, and nothing on screen changes — which reads as "the
   pre-fill is broken".

   These are the five selectors that same comment names as the way to restore
   it, using the design's own tokens and its own recipe. Nothing is invented,
   and quote-wizard.css stays byte-identical to the design team's file.
   ========================================================================== */
.qz-opt.is-selected::after { border-color: var(--qz-card-line-h); }
.qz-opt.is-selected .qz-opt-pill { background: var(--accent); }
.qz-opt.is-selected .qz-opt-chip { background: var(--ink); }
.qz-opt.is-selected .qz-opt-chip img { opacity: 1; }
.qz-opt.is-selected .qz-opt-beds { color: var(--ink); }

/* ==========================================================================
   CONFIRMED ADDRESS — clear of the CTA
   --------------------------------------------------------------------------
   The suggestion list is a dropdown and overlaying the Next button is correct;
   it closes as soon as a suggestion is picked. The confirmation that replaces
   it is persistent, so it drops below the CTA instead of covering it.
   ========================================================================== */
.qz-addr.qz-addr-done { top: calc(50% + 8.75rem); }

/* Inside the row the buttons flow side by side, so the design's own absolute
   `top` on .qz-cta no longer applies — they are no longer `.qz-step > *`. */
.qz-actions .qz-cta { position: static; }

/* A disabled action reads as unavailable rather than merely unresponsive. */
.qz-actions .qz-cta:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.qz-actions .qz-cta:disabled:hover { transform: none; box-shadow: none; }

/* ==========================================================================
   THE kWh SLIDER'S AXIS LABELS SIT UNDER THE RAIL
   --------------------------------------------------------------------------
   The design draws the rail, the handle, two bordered min/max captions and two
   axis labels. A `.qz-slider-val` chip rode the handle here for a while — the
   step reported nothing about the figure being chosen and the handle was the
   only control — and it has been removed: the typed field under the rail is
   that readout now, and it does not move.

   The ticks move in here from `.qz-slider`: they carry a computed `left`, so
   they must measure the same 100% the handle does. Their authored 22-below-the
   rail is the same on both breakpoints — desktop 34 from a group top of 12,
   phone 30 from a group top of 8 — hence one rule, and the two-class selector
   outranks the design's own single-class `top` at either width.
   ========================================================================== */
.qz-slider-track .qz-slider-tick {
  top: 1.375rem;                        /* 22 below the rail, as authored */
  transform: translateX(-50%);
}

/* ==========================================================================
   THE kWh STEP TAKES A TYPED FIGURE
   --------------------------------------------------------------------------
   The rail is a COARSE control and cannot be anything else: 24,600 kWh across
   a 452px track is about 55 kWh per pixel, and its 100 kWh step means a figure
   read off a bill — "3,247 kWh" — can be approached but never entered. The
   readout above made the chosen figure visible; it did not make it settable.
   This is the same value as a field, so the homeowner can type what their bill
   says instead of hunting for it with a handle.

   BOTH CONTROLS ARE THE SAME VALUE. The field and the rail write `annual_kwh`
   through the one `clampKwh`, so the typed figure is held to the same range and
   the same 100 kWh step the handle is: neither control can carry a number the
   other cannot reach, and the readout, the fill and the field always agree.

   WHERE IT SITS. In the empty band the design already leaves between the rail
   and Next — 12.5rem on the desktop midline, and on the phone canvas the rail
   ends at 33.6 against a bottom-anchored Next at about 50. Both are measured
   below, so no authored element moves at either width.

   No `left` or `transform` here: `.qz-step > *` in quote-wizard.css already
   centres every direct child, and setting a transform would drop the -50%.
   ========================================================================== */
.qz-kwh-entry {
  top: 32.25rem;                        /* 1.5rem under the rail's tick row */
  display: grid;
  justify-items: center;
  gap: 0.625rem;
}
.qz-kwh-entry-label {
  font-family: var(--font-sans);
  font-weight: 500;                     /* it introduces the field, not the rail */
  font-size: 1rem;
  line-height: 1.22rem;                 /* 19.52, the captions' own */
  letter-spacing: -0.01rem;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   THE FIELD IS AN INPUT, AND HAS TO LOOK LIKE ONE
   --------------------------------------------------------------------------
   It was first drawn in the CAPTIONS' vocabulary — white on a 2px --qz-chip
   border — which was the wrong borrowing: --qz-chip is #f5f6f0, all but
   invisible against the white it sits on, and the min/max chips it copied are
   passive labels. So the one thing on the step the homeowner is meant to type
   into carried the same weight as the two things they cannot touch, and read as
   another caption.

   This wizard already HAS an input look — `.qz-field input[type="number"]` on
   the tariffs step: an elevated white card, no border at all, a soft ambient
   shadow, lifting on hover and taking an accent ring on focus. That is the
   design's own answer to "type here", so the field is moved into it rather than
   given a heavier border, and the two inputs in the journey now read as the
   same control. The figure itself steps up to the heading's display face,
   because it is the answer the whole screen is asking for.

   PROMINENCE IS THE TREATMENT, NOT THE SIZE. The lift off the page, the shadow
   and the display face are what carry it. The box stays close to the height it
   had: a single number does not need a bigger container than the three-field
   tariffs form to be the thing you reach for, and at 4rem tall with a 2rem
   figure it stopped reading as a field on this step and started reading as a
   panel dropped onto it.
   -------------------------------------------------------------------------- */
.qz-kwh-field {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  height: 3.25rem;
  padding: 0 1.25rem;
  border: 0;
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: 0 0 2.5rem rgba(0, 0, 0, 0.08);
  cursor: text;
  transition:
    box-shadow var(--dur-hover) var(--ease-premium),
    translate var(--dur-hover) var(--ease-premium);
}
/* `translate`, never `transform`: `.qz-step > *` owns the transform on the
   wrapper above, and the two properties are independent by design. */
.qz-kwh-field: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-kwh-field: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-kwh-input {
  /* 6ch holds the widest figure the field accepts (100,000) with the caret
     beside it, and right-aligns so the digits stay against the unit as they are
     typed rather than drifting away from it. */
  width: 6ch;
  margin: 0;
  padding: 0;
  border: 0;
  outline: 0;
  background: none;
  font-family: var(--font-display);     /* the heading's face — this is the answer */
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.8125rem;
  letter-spacing: -0.02rem;
  text-align: right;
  color: var(--ink);
}
.qz-kwh-unit {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;
  letter-spacing: -0.01rem;
  color: var(--ink-70);
}
/* Absolute, so a figure typed outside the range says so without shifting the
   field it is about. It is `role="status"`, so it is also announced. */
.qz-kwh-note {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%);
  width: 24.25rem;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 0.875rem;
  line-height: 1.125rem;
  letter-spacing: -0.01rem;
  text-align: center;
  color: var(--ink-70);
}

/* The one-tap correction inside that line — "Yes, use 3,600 kWh" when a year's
   usage was typed as a month's. An underlined run of the sentence rather than a
   button of its own: it belongs to the question it answers, and the design's
   own "Book a Call / WhatsApp now" line already sets buttons in copy this way.
   Ink rather than ink-70, so the thing to press is the darkest thing there. */
.qz-kwh-note-fix {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 500;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.1875rem;
  cursor: pointer;
}
.qz-kwh-note-fix:hover { text-decoration-thickness: 0.125rem; }

/* ==========================================================================
   SIDEBAR ROWS ARE DISPLAY ONLY
   --------------------------------------------------------------------------
   The rows report progress; the journey moves through Back and Next. The
   design's authored hover and pointer cursor came with the row being a click
   target, so both are stood down.
   ========================================================================== */
.qz-q .qz-q-btn { cursor: default; }
.qz-q:hover { opacity: inherit; }
.qz-q:hover .qz-qbox { background: var(--qz-chip); }
.qz-q.is-done:hover .qz-qbox { background: var(--ink); }

/* In a scrolling column the default flex-shrink would squeeze the rows to fit
   rather than letting them overflow — which is the same overlap by another
   route. Nothing shrinks; the container scrolls. */
.qz-qlist > .qz-q { flex: none; }

/* The bar itself is hidden — the list still scrolls, by wheel, drag, keyboard
   and the active row scrolling itself into view. */
.qz-qlist { scrollbar-width: none; -ms-overflow-style: none; }
.qz-qlist::-webkit-scrollbar { width: 0; height: 0; display: none; }
.qz-roof .border-neutral-300.bg-white:hover:not(:disabled) {
  border-color: rgba(17, 17, 17, 0.3);
  background: var(--qz-chip, rgba(17, 17, 17, 0.04));
}

/* The controls sit in a row rather than stacked full-width, so the step reads
   as one action bar under the map instead of a column of grey slabs. */
.qz-roof .flex.flex-col.gap-2 > .bg-neutral-900.text-white,
.qz-roof .flex.flex-col.gap-2 > .border-neutral-300.bg-white { width: auto; }

/* ---- the "Roof faces" label --------------------------------------------- */
.qz-roof .text-neutral-700 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.125rem;
  letter-spacing: -0.02rem;
  color: var(--ink);
}
.qz-roof li > .rounded-xl.border.border-neutral-900 {
  border-color: rgba(17, 17, 17, 0.28);
  background: var(--bg);
  box-shadow: 0 0 0 0.0625rem rgba(17, 17, 17, 0.06);
}
.qz-roof li .font-semibold.text-neutral-900 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.02rem;
  color: var(--ink);
}
.qz-roof li .text-neutral-600 {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  line-height: 1.375rem;
  color: var(--ink-70);
}

/* Per-card links: Edit gutter / Redraw / Delete — the design's underlined
   inline links, as used by "can't find" and the help line. */
.qz-roof li .underline,
.qz-roof li button.text-xs {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  color: var(--ink);
  text-underline-offset: 0.1875rem;
}

/* The imagery/credit note keeps the design's helper type. */
.qz-roof .text-neutral-500 {
  font-family: var(--font-sans);
  font-size: 0.75rem;
  line-height: 1.125rem;
  color: var(--ink-70);
}

/* ==========================================================================
   DESKTOP GEOMETRY ONLY
   --------------------------------------------------------------------------
   Everything below re-places or re-sizes something on the design's 1920 canvas.
   The design ships no separate mobile stylesheet: its own `@media (max-width:
   767.98px)` blocks live inside quote-*.css and restate these same boxes for the
   428 canvas. Unscoped, these rules outranked those blocks at every width and
   the phone layout inherited desktop coordinates.

   Scoped to >=768 they apply exactly as before on desktop — which is unchanged —
   and below it the design's own mobile rules govern, as they were written to.

   Rules that are not geometry (colour, type, wrapping, hover and disabled
   states, the brand wordmark) stay global above: they are correct at any width.
   ========================================================================== */

/* ==========================================================================
   AT EVERY WIDTH — elements the design does not style
   --------------------------------------------------------------------------
   The design has no rules for these at any breakpoint: the EPC address list,
   the tariffs fields, the roof designer's opt-out, the Next row, the brand
   wordmark and the fitted logo slots are all ours. There is no mobile fallback
   for them to inherit, so scoping them to desktop left them positioned by the
   design's `> *` canvas rules on a phone. The roof designer matters most: it is
   an interactive map with its own controls and cannot live on a coordinate
   canvas at ANY width.
   ========================================================================== */
/* Address lookup states for the /quote wizard's step 1.
 *
 * Kept out of quote-wizard.css so that file stays byte-identical to the design
 * team's build. Everything here is additive: it styles only the markup the
 * backend integration adds (the EPC suggestion list, the searching/error rows),
 * and it inherits the wizard's own tokens and geometry, so the approved design
 * of the field, the CTA and the "can't find" line is untouched.
 *
 * Geometry follows .qz-search itself — the 452px card at the same left edge —
 * so the list reads as part of that control rather than a new element.
 */

/* `.qz-step > *` already gives every step child `position:absolute; left:50%;
   transform:translateX(-50%)`, so only the y is set here: the field is centred
   at 50% and is 3.75rem tall, which puts its bottom edge at 50% + 1.875rem. */
.qz-addr {
  top: calc(50% + 1.875rem);
  z-index: 5;
  width: 28.25rem; /* 452 — the field's own width */
  max-width: 100%;
}
.qz-addr-list {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  max-height: 264px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid rgba(17, 17, 17, 0.12);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(17, 17, 17, 0.12);
}

.qz-addr-btn {
  display: block;
  width: 100%;
  padding: 12px 20px;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: #111;
}

.qz-addr-line {
  display: block;
  font-size: 0.9rem;
  line-height: 1.35;
}

.qz-addr-pc {
  display: block;
  margin-top: 2px;
  font-size: 0.75rem;
  color: rgba(17, 17, 17, 0.55);
}

/* Status rows: searching, no matches, and a failed lookup with a retry. */
.qz-addr-note {
  margin: 8px 0 0;
  padding: 12px 20px;
  background: #fff;
  border: 1px solid rgba(17, 17, 17, 0.12);
  border-radius: 12px;
  font-size: 0.85rem;
  line-height: 1.4;
  color: rgba(17, 17, 17, 0.7);
}

.qz-addr-retry {
  margin-left: 8px;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* The selected address, echoed under the field so the homeowner can see what the
   lookup matched before they continue. */
.qz-addr-picked {
  margin: 8px 0 0;
  padding: 10px 20px;
  border-radius: 12px;
  background: #f5f6f0;
  font-size: 0.85rem;
  line-height: 1.4;
  color: #111;
}

.qz-addr-epc {
  display: block;
  margin-top: 2px;
  font-size: 0.75rem;
  color: rgba(17, 17, 17, 0.55);
}

/* ==========================================================================
   ROOF DESIGNER STEP
   --------------------------------------------------------------------------
   The backend's journey includes a roof designer (`q8_roof`) that the design
   source has no screen for — its step 2 is a static image, and the platform's
   own DesignStep is the real map + drawing UI. It therefore keeps its existing
   appearance and behaviour, and only the surrounding chrome (sidebar, back
   link, progress dots) comes from the design.

   `.qz-step > *` absolutely positions every direct child on the 1920 canvas,
   which a full interactive component cannot live inside. This container opts
   that one step out, so DesignStep lays itself out exactly as it does today.
   ========================================================================== */
.qz-step > .qz-roof {
  position: static;
  transform: none;
  left: auto;
  width: 100%;
  height: 100%;
  overflow-y: auto;
}

/* ==========================================================================
   THE PANE'S CHROME SITS ABOVE THE STEP, NOT UNDER IT
   --------------------------------------------------------------------------
   Back, the "Book a Call / WhatsApp now" line and the chat button are siblings
   of `.qz-step` inside `.qz-main`, and they are declared BEFORE it. All four
   are positioned with no z-index, so the step paints over all three.

   On every other screen that is harmless: `.qz-step` is `pointer-events: none`
   and only its absolutely-positioned children take events, and those children
   are small centred boxes that never reach the corners the chrome occupies.
   quote-wizard.css says so in its own note on `.qz-step.is-active`.

   The roof step is the one screen where it is not harmless. `.qz-roof` above is
   a direct child, so it takes `pointer-events: auto` from
   `.qz-step.is-active > *` — and it is `position: static; width: 100%;
   height: 100%`, so it covers the WHOLE pane, including (0,0). It is
   transparent, so Back still looked exactly right; every click on it landed on
   the roof container instead. That is the reported dead Back button, and the
   same swallow silently killed Book a Call, WhatsApp and the chat button there.

   Ordering the chrome above the step is the fix, rather than clawing back the
   corners from `.qz-roof`: the chrome is meant to be on top — it is the frame
   the steps are shown inside — and stating that once here covers any future
   step that needs a full-pane child, instead of leaving the next one to
   rediscover this. Nothing moves: z-index paints, it does not lay out.
   ========================================================================== */
.qz-main > .qz-back,
.qz-main > .qz-help,
.qz-main > .qz-fab {
  z-index: 1;
}

/* Inside it the step lays out like every other question: the design's centred
   column, its heading and sub-line, then the map, then the Back/Next row. The
   children are in normal flow here, so the absolute `top` values the design
   gives .qz-h / .qz-map-sub / .qz-actions are stood down and replaced by
   ordinary spacing. */
.qz-roof {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  /* A symmetric gutter, not a top offset. The old `padding-top: 3.5rem` pinned
     the step to the top of the pane — on a 930-tall canvas the heading, the map
     and Continue sat in the upper half with the rest of the pane empty — and
     gave the narrow widths no side gutter at all. */
  padding: 1.5rem clamp(1rem, 4vw, 2.5rem);
}

/* ---- centred, and still scrollable when it does not fit -----------------
   Centring is done with auto margins rather than `justify-content: center`,
   deliberately. This element is the step's scroll container, and a centred
   flex column that overflows puts its first child ABOVE the scrollable origin —
   the heading becomes unreachable, with no scrollbar position that reveals it.
   Auto margins collapse to 0 the moment the content is taller than the pane, so
   the short screens (pin your roof) centre and the tall one (draw your roof,
   once the roof-face list is on screen) falls back to top-aligned and scrolls
   normally. */
.qz-roof > :first-child { margin-top: auto; }
.qz-roof > :last-child { margin-bottom: auto; }

.qz-roof .qz-h,
.qz-roof .qz-map-sub,
.qz-roof .qz-actions {
  position: static;
  transform: none;
  left: auto;
  top: auto;
  width: auto;
  max-width: 47.375rem;
}

/* The map column is the design's 758 width and nothing more.
   NO `overflow` and NO height here, deliberately: DesignMap renders the map and
   its own controls — Finish roof, Redraw outline, Undo point, Cancel — as
   siblings in one container. Clipping or shrinking this box hides those
   controls and the roof can never be finished. */
.qz-roof-map {
  width: 47.375rem;
  max-width: 100%;
}

/* The crosshair frame's own Tailwind height is a flat 256, or 320 from 640 up,
   so it neither used the room a desktop pane has nor gave way on a short one.
   It tracks the viewport between the design's own two figures instead — 260 is
   the 428 frame's map card, 360 the 1920 one's — which keeps every width inside
   the numbers the step was drawn at. The two-class selector is what outranks
   Tailwind's `sm:h-80`; both of its own are single-class. */
.qz-roof-map .qz-map-frame {
  height: clamp(16.25rem, 38vh, 22.5rem);    /* 260 -> 360 */
}

/* The roof-face list and status line follow the design's helper type. */
.qz-roof .qz-roof-note {
  font-family: var(--font-sans);
  font-size: 0.875rem;
  color: var(--ink-70);
  margin: 0;
}

/* ==========================================================================
   TARIFFS STEP
   --------------------------------------------------------------------------
   Another backend step the design has no screen for. Rather than invent a look,
   it borrows the design's own field card (.qz-search's white 452 card, radius
   and shadow) and type scale, so it sits inside the journey without pretending
   to be a Figma screen.
   ========================================================================== */
.qz-fields {
  top: calc(50% - 9rem);
  width: 28.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  text-align: left;
}

.qz-field {
  display: block;
}

.qz-field-label {
  display: block;
  margin-bottom: 0.25rem;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  color: var(--ink-70);
}

/* ---- the field IS step 1's address card ----------------------------------
   The two controls were drawn from the same recipe but only step 1 was ever
   finished. Its card (`.qz-search`, quote-wizard.css) is a 60-tall white box on
   Figma's one real shadow that lifts 2px on hover and takes a lime ring on
   focus; this one had the white box and the resting shadow and then stopped —
   no lift, no ring, no transition, and its own type metrics.

   Every value below is `.qz-search`'s own, copied rather than reinvented:

     height 3.75rem            60, was 3.25rem (52) — the last resting mismatch
     line-height 1.22rem       19.52, InstrumentSans-Regular
     letter-spacing -0.01rem   -0.16
     cursor text               step 1's whole card carries it

   The one structural difference is where the state lives. On step 1 the card is
   a <label> WRAPPING the input, so it reacts on `:hover` / `:focus-within`. Here
   the input IS the card — `.qz-field` is a label carrying the caption above it —
   so the same states sit on the input and `:focus-within` becomes `:focus`. The
   field holds nothing else focusable, so the two are equivalent.

   `translate` and not `transform`, matching step 1 for the same reason it does:
   it keeps the lift on its own property so nothing has to restate a transform
   it does not own. (Step 1 needs that because of its CENTER/CENTER pin; here it
   simply keeps the two declarations identical.) */
.qz-field input[type="number"] {
  width: 100%;
  height: 3.75rem;
  padding: 0 1.25rem;
  border: 0;
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: 0 0 2.5rem rgba(0, 0, 0, 0.08);
  outline: none;
  appearance: none;
  cursor: text;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.22rem;
  letter-spacing: -0.01rem;
  color: var(--ink);
  transition:
    box-shadow var(--dur-hover) var(--ease-premium),
    translate var(--dur-hover) var(--ease-premium);
}
.qz-field input[type="number"]: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);
}
/* focus wins over hover — declared later, and both are (0,2,0) on the same
   element, which is exactly how quote-wizard.css orders step 1's pair */
.qz-field input[type="number"]:focus {
  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-field-checks {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.qz-field-checks label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  color: var(--ink);
}

/* ==========================================================================
   NEXT BUTTON ON THE QUESTION STEPS
   --------------------------------------------------------------------------
   The design advances by clicking an option card, so it draws a CTA only on
   the address, map and kWh screens. Selecting and confirming are separated
   here — pick an answer, then press Next — which also lets a pre-filled answer
   be reviewed instead of being skipped past.

   The position is the design's own: 43.3125rem is exactly where `.qz-cta-map`
   sits, clear of the tallest card grid (ends at 619) and of the helper text
   (starts around 830).
   ========================================================================== */
.qz-actions {
  top: 43.3125rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* ==========================================================================
   THE ROOF STEP'S OWN CONTROLS
   --------------------------------------------------------------------------
   The roof designer is the platform's, not the design's, so its buttons and its
   roof-face cards carry the dashboard's look — grey borders, small system type
   — which reads as a different product next to the wizard's chrome.

   These rules restyle them in the design's own tokens. Every selector is scoped
   to `.qz-roof`, so the shared primitives (SolarPrimaryButton, SolarSecondary-
   Button, SolarFieldLabel) are untouched everywhere else they are used — the
   staff designer, the survey and the other steps keep their current look. No
   component, prop or handler changes: this is appearance only.
   ========================================================================== */

/* ---- primary action: Finish outline / Finish roof / Show my prices -------- */
.qz-roof .bg-neutral-900.text-white {
  border-radius: var(--radius);
  background: var(--ink);
  padding: 0.9375rem 1.25rem;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.01rem;
  color: var(--white);
  transition:
    background-color var(--dur-hover) var(--ease-premium),
    transform var(--dur-hover) var(--ease-premium);
}
.qz-roof .bg-neutral-900.text-white:hover:not(:disabled) {
  background: var(--ink);
  transform: translateY(-0.0625rem);
}

/* ---- secondary action: Undo point / Redraw outline / Cancel -------------- */
.qz-roof .border-neutral-300.bg-white {
  border-radius: var(--radius);
  border: 0.0625rem solid rgba(17, 17, 17, 0.14);
  background: var(--white);
  padding: 0.9375rem 1.25rem;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.01rem;
  color: var(--ink);
  transition:
    background-color var(--dur-hover) var(--ease-premium),
    border-color var(--dur-hover) var(--ease-premium);
}

/* Disabled reads as unavailable, matching the wizard's own Next button. */
.qz-roof .bg-neutral-900.text-white:disabled,
.qz-roof .border-neutral-300.bg-white:disabled {
  opacity: 0.45;
  transform: none;
}

/* ---- a roof-face card ---------------------------------------------------
   The coloured left edge is set inline from the map's own palette, so it is
   left alone — it is what ties a card to its outline on the map. */
.qz-roof li > .rounded-xl.border {
  border-radius: var(--radius);
  border-color: rgba(17, 17, 17, 0.12);
  background: var(--white);
  padding: 1rem;
  font-family: var(--font-sans);
}

/* ---- the overall summary strip ------------------------------------------ */
.qz-roof .rounded-xl.bg-neutral-50,
.qz-roof .rounded-lg.bg-neutral-50 {
  border-radius: var(--radius);
  background: rgba(226, 250, 90, 0.2);       /* the design's own lime wash */
  padding: 0.875rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.875rem;
  color: var(--ink);
}


@media (min-width: 768px) {

  

  /* ==========================================================================
     THE QUESTION LIST SCROLLS
     --------------------------------------------------------------------------
     The design's list is a fixed 195x577 column whose row pitch is derived to fit
     exactly seven rows between the two rules. The backend's journey decides how
     many questions there are — a tenant can enable more, and the enquiry-flow
     settings can reorder or add steps — so at nine rows the column simply ran
     past the bottom rule and over the reviews footer.

     The band is now anchored to BOTH rules instead of carrying a fixed height, so
     it ends where the design's bottom rule begins whatever the card's height, and
     scrolls when the rows no longer fit. With seven or fewer rows there is
     nothing to scroll and the list looks exactly as designed.
     ========================================================================== */
  .qz-qlist {
    height: auto;
    bottom: 7.6875rem;              /* the bottom rule sits at 6.6875 — 1rem clear */
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;   /* the page does not scroll on from the list */
    /* No `scrollbar-width` here. This block used to re-declare it as `thin`,
       which — being later in the file at equal specificity — reinstated the very
       bar the top-level rule above hides, so desktop drew a track down the
       middle of the sidebar card while the same list on a phone did not. The
       bar stays hidden in one place; the list still scrolls by wheel, drag,
       keyboard and the active row scrolling itself into view. */
  }
}

/* ==========================================================================
   THE ROW FADE REACHES EVERY ROW
   --------------------------------------------------------------------------
   quote-wizard.css carries the design's fade as six chained
   `.is-current + .qz-q + …` selectors, one per step of its `1 - top/577` ramp.
   A chain only reaches as far as it is written: at the backend's nine questions
   the seventh and eighth rows below the current one matched no rule at all and
   fell back to `opacity: 1`, so Q08/Q09 rendered as the boldest rows on the
   card — the opposite of what the ramp is for.

   The ramp is computed in QuoteShell now (see `rowOpacity` there — same Figma
   values, held at its own last step instead of running to nothing) and arrives
   as `--qz-q-dim` on the row. The chain is left in the design file untouched;
   this single declaration supersedes it, exactly as `.qz-qfade` was superseded
   by the chain in the first place.

   Desktop only: the mobile stepper is a row of identical pills and deliberately
   fades nothing, which is `.qz-q { opacity: 1 }` inside the design's own
   max-width block. `.qz-q:hover` stays a step more specific, so hovering still
   brings a row fully forward.
   ========================================================================== */
@media (min-width: 768px) {
  .qz-q { opacity: var(--qz-q-dim, 1); }
}

/* ==========================================================================
   MOBILE — lifted back to the top level
   --------------------------------------------------------------------------
   These blocks were written for phones, and an automated pass that scoped the
   desktop geometry swept them inside `@media (min-width: 768px)`. A max-width
   query nested in a min-width query can never match, so they were silently
   inert at every width. Restored to the top level, where they apply.
   ========================================================================== */
@media (max-width: 767.98px) {
  .qz-addr {
    width: 100%;
  }
}

/* ==========================================================================
   THE COMPANY'S REVIEWS LINE STAYS INSIDE THE RAIL
   --------------------------------------------------------------------------
   `.qz-side-bot` is a 370x24 bottom-anchored row holding the reviews line and
   the Trustpilot mark, sized for the one short sentence it was drawn with. That
   sentence is now company-authored and shared across the journey, so a longer
   one wraps out of a 24-tall box.

   The row is bottom-anchored, so letting it grow simply extends upward. The
   dashed rule above sits at bottom 107 and the row's top at 67 — 40 of
   clearance, which is one extra line at the authored 24.4 leading and no more.
   Hence the two-line bound: the line can grow into the room that exists, and
   cannot reach the rule.
   ========================================================================== */
/* Desktop only, and every word of the reasoning above says so: the 370x24 row,
   the reviews sentence, the dashed rule at bottom 107. The 428 frame keeps ONLY
   the Trustpilot lockup in this row — `.qz-rev` is `display: none` there — so on
   a phone there is nothing for `height: auto` to grow for, and it grew anyway:
   measured at 390 the row was 44 tall against the design's own 22, which is the
   reference's figure exactly. Those 22 px are the band the Next row has to fit
   into on the option-card steps, so the row keeps its authored height there. */
@media (min-width: 768px) {
  .qz-side-bot {
    height: auto;
    min-height: 1.5rem;                 /* the authored 24 */
    align-items: flex-end;              /* grows upward off the baseline row */
  }
}
.qz-side-bot > .qz-rev {
  min-width: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.qz-side-bot > .qz-tp { flex: none; }

/* ==========================================================================
   Q8 "YOUR ELECTRICITY TARIFFS" ON A PHONE
   --------------------------------------------------------------------------
   base.css scales the root font with the viewport — `100vw / 26.75` below 768,
   i.e. the 428 canvas is exactly 26.75rem wide. The tariffs column is the
   desktop canvas's 452 (`28.25rem`), which under that scale is 105.6% of the
   viewport: it overflowed the frame by ~24px on EVERY phone, and `.qz-main`'s
   `overflow: clip` swallowed the difference, so each field lost about 12px off
   both ends. The step had no mobile rule of its own — the only question in the
   journey without one — because the design has no screen for it to be ported
   from.

   24.25rem is the 428 canvas's own 388 column, the same width `.qz-s8 .qz-h`,
   `.qz-slider` and `.qz-roof-map` use, so the fields now sit on the design's
   own mobile gutters at any phone width.

   The band is also re-anchored: at four fields plus three checks the column is
   taller here than the desktop `calc(50% - 9rem)` allows for, and Next sits at
   43.3125rem. Anchoring the top under the heading instead of on the midline
   keeps the two apart without touching either.
   ========================================================================== */
@media (max-width: 767.98px) {
  .qz-fields {
    width: 24.25rem;                      /* 388 — the phone frame's column */
    top: 17rem;                           /* clear of the 2-line heading at 191 */
  }
  /* The inputs keep the design's 52 height; only the label wraps at this width,
     so nothing else in the field card needs a mobile value. */
  .qz-field-label { line-height: 1.3; }
}

/* ==========================================================================
   THE PHONE FRAME NO LONGER SCROLLS IN EITHER DIRECTION
   --------------------------------------------------------------------------
   Measured on the running build at 360x800, 390x844 and the design's own
   428x926, on the first wizard screen:

       viewport 428   documentElement.scrollWidth 524   scrollHeight 1134

   Two independent overflows, both present at every phone width, and between
   them they are the whole "mobile is not responsive" report. The horizontal one
   is the damaging half: once the document is wider than the viewport the page
   can be panned, and every screen then renders shifted left by whatever
   scrollLeft the browser is holding — which is why the logo, the headings, the
   map card and the field labels are all clipped on their LEFT edge in the
   report's screenshots, on three different steps. Nothing on those steps is
   individually mispositioned; the whole frame is off-origin.

   Each cause is fixed at its source below, and `.qz-shell` then clips as a
   backstop so a future overflow can never re-introduce the pan.
   ========================================================================== */

/* --- 1. the stepper is sized for SEVEN pills, the journey has NINE ---------
   quote-wizard.css lays the mobile stepper out as a fixed 40-wide pill on a
   fixed 58 pitch — the 428 frame's own numbers, and exactly right for the seven
   rows Figma drew: 20 + 6*58 + 40 = 408, inside the frame's 428.

   `sidebarRows()` derives the rows from the backend journey, which runs to nine
   (a tenant can enable more, or reorder them from the enquiry-flow settings).
   At nine the same arithmetic gives 20 + 8*58 + 40 = 524, i.e. 96 past the
   frame — the measured scrollWidth above, to the unit. The desktop list already
   has the matching fix (it is anchored to both rules and scrolls); the mobile
   row, being horizontal, got nothing.

   Rather than scroll a progress indicator, the pitch is derived from the row
   count so the run always spans the frame's own 388 column. Figma's pill/pitch
   ratio is kept — 40/58 — so the whole geometry falls out of one number:

       pitch = 388 / (N - 18/58)      pill = pitch * 40/58      gap = pitch - pill

   At N = 7 that is 58 / 40 / 18: every authored value reproduced exactly, so
   the seven-row design is untouched. At N = 9 it is 44.6 / 30.8 / 13.8 — the
   pills are narrower and the row fits. `flex-shrink` and `min-width: 0` below
   are the belt-and-braces: if `--qz-qcount` is ever absent the pills compress
   into the 388 instead of overflowing it again. */
@media (max-width: 767.98px) {
  .qz-qlist {
    --qz-row-pitch: calc(24.25rem / (var(--qz-qcount, 7) - 0.3103448));
    --qz-pill-w: calc(var(--qz-row-pitch) * 0.6896552);
    gap: calc(var(--qz-row-pitch) - var(--qz-pill-w));
  }
  .qz-q {
    width: var(--qz-pill-w);
    flex: 0 1 var(--qz-pill-w);
    min-width: 0;
  }
  .qz-qbox { width: var(--qz-pill-w); }
  /* The check is a 40-box viewBox drawn 1:1, pulled up so it centres on the 28
     pill. Both values now track the pill: at the design's 40 this is width 40 /
     top -6, which is the authored pair. */
  .qz-qbox img {
    width: var(--qz-pill-w);
    height: var(--qz-pill-w);
    top: calc((1.75rem - var(--qz-pill-w)) / 2);
  }
  /* The connector spans one gap, from the pill's own right edge — the same two
     derived values, so the line and the gap cannot drift apart. */
  .qz-q:not(:last-child)::after {
    left: var(--qz-pill-w);
    width: calc(var(--qz-row-pitch) - var(--qz-pill-w));
  }
}

/* --- 2. the reviews line was un-hidden on a phone -------------------------
   The 428 frame keeps only the Trustpilot lockup in its footer row, which
   quote-wizard.css states as `.qz-rev { display: none }` inside its mobile
   block. The two-line clamp added above for desktop is `.qz-side-bot > .qz-rev`
   — (0,2,0) against that rule's (0,1,0) — so its `display: -webkit-box` won at
   every width and put the sentence back on the phone.

   Restored there, it is a flex item next to two fixed-width images in a 225
   row, so it resolves to width 0 and its <b> wraps to one character per line:
   measured 317 tall, bottom edge at y1430 inside a 926 frame. That single
   invisible element is the entire 1134 scrollHeight.

   The clamp is desktop's concern anyway — its own comment reasons about the
   desktop rule's bottom 107 and top 67 — so it is scoped there and the frame's
   own `display: none` governs the phone again. */
@media (min-width: 768px) {
  .qz-side-bot > .qz-rev {
    min-width: 0;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
  }
}

/* --- 3. Next is pinned to a DESKTOP y at every width ----------------------
   `.qz-actions` carries one `top: 43.3125rem`, chosen as `.qz-cta-map`'s own
   place on the 1920 canvas's 930-tall pane. The phone frame is 926 tall, so the
   same number lands at 693 — mid-frame, with 125 of dead space under it and no
   relationship to the content above. That is the report's "the next button is
   fixed not responsive".

   The frame's footer is the right anchor. Measured, that footer row occupies
   845..894 rather than Figma's 870..894 — the desktop `height: auto` above lets
   it grow upward — so bottom 104 puts the 52-tall Next row at 770..822 and
   keeps 23 clear of it. Being a `bottom` it holds that relationship at every
   phone width instead of drifting with the canvas.

   `.qz-roof .qz-actions` is (0,2,0) and makes the row `position: static` so the
   roof step can flow; a static box ignores `bottom`, so that step is unaffected
   and keeps Next in flow under its map. */
@media (max-width: 767.98px) {
  .qz-actions {
    top: auto;
    /* 4.75rem, not 6.5. The design draws NO CTA on the option-card steps — its
       own step 7 has none, verified against the reference build — so this row is
       the platform's own addition and has to fit in the band the design leaves
       between the card grid and the Trustpilot lockup.

       Measured at 390 on the usage step, whose 2x2 grid is the tallest content
       in the journey (and byte-identical to the reference's, 385..712):

           grid bottom 712   footer top 793   band 81   row 47

       At 6.5rem the row sat at 702..749 and the grid ran 10 into it — the
       reported overlap, present at every width (8 at 320, 11 at 428). At 4.75rem
       it sits at 728..775: 16 clear of the grid and 18 clear of the footer, so
       the band is shared rather than fought over. */
    bottom: 4.75rem;
  }

  /* The typed field, 1.5rem under the rail group — the phone canvas puts the
     rail at 30.5 on a 3.125 group, so 35.125, and its own 4.72 ends at 39.85
     against a Next row that lands around 50. The captions are hidden at this
     width, so the field is the only figure on screen besides the readout. */
  .qz-kwh-entry { top: 35.125rem; }
  .qz-kwh-note { width: 24.25rem; }
}

/* ==========================================================================
   THE TARIFFS STEP'S NEXT ROW DRIFTS INTO ITS OWN FORM ON DESKTOP
   --------------------------------------------------------------------------
   Found while matching this step's field to step 1's, and it is not caused by
   that: the two anchors here have never agreed. `.qz-fields` hangs off the pane
   MIDLINE (`top: calc(50% - 9rem)`), while `.qz-actions` carries a fixed canvas
   y (`top: 43.3125rem`). quote-wizard.css makes the root font-size
   `min(100vw / 120, 100vh / 63.125)`, so on any window shorter than the design's
   1010 the scale goes HEIGHT-bound — and from that point the midline tracks the
   viewport while the fixed rem tracks the root. They close on each other.

   Measured on the running build, fields-bottom minus Next-top:

       1920x1010    0     the design canvas — they just clear
       1366x768     9     Next is already inside the checkbox list
       1440x900    54     and well into it here

   Every other question step is immune: their content is option cards at fixed
   canvas tops, so both anchors are the same kind. This is the one step whose
   content is a form measured from the middle, which is why it is scoped to it
   rather than fixed on `.qz-actions` — no other step's Next moves.

   The fix is the one below: both anchors become the pane MIDLINE, so they scale
   together and the drift cannot happen. This step needs a larger offset than the
   shared one, because its form is both the tallest content in the journey and the
   only content whose height is not fixed: the labels and the three checkbox lines
   wrap inside a fixed 28.25rem column, and how many lines they take depends on the
   root size. Measured across the whole root range the wizard can reach — 6.4px at
   768 wide up to 16px at 1920 — the form runs 366 units tall at the top of that
   range and 408 at the bottom, ending at midline +264 in the worst case.

   +288 is that worst case plus 24 clear, and it holds at every size measured
   rather than only at the canvas. The strings are fixed in
   SolarQuestionnaire.tsx, not tenant-authored, so the range really is bounded.
   ========================================================================== */

/* ==========================================================================
   THE QUESTION STEPS' CONTENT GROUP IS STRANDED AT THE TOP OF THE PANE
   --------------------------------------------------------------------------
   Reported as "the Next button is fixed, not responsive", and Next is where you
   see it — but it is the last item of a group that is stranded as a whole, so
   moving Next alone would only tear it off its own cards.

   The mechanism. quote-wizard.css scales the root by
   `min(100vw / 120, 100vh / 63.125)`, i.e. by whichever axis binds. On a narrow
   window WIDTH binds, so the design shrinks with the width while `.qz-shell`
   keeps `height: 100vh` — the pane stays as tall as the viewport while its
   contents get smaller. Heading, card grid and Next are pinned to canvas TOPS;
   the helper, the can't-find line and the dots are pinned to the BOTTOM. Every
   unit of surplus height therefore pools in one place: the gap under Next.

   Measured on the running build, that gap:

       1920x1010    106     the design canvas — correct
       1440x900     222
       1024x768     286
        768x1024    662     Next is a 21px sliver in the top quarter of the pane
        820x1180    794

   The design already answers this for step 1, and says so: Figma pins the search
   card CENTER/CENTER and quote-wizard.css notes the spare height is "absorbed by
   the centred search card and the bottom-anchored footer". Step 1 is the one
   screen that has never had this problem, for exactly that reason. Figma marks
   the option grid CENTER/CENTER too — the desktop port just wrote its literal
   `top` instead, which is what pins it.

   So this applies the same treatment to the rest of the group: every offset
   below is the design's OWN pane coordinate re-expressed against the 930 pane's
   midline of 465, so at a 1010-tall window every one resolves to the identical
   pixel and the approved render does not move. Away from 1010 the group stays
   centred and Next keeps its authored 5.33rem gap under the cards.

   The footer band — .qz-helper, .qz-cant, .qz-dots — is deliberately NOT touched.
   Those are bottom-anchored on purpose (see their own notes in quote-wizard.css)
   and that is the other half of the design's answer.
   ========================================================================== */
@media (min-width: 767.99px) {
  /* headings — 191 / 167 / 294 against the midline */
  .qz-h { top: calc(50% - 17.125rem); }
  .qz-h-floors { top: calc(50% - 18.625rem); }
  .qz-h-kwh { top: calc(50% - 10.6875rem); }

  /* option grids — 335, and D04's two rows at 311 / 473 */
  .qz-grid { top: calc(50% - 8.125rem); }
  .qz-grid-2 { top: calc(50% - 9.625rem); }
  .qz-grid-2b { top: calc(50% + 0.5rem); }

  /* the kWh group — 438 — and the CTA the design draws for that step, 543 */
  .qz-slider { top: calc(50% - 1.6875rem); }
  .qz-cta-kwh { top: calc(50% + 4.875rem); }

  /* the typed field, 1.5rem under the rail group (which ends at +1.6875) —
     its own 4.72 tall block then clears Next at +14.25 by over 6 */
  .qz-kwh-entry { top: calc(50% + 3.1875rem); }

  /* Next — 693, which is `.qz-cta-map`'s own y and where this row already sat */
  .qz-actions { top: calc(50% + 14.25rem); }

  /* the tariffs form's own, taller offset — see the block above */
  .qz-step:has(.qz-fields) .qz-actions { top: calc(50% + 18rem); }

  /* `.qz-h-address` / `.qz-cta-address` / `.qz-search` are already midline-anchored
     in the design and are left exactly as they are. The roof step is not affected
     either: `.qz-roof .qz-h` and `.qz-roof .qz-actions` are (0,2,0) and make those
     `position: static` so the step can flow, which outranks every rule here. */
}

/* --- 4. the tariffs step's helper is the one with no mobile rule -----------
   Every mobile `.qz-helper` in quote-wizard.css is step-scoped — `.qz-s3` … 
   `.qz-s9` — and `q9_energy` deliberately takes no `qz-sN` class, being one of
   the two screens the design never drew. So this is the only helper in the
   journey that keeps its desktop box on a phone: `.qz-helper-narrow`'s 416
   inside the frame's 388 (clipped both ends), bottom-anchored at 40 where the
   Trustpilot row lives (overlapping it). Both are visible in the report's
   first screenshot.

   `:has(.qz-fields)` identifies the step by the only markup unique to it, so no
   component, class or prop changes. 24.25rem is the 388 column every other
   mobile helper uses; bottom 11.5rem ends it at 742, which is 28 clear of the
   Next row at 770 and leaves the fields (ending at 614) 89 above it.
   ========================================================================== */
@media (max-width: 767.98px) {
  .qz-step:has(.qz-fields) .qz-helper {
    width: 24.25rem;
    top: auto;
    /* Tracks the Next row above: it moved down 1.75rem, so this does too and the
       gap between them is unchanged. */
    bottom: 9.75rem;
  }
}

/* --- 5. the roof step flows over the frame's own furniture -----------------
   `.qz-step > .qz-roof` opts this step out of the coordinate canvas — correct,
   an interactive map cannot live on one — and gives it the pane's full box. On
   desktop that box is `.qz-main`, a pane beside the sidebar. On a phone both
   panes collapse ONTO the frame, so the same box is the whole 428x926 and the
   step flows straight over the logo and stepper at the top and the Trustpilot
   row at the bottom: the report's third screenshot has the heading across the
   stepper pills and "Show my prices" on the reviews row.

   The fix is a mobile gutter, in the frame's own units: 168 clears the stepper
   (which ends at 162) and 72 clears the footer row (which starts at 870). The
   auto margins above still centre a short screen inside what is left, and
   `overflow-y: auto` still scrolls the tall one.

   The map card takes the 428 frame's own 260 rather than the viewport-tracked
   clamp, which was resolving to 321 here and pushing the roof-face list and the
   three drawing buttons off the frame. */
@media (max-width: 767.98px) {
  .qz-roof {
    padding: 10.5rem 1.25rem 4.5rem;
    gap: 0.75rem;
  }
  .qz-roof-map .qz-map-frame { height: 16.25rem; }
}

/* --- 6. backstop: the frame cannot be panned ------------------------------
   The four causes above are fixed at source; this makes the class of bug
   non-recurring. The mobile shell IS the 428x926 frame, and the design's stated
   contract is that it does not scroll, so clipping it is the contract rather
   than a workaround. `clip` and not `hidden` on x for the same reason .qz-main
   uses it — it never becomes a scroll container, so it cannot be panned and
   cannot steal client height. */
@media (max-width: 767.98px) {
  .qz-shell { overflow-x: clip; }
}

/* ==========================================================================
   THE HELP LINKS ARE BUTTONS NOW, AND STILL LOOK LIKE THE DESIGN'S LINKS
   --------------------------------------------------------------------------
   "Start Chat", "Book a Call" and "WhatsApp now" open the Request-callback sheet
   rather than navigating, so they are <button>s — which is also the element the
   results and submit screens already use for the same line. They were
   `<a href="#">` with no handler at all: a click pushed a `#` onto the URL and
   did nothing else.

   quote-wizard.css styles this line's links as `.qz-help a` / `.qz-cant a` — the
   ink colour, the 500 weight, the underline and the thickness/offset hover that
   the homepage's own text-link recipe gives it. A <button> matches none of that,
   and base.css strips buttons to `font: inherit; background: none` — so without
   this they would have rendered as plain body text.

   Every declaration below is the design's own, from those two rule blocks, with
   `button` added to the selector list. Nothing new is introduced and the anchors
   still match their original rules, so the one remaining real link on the line —
   "Call: 000 000 000", a number to dial rather than a form to open — is
   unchanged.
   ========================================================================== */
.qz-help button,
.qz-cant button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink);
  font-weight: 500;
  text-decoration: underline;
  text-decoration-thickness: 0.0625rem;
  text-underline-offset: 0.125rem;
  cursor: pointer;
  transition:
    text-decoration-thickness var(--dur-hover) var(--ease-premium),
    text-underline-offset var(--dur-hover) var(--ease-premium);
}
.qz-help button:hover,
.qz-cant button:hover {
  text-decoration-thickness: 0.125rem;
  text-underline-offset: 0.25rem;
}
.qz-help button:focus-visible,
.qz-cant button:focus-visible {
  outline: 0.125rem solid var(--ink);
  outline-offset: 0.1875rem;
  border-radius: 0.1875rem;
}

/* AUTO-DRAWN ROOF — CONFIRMATION NOTICE
   ---------------------------------------------------------------------------
   The roof designer now measures the roof from aerial imagery instead of
   opening on an empty canvas. This sits above the roof-face list and asks the
   homeowner to look at the shape before continuing: they have not agreed to a
   roof they did not draw until they have seen it. Dismissed by either action,
   and never shown for a roof they drew themselves. */
.qz-autodraw-notice {
  padding: 14px 16px;
  border: 1px solid rgba(17, 17, 17, 0.12);
  border-left: 3px solid #7ac143;
  border-radius: 12px;
  background: rgba(122, 193, 67, 0.06);
}

.qz-autodraw-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.35;
  color: #111;
}

.qz-autodraw-body {
  margin: 6px 0 0;
  font-size: 0.85rem;
  line-height: 1.45;
  color: rgba(17, 17, 17, 0.66);
}

.qz-autodraw-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.qz-autodraw-primary,
.qz-autodraw-secondary {
  padding: 9px 18px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.qz-autodraw-primary {
  border: 0;
  background: #111;
  color: #fff;
}

.qz-autodraw-secondary {
  border: 1px solid rgba(17, 17, 17, 0.2);
  background: #fff;
  color: #111;
}

.qz-autodraw-primary:hover,
.qz-autodraw-primary:focus-visible { background: #000; }

.qz-autodraw-secondary:hover,
.qz-autodraw-secondary:focus-visible { background: rgba(17, 17, 17, 0.04); }

.qz-autodraw-primary:focus-visible,
.qz-autodraw-secondary:focus-visible {
  outline: 2px solid #7ac143;
  outline-offset: 2px;
}

/* The shared proposal breakdown emits an <h3> per section; each accordion row
   already names itself, so its blank heading is collapsed rather than left as
   a gap. */
.qr .prose h3:empty,
.qr section > h3:empty { display: none; }

/* ==========================================================================
   STEPPING BACK — the same move, from the other side
   ==========================================================================
   quote-wizard.css parks an inactive step at `translate: 3rem 0`, i.e. every
   step enters from the RIGHT, which is correct going forward and wrong coming
   back: a "back" that slides in from the right reads as another step forward.
   `.qz-step-back` is stamped by QuoteShell for the one render that paints the
   waiting position, so the step comes in from the left instead.

   (0,3,0), which outranks `.qz-step`'s own (0,1,0). The design's
   `:has(~ .qz-step.is-active)` rule is the same weight but cannot match here —
   this build mounts one step, so no sibling is ever active. */
.qz-step.qz-step-back:not(.is-active) { translate: -3rem 0; }

/* THE ONE FRAME THAT MUST NOT ANIMATE.
   `.qz-step` declares its `transition` unconditionally, so the transition runs
   in BOTH directions: rendering the step without `.is-active` does not put it
   at the waiting position, it animates it there over the full 600ms — and the
   next frame, which adds the class back, then animates it home from wherever
   it had got to. Which is roughly nowhere. That is why withholding the class
   on its own produced no visible move.

   The reference build never meets this: its entering step is a different
   element that has sat at `opacity: 0` since mount, so it already starts from
   a real zero. A single element has to be PUT there instead, and this is the
   frame that does it — QuoteShell stamps `.qz-step-enter` for exactly one
   paint, the transition is off so opacity and translate snap, and the class is
   gone by the next frame so the design's own transition drives the entrance.

   (0,2,0), which outranks `.qz-step`'s own (0,1,0). `transition: none` also
   covers `visibility`, whose `allow-discrete` behaviour would otherwise hold
   the old state for the whole duration. */
.qz-step.qz-step-enter { transition: none; }

/* AND THE BACKWARD SLIDE HAS TO OBEY REDUCED MOTION TOO.
   quote-wizard.css's own reduced-motion block drops the slide and keeps the
   dissolve — "which is Figma's own DISSOLVE transition anyway" — with
   `.qz-step { translate: none }`. That is (0,1,0); the back rule above is
   (0,3,0) and would have outranked it, leaving the one direction nobody asked
   for still moving. Restated here at a weight that loses to nothing. */
@media (prefers-reduced-motion: reduce) {
  .qz-step.qz-step-back:not(.is-active) { translate: none; }
}

/* ==========================================================================
   THE TARIFF CHECKBOXES — the design's own chip, not the browser's
   ==========================================================================
   These three were bare `input[type="checkbox"]`, so they rendered in the
   platform's own accent colour — the orange-red box in the screenshot — next
   to a journey whose every other check is an ink chip with a lime tick.

   The recipe is the design's, taken from the two places it already draws this
   control: `.qz-qbox` (the sidebar's completed question) and `.qz-opt-chip`
   (the option card's tick). Both are a `--radius-sm` square that is
   `--qz-chip` when off and `--ink` when on, holding quote-check.svg — which is
   filled #e2fa5a in the asset itself, so the lime comes from the file rather
   than from a colour restated here.

   Drawn as a background-image rather than a pseudo-element: an `appearance:
   none` input's pseudo-elements are not dependably rendered, and the image
   swap is what every browser agrees on. The colour still transitions, so the
   change is not a hard cut. 20 rather than the design's 24 — these sit inline
   against a 0.9rem label, where a 24 chip overpowers the line it belongs to. */
.qz-field-checks input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 1.25rem;
  height: 1.25rem;                                  /* 20 */
  margin: 0;
  border-radius: var(--radius-sm);
  background-color: var(--qz-chip);                 /* #f5f6f0 — off */
  background-repeat: no-repeat;
  background-position: center;
  background-size: 1rem 1rem;                       /* the 16 inner frame */
  cursor: pointer;
  transition: background-color var(--dur-hover) var(--ease-premium);
}
.qz-field-checks input[type="checkbox"]:checked {
  background-color: var(--ink);
  background-image: url("/assets/img/quote-check.svg");
}
.qz-field-checks label:hover input[type="checkbox"]:not(:checked) {
  background-color: rgba(17, 17, 17, 0.1);
}
/* The chip IS the control, so the ring goes round the chip rather than round
   the label — the browser's own outline lands nowhere once appearance is off. */
.qz-field-checks input[type="checkbox"]:focus-visible {
  outline: 0.125rem solid var(--ink);
  outline-offset: 0.125rem;
}
/* the label is a click target for its own box, so the whole row is a pointer */
.qz-field-checks label { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .qz-field-checks input[type="checkbox"] { transition: none; }
}
