/* =============================================================================
   Summit residential estimator — delta on top of summit-site.css
   -----------------------------------------------------------------------------
   The section, the grid, the card, the sliders, the price block and the CTA all
   come from summit-site.css, which is the home page's own stylesheet loaded
   verbatim. This file adds only what the residential version needs on top:
   the step machine inside the card, tiles, add-on rows and the room steps.

   The card keeps the same 512px footprint as the departure-clean estimator, so
   the section occupies the same space on the page.
   ============================================================================= */

/* ---------- head: step title + counter ------------------------------------ */
.est-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.estimator-card .est-head .card-title {
  margin-bottom: 0; display: flex; align-items: center; gap: 9px; min-width: 0;
}
.step-icon { display: flex; flex-shrink: 0; color: var(--button-teal); }
.est-count {
  font-family: var(--font-body); font-size: 12px; letter-spacing: 1.2px;
  color: var(--button-teal); opacity: .8; white-space: nowrap;
  -webkit-font-smoothing: antialiased;
}
.est-progress {
  height: 2px; background: rgba(24, 86, 104, .16);
  margin: 14px 0 22px; overflow: hidden;
}
.est-progress span {
  display: block; height: 100%; width: 0;
  background: var(--medium-teal);
  transition: width .42s cubic-bezier(.4, 0, .2, 1);
}

/* ---------- the morphing step area ---------------------------------------- */
.est-steps { position: relative; }
.est-step[hidden] { display: none; }
.est-step { animation: estStepIn .34s cubic-bezier(.4, 0, .2, 1); }
.est-step.is-back { animation-name: estStepInBack; }
@keyframes estStepIn     { from { opacity: 0; transform: translateX(22px); } to { opacity: 1; transform: none; } }
@keyframes estStepInBack { from { opacity: 0; transform: translateX(-22px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .est-step { animation: none; } }

.step-lede {
  font-size: 14.5px; line-height: 1.55; color: var(--dark-teal);
  opacity: .78; margin-bottom: 18px;
}
.step-note {
  font-size: 12.5px; line-height: 1.5; color: var(--dark-teal);
  opacity: .68; margin-top: 11px;
}

/* ---------- tiles ---------------------------------------------------------- */
.tile-row { display: flex; flex-wrap: wrap; gap: 8px; }
.tile-row.cols-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.tile-row.cols-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.tile-row.cols-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.tile-row.cols-4 .tile { font-size: 13px; padding: 11px 4px; line-height: 1.3; }
.tile-row.days   { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }

.tile {
  font-family: var(--font-body); font-size: 14.5px; color: var(--dark-teal);
  background: var(--white); border: 1px solid rgba(24, 86, 104, 0.3);
  padding: 12px 10px; cursor: pointer; text-align: center; min-height: 46px;
  transition: border-color .16s, background .16s, color .16s;
  -webkit-font-smoothing: antialiased;
}
@media (hover: hover) { .tile:hover { border-color: var(--button-teal); background: rgba(197, 213, 217, .3); } }
.tile.is-on { background: var(--medium-teal); border-color: var(--medium-teal); color: #fff; }
.tile:focus-visible { outline: 2px solid var(--button-teal); outline-offset: 2px; }
.tile-row.days .tile { font-size: 12.5px; padding: 11px 2px; min-height: 42px; }

/* A frequency that is not available for the clean they picked. Same treatment
   the capped slider stops already use: visibly there, visibly not selectable,
   so the customer can see what a regular clean would open up. */
.tile.is-blocked {
  opacity: .38; cursor: not-allowed; background: var(--white);
  border-color: rgba(24, 86, 104, .18); text-decoration: line-through;
  text-decoration-thickness: 1px;
}
@media (hover: hover) {
  .tile.is-blocked:hover { border-color: rgba(24, 86, 104, .18); background: var(--white); }
}

/* the two big options on step one */
.opt-stack { display: grid; gap: 10px; }
.opt {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--white); border: 1px solid rgba(24, 86, 104, 0.3);
  padding: 18px 20px 19px; transition: border-color .16s, background .16s;
  font-family: var(--font-body);
}
@media (hover: hover) { .opt:hover { border-color: var(--button-teal); background: rgba(197, 213, 217, .26); } }
.opt.is-on { background: var(--medium-teal); border-color: var(--medium-teal); }
.opt-title {
  display: block; font-family: var(--font-heading); font-size: 25px; line-height: 1.1;
  color: var(--medium-teal); margin-bottom: 6px; -webkit-font-smoothing: antialiased;
}
.opt.is-on .opt-title { color: #fff; }
.opt-sub { display: block; font-size: 13.5px; line-height: 1.5; color: var(--dark-teal); opacity: .78; }
.opt.is-on .opt-sub { color: #fff; opacity: .88; }
.opt:focus-visible { outline: 2px solid var(--button-teal); outline-offset: 2px; }

/* ---------- groups and labels ---------------------------------------------- */
.mini-group { margin-top: 24px; }
.mini-group[hidden] { display: none; }
/* summit-site.css gives .est-control a bottom margin but no top one, so a slider
   sitting straight under a tile grid collides with its 25px value label. */
.est-step .est-control { margin-top: 26px; }
.est-step .est-control:first-child { margin-top: 0; }
.partial-wrap .est-control:first-child { margin-top: 0; }
.mini-label {
  display: flex; align-items: center; gap: 7px;
  font-family: var(--font-body); font-size: 15px; color: var(--dark-teal);
  margin-bottom: 11px; -webkit-font-smoothing: antialiased;
}
.inline-icon { color: var(--button-teal); flex-shrink: 0; pointer-events: none; }

/* Rooms-to-clean sliders: the stretch above the property's own size is dead
   track, and its stops are unpickable rather than silently snapping back. */
input[type="range"].est-slider.is-capped {
  background: linear-gradient(to right,
    var(--medium-teal) 0 var(--fill),
    var(--light-bg) var(--fill) var(--cap),
    rgba(24, 86, 104, .07) var(--cap) 100%);
}
.est-stops button.is-blocked {
  color: var(--dark-teal);
  opacity: .26;
  cursor: not-allowed;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
@media (hover: hover) { .est-stops button.is-blocked:hover { color: var(--dark-teal); } }

/* A slider nobody has touched: the label prompts instead of asserting a number.
   Body font, muted — so it never reads as an answer we already have. */
.est-control-head .value.is-unset {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--button-teal);
  opacity: .75;
  letter-spacing: .3px;
}

/* square footage: a statement derived from the sheet, never an input */
.sqft-note {
  font-size: 12.5px; line-height: 1.5; color: var(--medium-teal);
  background: rgba(197, 213, 217, .34);
  border-left: 2px solid var(--button-teal);
  padding: 9px 12px; margin: -4px 0 16px;
}
.sqft-note[hidden] { display: none; }

.check-row {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 14.5px; color: var(--dark-teal); cursor: pointer; line-height: 1.45;
  margin-top: 18px;
}
.check-row input { width: 18px; height: 18px; margin-top: 1px; flex-shrink: 0; accent-color: var(--medium-teal); }

.partial-wrap[hidden] { display: none; }
.partial-wrap { margin-top: 22px; padding-top: 20px; border-top: 1px solid rgba(24, 86, 104, .14); }

/* ---------- a room step ---------------------------------------------------- */
.room-includes {
  background: rgba(197, 213, 217, .28);
  padding: 13px 15px 14px; margin-bottom: 16px;
}
.room-col-title {
  font-family: var(--font-body); font-size: 11.5px; letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--button-teal); margin-bottom: 11px;
  -webkit-font-smoothing: antialiased;
}
/* Kelvin read this on a phone and said it "gets lost… pretty small text", and
   Kirsty pre-authorised the height it costs to fix ("I don't mind if that means
   more of a scroll"). One ticked line each, at the size of the add-ons below. */
.incl-list { list-style: none; margin: 0; padding: 0; }
.incl-list li {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 14px; line-height: 1.45; color: var(--dark-teal);
  padding: 3px 0;
}
.incl-list li svg { flex-shrink: 0; margin-top: 2px; color: var(--button-teal); }
.room-none { font-size: 13px; line-height: 1.5; color: var(--dark-teal); opacity: .75; }

/* One line per add-on: tick, label, price hard right. Notes and the quantity
   stepper only appear when they are needed, which keeps the card compact. */
.addon {
  border: 1px solid rgba(24, 86, 104, .16);
  margin-bottom: 6px;
  transition: border-color .16s, background .16s;
}
.addon.is-on { border-color: var(--button-teal); background: rgba(197, 213, 217, .22); }
/* The padding lives on the LABEL, not the row, so the whole row is tappable
   rather than just the 20px line of text inside it. */
.addon-main {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 11px 12px; min-height: 44px; box-sizing: border-box;
}
.addon-main input { width: 17px; height: 17px; flex-shrink: 0; accent-color: var(--medium-teal); }
.addon-text {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex: 1; min-width: 0;
}
.addon-label { font-size: 14px; line-height: 1.35; color: var(--dark-teal); }
.addon-price { font-size: 12.5px; color: var(--button-teal); white-space: nowrap; flex-shrink: 0; }
.addon-note {
  font-size: 12px; line-height: 1.45; color: var(--dark-teal); opacity: .68;
  margin: 0; padding: 0 12px 10px 39px;
  display: flex; align-items: flex-start; gap: 5px;
}
.addon-main + .addon-note { margin-top: -4px; }
.addon-note svg { flex-shrink: 0; margin-top: 2px; }
.addon-ladder { opacity: .88; color: var(--medium-teal); }

.addon-qty {
  display: flex; align-items: center; gap: 7px;
  margin: 0; padding: 0 12px 11px 39px; font-size: 13px; color: var(--dark-teal);
}
.addon-main + .addon-qty { margin-top: -2px; }
.addon-qty[hidden] { display: none; }
.qty-btn {
  width: 29px; height: 29px; flex-shrink: 0;
  border: 1px solid rgba(24, 86, 104, .35); background: #fff;
  color: var(--medium-teal); font-size: 16px; line-height: 1; cursor: pointer;
  transition: background .15s, border-color .15s;
}
@media (hover: hover) { .qty-btn:hover { background: var(--light-bg); border-color: var(--button-teal); } }
.qty-input {
  width: 50px; height: 29px; text-align: center;
  border: 1px solid rgba(24, 86, 104, .35);
  font-family: var(--font-body); font-size: 15px; color: var(--dark-teal);
  background: #fff; -moz-appearance: textfield;
}
.qty-input::-webkit-outer-spin-button, .qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-unit { opacity: .7; }

/* ---------- deep clean: what it replaces the add-on steps with -------------- */
.deep-panel { background: rgba(197, 213, 217, .3); padding: 17px 19px 18px; }
.deep-panel h4 {
  font-family: var(--font-heading); font-weight: 400; font-size: 21px;
  color: var(--medium-teal); margin-bottom: 8px; -webkit-font-smoothing: antialiased;
}
.deep-panel p { font-size: 13.5px; line-height: 1.55; color: var(--dark-teal); }

/* ---------- deep clean: the room steps, read-only -------------------------
   A deep clean runs the SAME five room screens as a regular one, so it reuses
   .room-includes / .room-col-title / .addon wholesale. The only difference is
   that nothing is pickable: the checkbox is replaced by a tick and the price by
   the word Included. Keeping the row identical in every other respect is the
   point — it is the same service, already paid for.
   -------------------------------------------------------------------------- */
.addon.is-included { background: rgba(197, 213, 217, .18); }
.addon.is-included .addon-main { cursor: default; align-items: flex-start; }
.addon-tick { flex-shrink: 0; color: var(--button-teal); margin-top: 1px; }
.addon.is-included .addon-price {
  color: var(--button-teal); opacity: .9; white-space: nowrap;
}


/* ---------- date + fields --------------------------------------------------- */
.date-row { display: flex; align-items: center; gap: 11px; }
/* The field carries its own width: a native date input with no value collapses
   to a narrow empty box on iOS Safari. */
.date-field { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.date-row input[type="date"] {
  display: block; width: 100%; min-width: 0; box-sizing: border-box;
  height: 46px; border: 1px solid rgba(24, 86, 104, .4); padding: 0 12px;
  font-family: var(--font-body); font-size: 16px; color: var(--dark-teal); background: #fff;
  -webkit-appearance: none; appearance: none;
}
/* Our own placeholder, masking whatever the browser does with an empty value.
   z-index because a form control paints above ordinary content regardless of
   DOM order, so without it Safari's own grey "yyyy-mm-dd" shows through. */
.date-ph {
  position: absolute; inset: 1px 1px 1px 1px; z-index: 2;
  display: flex; align-items: center; padding-left: 12px;
  background: #fff; pointer-events: none;
  font-family: var(--font-body); font-size: 16px;
  color: var(--dark-teal); opacity: .5;
}
.date-field.has-value .date-ph { display: none; }
/* Belt and braces: drop the native empty text entirely while ours is showing. */
.date-field:not(.has-value) input[type="date"]::-webkit-datetime-edit { opacity: 0; }
/* iOS Safari renders the chosen date inside ::-webkit-date-and-time-value and
   CENTRES it, which leaves this the only field on the card whose text is not
   left aligned. Desktop Safari already left aligns, so these are no-ops there. */
.date-row input[type="date"] { text-align: left; }
.date-row input[type="date"]::-webkit-date-and-time-value { text-align: left; width: 100%; }
.date-row input[type="date"]::-webkit-datetime-edit { text-align: left; }
.link-btn {
  background: none; border: none; cursor: pointer;
  padding: 0 8px; min-height: 44px;
  font-family: var(--font-body); font-size: 13px; color: var(--button-teal);
  text-decoration: underline; text-underline-offset: 3px;
}
.link-btn:hover { color: var(--dark-teal); }

.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 13px 14px; }
.field { display: flex; flex-direction: column; }
.field.full { grid-column: 1 / -1; }
.field label { font-size: 13.5px; color: var(--dark-teal); margin-bottom: 6px; }
.field input, .field textarea, .field select {
  width: 100%; box-sizing: border-box; height: 42px;
  border: 1px solid rgba(24, 86, 104, .6); padding: 0 12px;
  font-family: var(--font-body); font-size: 16px; color: var(--dark-teal);
  background: #fff; -webkit-appearance: none; appearance: none;
}
.field select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2352808e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center; padding-right: 34px;
}
/* Kirsty asked for "a paragraph text box", so it is sized like one. The fixed
   74px beat the textarea's own rows attribute and gave her two lines. */
.field textarea { height: auto; min-height: 108px; padding: 10px 12px; resize: vertical; }
.field .iti { display: block; width: 100%; }
.field .iti input[type="tel"] { width: 100%; }
.field input:focus, .field textarea:focus, .field select:focus { outline: 2px solid var(--button-teal); outline-offset: -1px; }
.field-grid .check-row { grid-column: 1 / -1; }
/* ⚠️ NOT asked for, and pre-existing since the first build: the terms and privacy
   links were rendering in the browser's default #0000EE with a default underline,
   inside a teal card, as the last thing a customer reads before submitting. */
.check-row a { color: var(--medium-teal); text-decoration: underline; text-underline-offset: 2px; }
@media (hover: hover) { .check-row a:hover { color: var(--dark-teal); } }

.form-error { grid-column: 1 / -1; color: #b42318; font-size: 13.5px; }
.form-error[hidden] { display: none; }

/* ---------- the email check (Kirsty, 2026-08-31) ---------------------------- */
/* Sits under the field it is about. The red edge is what carries at a glance on a
   phone; the sentence is what tells them why. */
.field-note { margin: 6px 0 0; color: #b42318; font-size: 13px; line-height: 1.4; }
.field-note[hidden] { display: none; }
.field input.is-invalid { border-color: #b42318; }
.field input.is-invalid:focus { outline-color: #b42318; }

/* The confirmation line, last thing above Request service. Quiet by design — it is
   a nudge to re-read one field, not a warning. */
.confirm-note {
  margin: 2px 0 0; font-size: 13.5px; line-height: 1.45;
  color: var(--medium-teal, #52808e);
}
.confirm-note[hidden] { display: none; }

/* ---------- foot: price + nav, visible on every step ------------------------ */
.est-foot { margin-top: 4px; }
/* Flexible-availability note: what a Fri-Mon visit would cost, so choosing a
   weekday reads as the saving rather than the weekend reading as a penalty. */
.est-flex-note[hidden] { display: none; }
.est-flex-note {
  font-size: 12.5px; line-height: 1.5; color: var(--dark-teal);
  text-align: center; opacity: .8; margin-top: 10px;
  padding: 9px 12px; background: rgba(197, 213, 217, .38);
}
.est-flex-note strong { color: var(--medium-teal); font-weight: 400; }
.estimator-card .est-result { min-height: 0; padding: 24px 6px 8px; }
.est-result .price-sub { min-height: 15px; }

/* the honest prompt to switch to a deep clean */
.deep-nudge-btn[hidden] { display: none; }
.deep-nudge-btn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: rgba(197, 213, 217, .5); border: none;
  border-left: 3px solid var(--button-teal);
  padding: 12px 14px; margin-top: 12px;
  font-family: var(--font-body); font-size: 13px; line-height: 1.5; color: var(--dark-teal);
  transition: background .16s;
}
.deep-nudge-btn:hover { background: rgba(197, 213, 217, .8); }

.est-nav { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.est-nav .est-cta:disabled { opacity: .38; cursor: default; }
.est-nav .est-cta:disabled:hover { background: var(--medium-teal); }
.est-nav .est-cta { margin-top: 0; flex: 1; }
/* .est-cta and .est-back are both display:inline-flex, which beats the UA
   stylesheet's [hidden] { display: none } — so say it explicitly. Same trap the
   home page hit with .quote-grid. */
.est-nav .est-cta[hidden], .est-nav .est-back[hidden] { display: none; }
/* 44px of hit area on touch; the label itself stays 13px. */
.estimator-card .est-nav .est-back {
  margin-bottom: 0; flex-shrink: 0; min-height: 44px; padding: 0 6px;
}

.est-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- photos on the enquiry -----------------------------------------
   Kirsty's point 6. Each file uploads as it is chosen, so the row shows its own
   state rather than a single bar for the lot — a failed one is visibly failed
   and visibly removable, and it never holds the enquiry up.
   -------------------------------------------------------------------------- */
.photo-field[hidden] { display: none; }
.field-label { font-size: 13.5px; color: var(--dark-teal); margin-bottom: 6px; display: block; }
.photo-add {
  align-self: flex-start; min-height: 44px; padding: 11px 18px;
  border: 1px dashed rgba(24, 86, 104, .5); background: #fff;
  font-family: var(--font-body); font-size: 14px; color: var(--medium-teal);
  cursor: pointer; transition: background .16s, border-color .16s;
}
@media (hover: hover) { .photo-add:hover:not(:disabled) { background: var(--light-bg); border-color: var(--button-teal); } }
.photo-add:disabled { opacity: .5; cursor: default; border-style: solid; }
.photo-hint { font-size: 12.5px; line-height: 1.5; color: var(--dark-teal); opacity: .68; margin-top: 8px; }

.photo-list { list-style: none; margin: 10px 0 0; padding: 0; }
.photo-item {
  display: flex; align-items: center; gap: 11px;
  border: 1px solid rgba(24, 86, 104, .16); padding: 8px 8px 8px 0;
  margin-bottom: 6px;
}
.photo-item.is-failed { border-color: rgba(180, 35, 24, .4); background: rgba(180, 35, 24, .04); }
.photo-thumb {
  width: 46px; height: 46px; flex-shrink: 0; overflow: hidden;
  background: rgba(197, 213, 217, .45); display: block;
}
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.photo-name {
  font-size: 13.5px; color: var(--dark-teal);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.photo-status { font-size: 12px; color: var(--button-teal); }
.photo-item.is-failed .photo-status { color: #b42318; }
.photo-remove {
  flex-shrink: 0; width: 40px; min-height: 40px;
  border: none; background: transparent; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--dark-teal); opacity: .55;
}
@media (hover: hover) { .photo-remove:hover { opacity: 1; } }

/* ---------- preview chrome (not part of the embeddable block) -------------- */
.preview-flag {
  background: var(--dark-teal); color: #fff;
  font-family: var(--font-body); font-size: 13px; line-height: 1.5;
  padding: 11px var(--shell-x); text-align: center; letter-spacing: .3px;
}
.preview-flag strong { letter-spacing: 1.2px; text-transform: uppercase; font-size: 12px; }
.preview-context {
  background: var(--white); padding: 60px var(--shell-x);
  text-align: center;
}
.preview-context p {
  font-family: var(--font-body); font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; color: var(--dark-teal); opacity: .3;
}


/* ---------- the "?" on an add-on row --------------------------------------
   Kelvin asked for a hover; it TOGGLES instead. He asked for it while reading
   the page on a phone, where there is no hover, and the row already carries a
   toggled panel (the quantity stepper) — so this is the shape the component
   already has rather than a second one beside it.
   ⛔ The button sits OUTSIDE the <label>, or asking what an add-on covers would
   tick it.
   -------------------------------------------------------------------------- */
.addon-head { display: flex; align-items: stretch; }
.addon-head .addon-main { flex: 1 1 auto; min-width: 0; }
.addon-help {
  flex: 0 0 auto; width: 38px; min-height: 44px; align-self: stretch;
  border: none; border-left: 1px solid rgba(24, 86, 104, .12);
  background: transparent; cursor: pointer;
  font-family: var(--font-body); font-size: 13px; line-height: 1;
  color: var(--button-teal);
  transition: background .16s, color .16s;
}
.addon-help.is-open { background: rgba(197, 213, 217, .5); color: var(--medium-teal); }
@media (hover: hover) { .addon-help:hover { background: rgba(197, 213, 217, .38); } }
.addon-help:focus-visible { outline: 2px solid var(--button-teal); outline-offset: -2px; }
.addon-info[hidden] { display: none; }
.addon-info {
  padding: 2px 12px 12px 39px;
  border-top: 1px solid rgba(24, 86, 104, .1);
  margin-top: -1px;
}
.addon-info p {
  font-size: 12.5px; line-height: 1.6; color: var(--dark-teal); opacity: .82;
  padding-top: 10px;
}

/* ---------- once, or every clean ------------------------------------------
   Kelvin, 2026-08-30. Inline on the add-on, appearing as it is ticked, and only
   on a recurring clean — on a one off the two answers are the same answer.
   -------------------------------------------------------------------------- */
.addon-when[hidden] { display: none; }
.addon-when {
  display: flex; align-items: center; flex-wrap: wrap; gap: 7px;
  margin: 0; padding: 0 12px 11px 39px;
}
.addon-main + .addon-when, .addon-qty + .addon-when { margin-top: -2px; }
.when-label { font-size: 13px; color: var(--dark-teal); margin-right: 2px; }
.when-btn {
  min-height: 34px; padding: 7px 13px;
  border: 1px solid rgba(24, 86, 104, .35); background: #fff;
  font-family: var(--font-body); font-size: 12.5px; line-height: 1.2;
  color: var(--dark-teal); cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.when-btn.is-on {
  border-color: var(--medium-teal); background: var(--medium-teal); color: var(--white);
}
@media (hover: hover) {
  .when-btn:not(.is-on):hover { background: var(--light-bg); border-color: var(--button-teal); }
}
.addon-vary[hidden] { display: none; }
.addon-vary { margin-top: -4px; margin-bottom: 12px; }

/* ---------- no total to sit an add-on price against ------------------------
   James to Kirsty, 2026-08-29: "I will hide the add-on prices whenever there is
   no total to sit them against." The price is REPLACED, not deleted — a figure
   that simply vanishes reads as a bug, and the add-on is still tickable because
   we still want it recorded on the quote.
   -------------------------------------------------------------------------- */
.addon-price.is-no-total {
  white-space: normal; opacity: .62; font-style: italic;
}
.no-total-note[hidden] { display: none; }
.no-total-note {
  margin-top: -4px; margin-bottom: 12px;
  background: rgba(197, 213, 217, .34);
  border-left: 2px solid var(--button-teal);
  padding: 9px 12px; opacity: .85;
}

/* ---------- scope note, duplex line, and the Other field ------------------- */
.scope-note {
  font-size: 12.5px; line-height: 1.55; color: var(--dark-teal);
  background: rgba(197, 213, 217, .34);
  border-left: 2px solid var(--button-teal);
  padding: 10px 13px; margin-bottom: 18px; opacity: .9;
}
.scope-note a { color: var(--medium-teal); text-decoration: underline; }

.other-wrap[hidden] { display: none; }
.other-wrap { margin-top: 14px; }
.other-label { display: block; font-size: 13.5px; color: var(--dark-teal); margin-bottom: 6px; }
.other-input {
  width: 100%; box-sizing: border-box; height: 42px;
  border: 1px solid rgba(24, 86, 104, .6); padding: 0 12px;
  font-family: var(--font-body); font-size: 16px; color: var(--dark-teal);
  background: #fff; -webkit-appearance: none; appearance: none;
}
.other-input:focus { outline: 2px solid var(--button-teal); outline-offset: -1px; }
.other-wrap .step-note { margin-top: 8px; }

/* ---------- the two-line price --------------------------------------------
   "First clean $X - $Y, then $A - $B per clean", shown only when a recurring
   frequency is chosen AND at least one add-on is first-clean-only.
   ⛔ summit-site.css hides .price and .price-sub behind .is-message; these two
   are ours and have to be hidden with them, or a custom quote keeps a "then"
   line under a message.
   -------------------------------------------------------------------------- */
.est-result .price-lead[hidden], .est-result .price-then[hidden] { display: none; }
.est-result.is-message .price-lead, .est-result.is-message .price-then { display: none; }
.est-result .price-lead {
  font-family: var(--font-body); font-size: 11.5px; letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--button-teal);
  margin-bottom: 7px; -webkit-font-smoothing: antialiased;
}
.est-result .price-then {
  font-family: var(--font-body); font-size: 14px; line-height: 1.5;
  color: var(--medium-teal); margin-top: 9px;
}

/* =============================================================================
   RESPONSIVE — the section's own breakpoints come from summit-site.css
   ============================================================================= */
/* Touch: a checkbox row is tapped by its label, so the label needs the height. */
@media (hover: none) {
  .check-row { padding: 12px 0; margin-top: 8px; }
  .addon-note { padding-left: 39px; }
}
/* iOS zooms the page whenever a text field under 16px takes focus — the Other
   description is 16px for the same reason every contact field is. */

@media (max-width: 768px) {
  .tile-row.cols-3 { grid-template-columns: repeat(3, 1fr); }
  .tile-row.days   { grid-template-columns: repeat(7, 1fr); gap: 4px; }
  .tile-row.days .tile { font-size: 11px; padding: 11px 1px; }
  .field-grid { grid-template-columns: 1fr; gap: 16px; }
  .field input, .field select, .other-input { height: 50px; }
  .field textarea { height: auto; min-height: 96px; }
  .field label { font-size: 14.5px; }
  .opt-title { font-size: 22px; }
  .est-nav { gap: 10px; }
  .preview-flag { text-align: left; }
  .preview-context { padding: 34px var(--sec-pad-x); }
}
@media (max-width: 380px) {
  .addon-qty, .addon-note, .addon-info, .addon-when { padding-left: 14px; }
}


/* The h2's manual break, only where it helps. Above 1500px "Our pricing
   estimator / for your Whistler home" is two balanced lines; below that the same
   break orphans "home" on a third line, and at 1300 forces a fourth, so it is
   dropped and the heading wraps to whatever column it is given.
   ⚠️ 1600, not 1500: between the two, "for your Whistler home" still does not fit
   on one line, so the break was buying a widow rather than a clean split. */
@media (max-width: 1599px) {
  .estimator-intro h2 .brk-wide { display: none; }
}

/* ---------- left column: balanced against the card ------------------------
   Jay, 2026-08-26: "the block on the right is obviously lower than the block on
   the left… the first thing that should be seen should be a bit more even."

   ⛔ Not fixable with fixed spacing, because the thing that drives it is how
   many lines the h2 takes: at ~1580px "Our pricing estimator for your Whistler
   home" fits on TWO lines and the column collapses ~180px shorter than the card,
   while at 1440px it takes FOUR and the column is the taller of the two. Any
   margin tuned for one width is wrong at the other.

   So the column stretches to the row height and the callout is pushed to the
   bottom of it. When the card is taller the callout's bottom edge lands on the
   card's; when the column is taller nothing moves. The grid is `align-items:
   start`, so this is the only item that stretches — the card keeps its own
   height and does not become a tall empty box.
   -------------------------------------------------------------------------- */
.estimator-intro {
  align-self: stretch;
  display: flex;
  flex-direction: column;
}
.estimator-intro .estimator-trust { margin-bottom: 0; }
/* The callout sits on the SAME rhythm as everything above it, and the slack the
   card leaves falls BELOW the column's last item (Jay, 2026-08-26: "I'd rather
   the space be there rather than where it is right now").
   It was pushed to the bottom with `margin-top: auto`, which levelled the two
   columns but paid for it with a 128px hole above it on a wide screen — the eye
   reads that as a mistake, not as alignment.

   ⭐ Kirsty, 2026-08-26: "I wonder if there's a way for it to be higher up on the
   page so clients can see both the estimator and the button to call us in one
   glance rather than scrolling?" So it now sits between the intro copy and the
   tick list rather than under them, and the tick list is what the slack falls
   below instead.
   ⛔ That changes the arithmetic: it follows a <p>, which already carries 18px
   under it, and margins do NOT collapse inside a flex column. So the callout's
   own margin is the REMAINDER of the 46px rhythm, not the whole of it, and the
   tick list — which now follows a block with no bottom margin — carries the
   whole 46px itself.
   ⛔ Scoped to 0,2,0. A bare `.estimator-callout` is 0,1,0, the same as the
   `margin-top` in the callout block below it, and the later rule would win. */
.estimator-intro .estimator-callout { margin-top: 28px; }              /* 18 + 28 = 46 */
.estimator-intro .estimator-callout + .estimator-trust { margin-top: 46px; }

/* ---------- left column: "would you rather talk it through?" ---------------
   Kirsty, 2026-08-26. The site's own .btn-cta.btn-call pair, not a button of
   our own, and the site's own 768px switch: CONTACT US scrolls to the form,
   CALL US dials.

   Jay, 2026-08-26, three times, and each note was the same note. Under the
   line it made the column overshoot the card. Pinned right of the line it read
   as an unrelated button on the same row. Boxed but stacked it left half the
   column empty, in type smaller than everything around it, with a button
   smaller than any other button on the site.
   So: copy LEFT, button RIGHT, both centred on the same axis. The heading steps
   up to 26px and the line matches the 17px the intro paragraphs use, and the
   button is the site's full 240x60 at 24px, identical to the header's.
   ⚠️ Both brand fonts ship a SINGLE weight (400), so `font-weight:700` here would
   be synthetic bold on a display serif and look broken. The site builds emphasis
   out of SIZE and colour, which is what this does.
   -------------------------------------------------------------------------- */
.estimator-callout {
  /* margin-top:auto above wins on the flex column; this stays as the floor for
     the stacked mobile layout, where there is no slack to absorb. */
  margin-top: 24px;
  background: rgba(197, 213, 217, .32);
  border-left: 3px solid var(--button-teal);
  padding: 24px 26px;
  display: flex; align-items: center; gap: 28px;
}
.callout-copy { flex: 1 1 auto; min-width: 0; }
/* ⛔ `.estimator-callout .callout-title`, not `.callout-title`. The site's own
   `.estimator-intro p` is 0,1,1 and sets 17px, so a bare class at 0,1,0 loses and
   the heading silently renders at body size — which is the exact complaint this
   rule exists to fix. audit.js #42 measures it rather than trusting the file. */
.estimator-callout .callout-title {
  /* Jay asked for this bold, and bold is the right call: the display serif is a
     thin high-contrast face with no bold cut (both brand fonts ship weight 400
     only), so at any size that fits on one line it reads LIGHTER than the 17px
     line beneath it and inverts the hierarchy. 34px fixed the weight and wrapped
     to two lines, making the box taller again. The body face at 600 is what the
     card already uses for its own labels, and it reads as a heading at a glance. */
  font-family: var(--font-body); font-weight: 600; font-size: 19px;
  line-height: 1.25; color: var(--dark-teal); margin: 0 0 6px;
  -webkit-font-smoothing: antialiased;
}
.estimator-callout .callout-line {
  font-size: 17px; line-height: 23.8px; color: var(--dark-teal);
  margin: 0; -webkit-font-smoothing: antialiased;
}
.estimator-callout .btn-cta {
  flex: 0 0 auto; width: 240px; height: 60px; font-size: 24px; padding: 0;
}
/* Below the site's own 768px switch the second button takes over, and the block
   stacks so the tap target can run the full width.
   ⛔ Name the two buttons SEPARATELY. A blanket `.estimator-callout .btn-cta` is
   specificity 0,2,0 and silently beats the site's `.call-desktop{display:none}`
   at 0,1,0, which showed BOTH buttons stacked on every phone. audit.js #33. */
@media (max-width: 900px) {
  .estimator-callout { gap: 20px; padding: 20px 22px; }
  .estimator-callout .btn-cta { width: 200px; font-size: 21px; }
}
@media (max-width: 768px) {
  /* Same 0,2,0 as the stretch rule, or the auto margin follows it into the
     stacked layout where there is no slack for it to absorb. */
  /* 22px total, and 18px of it is already on the paragraph above. The tick list
     below takes the SAME 22px, so the callout sits on one rhythm here too — it
     is mid-column now, and a box with 22px above it and 46px below reads as a
     mistake in exactly the way the desktop version did. */
  .estimator-intro .estimator-callout { margin-top: 4px; }
  .estimator-intro .estimator-callout + .estimator-trust { margin-top: 22px; }
  .estimator-callout { display: block; padding: 18px 20px 20px; }
  .callout-copy { margin-bottom: 16px; }
  .estimator-callout .call-desktop { display: none; }
  .estimator-callout .call-mobile  {
    display: flex; width: 100%; max-width: 340px; height: 56px; font-size: 21px;
  }
}


/* ---------- touch: never zoom the page when they are tapping a control ------
   Jay, 2026-08-26: "if I tap + or - twice quite quickly on mobile, it zooms in
   the whole form… I should be able to tap as quickly as I want."

   `touch-action: manipulation` is pan + pinch MINUS the browser's non-standard
   double-tap-to-zoom, so the fast second tap lands on the button instead of
   being swallowed as a zoom gesture. It also drops the 300ms click delay that
   gesture required, so the steppers feel immediate. Applied at SECTION level so
   it covers the controls the script injects (add-on steppers, slider stops,
   frequency and day tiles) as well as the ones in the markup.

   ⛔⛔ Deliberately NOT done with `user-scalable=no` / `maximum-scale=1` on the
   viewport meta. That is the answer that turns up first and it is the wrong
   one: it kills PINCH zoom as well, which fails WCAG 1.4.4 for anyone who needs
   to magnify, and iOS Safari has ignored it since iOS 10 anyway. Pinch-to-zoom
   still works everywhere on this page.
   -------------------------------------------------------------------------- */
.section-estimator button,
.section-estimator a,
.section-estimator label,
.section-estimator input,
.section-estimator select,
.section-estimator textarea {
  touch-action: manipulation;
}

/* Rapid taps on a stepper otherwise begin a text selection, and on iOS bring up
   the magnifier and the Copy callout. Controls only — the price, the estimate
   message and the body copy stay selectable. */
.qty-btn, .est-stops button, .tile, .opt, .when-btn, .addon-help,
.photo-add, .photo-remove,
.est-cta, .est-back, .link-btn, .deep-nudge-btn {
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}

/* ⛔ The same complaint by a second route: iOS Safari zooms the PAGE whenever a
   text-entry field under 16px takes focus. Every contact field was already 16px;
   the stepper's own number box was 15px, and it sits between the + and the -. */
@media (pointer: coarse) {
  .qty-input { font-size: 16px; }
}

/* ═══ Kelvin's prominence pass, 2026-09-01 ═══════════════════════════════════
   "the name of the type of rooms is not bold enough", "the question marks could
   be more bold", "'clean all bedrooms and bathrooms' more prominent".
   ⛔ All three live HERE, never in summit-site.css — that sheet is the whole
   site, including the home page's own departure-clean estimator, and these
   selectors are scoped so nothing outside this card can be reached. */

/* The step name: "Bedrooms", "Bathrooms", "Kitchen". It was 13px/uppercase in the
   site sheet, which reads as a label rather than the heading of the screen. */
/* ⛔⛔⛔ SIZE AND COLOUR, NEVER font-weight. `SummitBody` ships a SINGLE weight
   (400) — the @font-face in summit-site.css declares only 400 — so `font-weight:
   700` here is SYNTHETIC bold, which is the exact thing the callout block below
   warns about, and `-webkit-text-stroke` is a second synthetic thickening on top
   of it. This site builds emphasis out of size and colour, and that is what
   "more prominence" has to mean on a one-weight brand face.
   ⚠️ I shipped weight+stroke first, because a size bump looked like it had broken
   the callout rhythm two viewports away. It had not: that was the QA measuring
   mid-reveal-animation. Do not re-derive a design decision from a flaky check. */
.estimator-card .est-head .card-title {
  font-size: 15px;
  letter-spacing: 1.5px;
  color: var(--dark-teal);
}

/* The "?" on every add-on. Same 44px hit area, bigger and darker. */
.addon-help {
  font-size: 17px;
  color: var(--medium-teal);
}

/* "Clean all bedrooms and bathrooms" — the one checkbox on the property step that
   changes what is being priced, so it should not read like the terms tickbox. */
.check-row[for="cleanAll"] {
  font-size: 16px;
  color: var(--dark-teal);
}

/* The rooms the price covers, directly under the sub-line. Deliberately quieter
   than the sub-line above it: it repeats the customer's own input back, it is not
   a new fact. */
.est-result .price-rooms {
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--dark-teal);
  opacity: .68;
  letter-spacing: .2px;
  margin-top: 4px;
}
.est-result .price-rooms[hidden] { display: none; }
.est-result.is-message .price-rooms { display: none; }

/* ═══ EMBEDDED ON A SERVICE PAGE ════════════════════════════════════════════
   `.svc-band--flush` sets `padding-top: 0` — it was flush because it used to
   follow the intro band, which supplied the gap itself. With the estimator
   between them, "Our services" ends up welded to the top edge of the white band
   with no breathing room at all (Jay, 2026-09-02: "Our Services is pinned to the
   top of the white section"). Give it back the same top padding every other
   `.svc-band` has, at each of the service page's own breakpoints.
   ⭐ This sheet only loads where the estimator is, so a flush band on any other
   page is untouched. */
.section-estimator + .svc-band--flush { padding-top: 96px; }
@media (max-width: 1024px) { .section-estimator + .svc-band--flush { padding-top: 72px; } }
@media (max-width: 768px)  { .section-estimator + .svc-band--flush { padding-top: 52px; } }

/* ⛔ 360px and under, and only real since the embed. Kirsty's rule is that the call
   button is visible "in one glance rather than scrolling" with the section at the
   top of the viewport. Embedded on the service page, under a sticky mobile header,
   CALL US ended 11px past the fold at 320x700.
   ⚠️ I chased this once before and it WAS a phantom then, because the harness was
   measuring mid-reveal. It is real now: the harness settles first, and the
   estimator no longer auto-scrolls the page on load, so this is the honest
   geometry. Reproducible on both a settled measurement and a screenshot.
   ⛔⛔ Trim the SECTION padding only. Zeroing the callout's own top margin also
   clears the fold and breaks the equal-gaps-around-the-callout rule (22 above,
   28 below), which is Jay's rhythm rule from round three.
   ⭐ This sheet is not loaded by the home page, so its .section-estimator is untouched. */
@media (max-width: 360px) {
  .section-estimator { padding-top: 26px; }
}

/* ═══ The "?" divider, and the iOS date value ═══════════════════════════════
   Both reported by Jay from a real phone, 2026-09-02. */

/* The note, the ladder line and the quantity block now sit in .addon-col inside
   .addon-head, so the ? button's border-left stretches the full height of the
   card instead of stopping at the bottom of the label. */
.addon-head .addon-col { flex: 1 1 auto; min-width: 0; }

/* ⛔ iOS lays the chosen date out from the TOP of the field rather than centring
   it, so "Sep 16, 2026" sat about 8px high in a 46px box. Chrome centres it
   already, which is why this never showed in QA, and why the fix is scoped to the
   WebKit pseudo-elements rather than the input's own line-height: changing that
   would move it on desktop too.
   ⚠️ This machine cannot run Safari (Playwright refuses WebKit on macOS 12), so
   this is verified on Chrome for no-regression and needs a real iPhone to confirm. */
.date-row input[type="date"]::-webkit-datetime-edit {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0;
}
.date-row input[type="date"]::-webkit-date-and-time-value { margin: 0; }

/* ═══ The chosen date is drawn by US, not by the browser ════════════════════
   See the note in estimator-residential.js. iOS puts the native value ~8px high
   in the box and would not be steered; our own overlay renders identically
   everywhere. The native text is hidden underneath in BOTH states now. */
/* ⛔ SPECIFICITY: the original rule is `.date-field.has-value .date-ph {display:none}`
   at 0,3,0. A plain `.date-field .date-ph` is 0,2,0 and loses, so the overlay
   vanished exactly when it had a value to show. Match the specificity. */
.date-field.has-value .date-ph {
  display: flex;
  opacity: 1;
  /* Leave the right-hand strip clear: Chrome paints its calendar picker icon
     there and a full-width overlay would hide it. */
  right: 40px;
}
.date-row input[type="date"]::-webkit-datetime-edit { opacity: 0; }

/* ⛔ The focus ring has to live on the WRAPPER, not the input. Chrome draws its
   default blue ring on the input itself, and our opaque overlay sits on top of it,
   so all you saw was the fragment poking out beside the calendar icon where the
   overlay stops. Jay spotted it as "some bold blue box on the right".
   ⭐ Keep a visible indicator: removing the outline with nothing in its place would
   leave keyboard users with no idea where they are. */
.date-row input[type="date"]:focus,
.date-row input[type="date"]:focus-visible { outline: none; }
/* ⭐ KEYBOARD ONLY. `:focus-within` fires on a mouse click too, which put a ring
   round the field every time anyone tapped it — noise for a pointer user, and Jay
   rightly asked what it was for. `:focus-visible` is the browser's own judgement of
   "reached by keyboard", so tabbing shows it and clicking does not.
   ⛔ Don't just delete the ring: without one, anyone tabbing through the form has
   no idea which field they are on. That is a WCAG failure, not a style preference. */
.date-field:has(input:focus-visible) {
  outline: 2px solid var(--button-teal);
  outline-offset: 0;
}
/* Anything without :has() still gets an indicator rather than none. */
@supports not selector(:has(*)) {
  .date-field:focus-within { outline: 2px solid var(--button-teal); outline-offset: 0; }
}
