/* ===========================================================================
   Base
   Brand colours are injected as custom properties by includes/header.php,
   which reads them from includes/config.php.
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* Neutrals are steps on the violet ramp rather than straight greys — it keeps
   the page reading as one family. The page sits on the light purple --bg while
   cards stay pure #fff, so every card lifts off the ground without needing a
   heavier border.
   header.php overrides --bg, --bg-soft, --bg-tint, --line and the two dark
   surfaces from config.php; the values here are the fallback for anything that
   loads this sheet on its own. */
:root {
  --ink-2:      #3F3A6E;
  --line:       #DDD6FE;
  --line-soft:  #EDE9FE;
  --bg:         #F5F3FF;   /* light purple — the page ground */
  --bg-soft:    #EDE9FE;   /* soft lavender bands, a step deeper than the ground */
  --bg-tint:    #EDE9FE;   /* lavender wash for hovers and brand chips */
  --sidebar:    #2E1065;   /* deep purple — dark bands, always with #fff text */
  --indigo:     #312E81;   /* dark indigo — the second dark surface */
  --surface:    #FFFFFF;   /* white cards */
  --star:       #c2860f;   /* antique gold stars — 3.1:1 on a white card, where
                              a brighter gold would drop under 3:1 (WCAG 1.4.11) */
  --green:      #0e7c66;
  --radius:     14px;
  --radius-sm:  10px;
  --shadow:     0 1px 2px rgba(30, 27, 75,.05), 0 8px 24px rgba(30, 27, 75,.07);
  --shadow-lg:  0 8px 40px rgba(30, 27, 75,.14);
  --wrap:       1200px;
  /* The page's side gutter. ONE source of truth, because anything that bleeds
     out to the screen edge has to cancel it exactly — see .nav-strip ul. When
     these were typed separately they drifted apart at every breakpoint (24 /
     16 / 14 / 12 against -16 / -14), and at 360px the mismatch put the nav
     strip 2px past the viewport, which was enough to make Chrome widen the
     layout viewport and shove the whole page left. */
  --gutter:     24px;
  --font:       "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

html { -webkit-text-size-adjust: 100%; }

/* Nothing scrolls the page sideways, at any width.

   THE ROOT MUST USE `hidden`, NOT `clip`.

   Overflow set on the root element is propagated to the viewport — that is the
   only reason a rule on <html> can stop the PAGE scrolling at all. But `clip`
   is excluded from that propagation: the viewport keeps its own `auto` and
   carries on scrolling. This rule said `clip` for a long time and therefore
   did nothing, while looking correct in DevTools.

   It matters even with zero overflowing content, because the layout viewport
   can be wider than the visible one (browser zoom, a widened layout viewport
   on Android). The page then pans into that empty gap — everything shifts left
   and a blank strip appears down the right — with nothing on the page actually
   too wide.

   `hidden` on the root does NOT break `position: sticky`: because the value is
   propagated to the viewport, <html> itself computes back to `visible`, so it
   never becomes a scroll container. That is only a danger on <body> and on
   ordinary ancestors, which is why body below keeps `clip`. */
html { max-width: 100%; overflow-x: hidden; }

/* `clip` here, deliberately. body IS an ancestor of the sticky header, and
   `overflow: hidden` on it would make it a scroll container and kill the
   stickiness. `clip` cuts the overflow without creating one. */
body { max-width: 100%; overflow-x: clip; }

/* Belt and braces below the header. <main> is an ordinary element, so clipping
   here always works, and it sits below the sticky header. */
main { overflow-x: clip; }

/* The usual causes, headed off at the source rather than only clipped.
   A grid or flex child defaults to min-width:auto — it refuses to shrink below
   its content, so one long word, a wide table or an un-wrapped URL pushes the
   whole column past the viewport. */
.wrap, .panel, .panel-body, .rail, .rail-card, .checkout-layout > * { min-width: 0; }
.panel-body table { max-width: 100%; }
/* An address, an email or a reference number with no spaces in it. */
.rail-item .ri-name, .rail-item .ri-sub, .field .err, .kv dd { overflow-wrap: anywhere; }

/* The header is sticky, so an anchor jump would land with the section heading
   hidden underneath it. Every linkable section reserves that height. */
#services, #reviews, #write-review { scroll-margin-top: 96px; }
@media (max-width: 640px) { #services, #reviews, #write-review { scroll-margin-top: 78px; } }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a   { color: inherit; text-decoration: none; }

h1, h2, h3, h4 { margin: 0; line-height: 1.25; letter-spacing: -0.02em; font-weight: 700; }
p  { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

button { font: inherit; cursor: pointer; border: 0; background: none; color: inherit; }
input, select, textarea { font: inherit; color: inherit; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

.muted     { color: var(--muted); }
.small     { font-size: 13px; }
.tiny      { font-size: 12px; }
.strike    { text-decoration: line-through; color: var(--muted); }
.center    { text-align: center; }
.hide      { display: none !important; }

/* ------------------------------------------------------------------ icons */
/* Icons are inline SVG from includes/icons.php, not emoji: they inherit
   currentColor, so one icon takes the brand on a white card and white on a
   purple band, and they all share a single stroke weight. */
.ico, .cb-icon, .track-ico { display: inline-flex; align-items: center; line-height: 0; }
.ico svg, .cb-icon svg, .track-ico svg { display: block; }
.promise-item .ico, .rail-usp .ico, .pay-opt .ico { color: var(--brand); }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 22px; border-radius: var(--radius-sm);
  font-weight: 600; font-size: 15px; white-space: nowrap;
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.btn:active { transform: translateY(1px); }

.btn-primary  { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-dark); }

.btn-accent   { background: var(--accent); color: #fff; }
.btn-accent:hover { filter: brightness(.94); }

.btn-outline  { border: 1.5px solid var(--brand); color: var(--brand); background: #fff; }
.btn-outline:hover { background: var(--bg-tint); }

.btn-ghost    { border: 1px solid var(--line); color: var(--ink); background: #fff; }
.btn-ghost:hover { border-color: #C4B5FD; }

.btn-block    { width: 100%; }
.btn-sm       { padding: 9px 16px; font-size: 14px; }
.btn[disabled], .btn.is-disabled { opacity: .5; pointer-events: none; }

/* ---------------------------------------------------------------- ratings */
/* Base size for every star glyph. Without this an unscoped <svg class="star">
   renders at the SVG default size and blows out its line. */
.star { width: 14px; height: 14px; fill: var(--star); flex: none; }
.star.off { fill: #DDD6FE; }

.rating { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; }
.rating .star { width: 14px; height: 14px; fill: var(--star); flex: none; }
.rating strong { font-weight: 600; }
.rating-count { color: var(--muted); font-weight: 400; }

/* ===========================================================================
   Header
   =========================================================================== */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255,255,255,.92);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
}
.header-inner {
  display: flex; align-items: center; gap: 22px;
  height: 84px;
}
.logo { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 19px; letter-spacing: -0.03em; }
.logo-mark {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: #fff; display: grid; place-items: center; font-size: 16px; font-weight: 800;
}
.logo-text small { display: block; font-size: 10.5px; font-weight: 500; color: var(--muted); letter-spacing: 0; margin-top: -3px; }

.loc-pill {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 18px; border: 1px solid var(--line); border-radius: 12px;
  font-size: 15px; color: var(--ink); width: 100%; max-width: 382px; text-align: left;
  background: #fff;
}
.loc-pill:hover { border-color: #C4B5FD; }
.loc-pill .pin   { flex: none; color: var(--ink); }
.loc-pill .caret { flex: none; margin-left: auto; color: var(--ink-2); }
.loc-pill span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The pill carries the FULL address, so it is set smaller and tighter than
   the rest of the header — a whole address at 15px would either wrap the
   header onto two rows or truncate after the house number. */
.loc-pill {
  cursor: pointer;
  padding: 9px 12px;
  font-size: 12.5px;
  line-height: 1.35;
  gap: 8px;
  max-width: 300px;
}
.loc-pill .pin,
.loc-pill .caret { width: 14px; height: 14px; }
.loc-pill .caret { margin-left: 4px; }

/* ---- Location picker modal ---- */
.loc-overlay {
  position: fixed; inset: 0; z-index: 200; display: none;
  background: rgba(30, 27, 75, .55); padding: 20px;
  align-items: center; justify-content: center;
}
.loc-overlay.open { display: flex; }
.loc-modal {
  background: #fff; border-radius: 18px; width: 100%; max-width: 520px;
  max-height: calc(100dvh - 40px); display: flex; flex-direction: column;
  overflow: hidden; box-shadow: 0 24px 70px rgba(30, 27, 75, .3);
}
.loc-modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 18px 22px; border-bottom: 1px solid var(--line);
}
.loc-modal-head h2 { font-size: 17px; margin: 0; }
.loc-x {
  border: 0; background: none; font-size: 26px; line-height: 1;
  color: var(--muted); cursor: pointer; padding: 0 4px;
}
.loc-x:hover { color: var(--ink); }
.loc-modal-body { padding: 18px 22px; overflow-y: auto; }
.loc-gps {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; padding: 13px; border-radius: 11px; cursor: pointer;
  border: 1.5px solid var(--brand); background: #fff; color: var(--brand);
  font: inherit; font-weight: 600; font-size: 15px;
}
.loc-gps:hover { background: var(--brand); color: #fff; }
.loc-or {
  display: flex; align-items: center; gap: 12px;
  margin: 16px 0; color: var(--muted); font-size: 12.5px;
}
.loc-or::before, .loc-or::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}
.loc-search, gmp-place-autocomplete {
  width: 100%; padding: 12px 14px; font: inherit; font-size: 15px;
  border: 1.5px solid var(--line); border-radius: 11px; margin-bottom: 14px;
  display: block;
}
.loc-search:focus { outline: 0; border-color: var(--brand); }
.loc-gps[data-busy] { opacity: .75; cursor: wait; }
.loc-status {
  margin: 10px 0 0; font-size: 13px; color: var(--brand);
  background: #eaf6f2; border-radius: 9px; padding: 9px 12px;
}

.loc-map-wrap { position: relative; border-radius: 12px; overflow: hidden; }
.loc-map { height: 260px; background: var(--bg-soft, #F5F3FF); }

/* Fixed centre pin — the map moves under it, the way delivery apps work.
   One pin only: a draggable marker plus a crosshair is two answers to
   "where do you live". */
.loc-map-wrap::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 18px; height: 18px; margin: -18px 0 0 -9px;
  border-radius: 50% 50% 50% 0; transform: rotate(-45deg);
  background: var(--brand); border: 3px solid #fff; pointer-events: none;
  box-shadow: 0 3px 10px rgba(30, 27, 75, .4); z-index: 2;
}
.loc-drag-hint {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  background: rgba(30, 27, 75, .78); color: #fff; font-size: 11.5px;
  padding: 5px 11px; border-radius: 999px; pointer-events: none;
  white-space: nowrap; z-index: 2;
}
.loc-readout { margin-top: 12px; min-height: 34px; }
.loc-preview { font-size: 13.5px; color: var(--ink); line-height: 1.45; }
.loc-acc { font-size: 12px; color: var(--muted); margin-top: 3px; }
.loc-modal-foot {
  display: flex; gap: 10px; padding: 16px 22px;
  border-top: 1px solid var(--line); background: #fff;
}
.loc-modal-foot .btn { flex: 1; justify-content: center; }

@media (max-width: 560px) {
  .loc-overlay { padding: 0; align-items: flex-end; }
  .loc-modal { max-width: 100%; border-radius: 18px 18px 0 0; max-height: 86dvh; }

  /* Compact on a phone: at the desktop spacing the sheet covered most of the
     screen. Smaller type and padding, and a shorter map — the pin is still
     easy to place, and Confirm stays in reach without scrolling. */
  .loc-modal-head { padding: 12px 16px; }
  .loc-modal-head h2 { font-size: 15.5px; }
  .loc-x { font-size: 24px; }
  .loc-modal-body { padding: 12px 16px; }
  .loc-gps { padding: 10px; font-size: 14px; border-radius: 10px; }
  .loc-status { margin-top: 8px; padding: 7px 10px; font-size: 12px; line-height: 1.4; }
  .loc-or { margin: 10px 0; font-size: 11.5px; }
  .loc-search, gmp-place-autocomplete { padding: 9px 12px; margin-bottom: 10px; }
  .loc-map { height: 160px; }
  .loc-drag-hint { bottom: 8px; padding: 4px 9px; font-size: 10.5px; }
  .loc-readout { margin-top: 8px; min-height: 0; }
  .loc-preview { font-size: 12.5px; line-height: 1.4; }
  .loc-modal-foot { padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); }
  .loc-modal-foot .btn { padding: 10px 14px; font-size: 14px; }
}

/* Field on the left, button on the right, as two items in a row. */
.header-search {
  flex: 1; max-width: 530px;
  display: flex; align-items: stretch; gap: 8px;
}
.search-field { position: relative; flex: 1; min-width: 0; }
.header-search input {
  width: 100%; padding: 14px 16px 14px 48px; font-size: 15px;
  border: 1px solid var(--line); border-radius: 12px;
  background: #fff; outline: none;
}
.header-search input:focus { border-color: var(--brand); }
/* Scoped to .search-field so it cannot catch an icon in the button beside it.
   translate(), not translateY(), so the focus nudge in the micro-interaction
   block below can add an x value without losing the -50% centring. */
.search-field svg { position: absolute; left: 18px; top: 50%; transform: translate(0, -50%); width: 19px; height: 19px; fill: var(--ink-2); }

.header-actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.cart-btn { position: relative; display: flex; align-items: center; gap: 8px;
            padding: 10px 16px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-weight: 600; font-size: 14px; }
.cart-btn:hover { border-color: #C4B5FD; }
.cart-btn svg { width: 18px; height: 18px; fill: var(--ink); }
.cart-badge {
  position: absolute; top: -7px; right: -7px; min-width: 20px; height: 20px; padding: 0 5px;
  background: var(--accent); color: #fff; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; display: grid; place-items: center;
}

.nav-strip { border-bottom: 1px solid var(--line); background: #fff; }
.nav-strip ul { display: flex; gap: 26px; overflow-x: auto; scrollbar-width: none; }
.nav-strip ul::-webkit-scrollbar { display: none; }
.nav-strip a {
  display: block; padding: 13px 0; font-size: 14px; font-weight: 500; color: var(--ink-2);
  border-bottom: 2px solid transparent; white-space: nowrap;
  /* The transparent border still reserves its 2px, so the underline below is
     drawn over that strip and nothing shifts when it appears. */
  position: relative;
}
.nav-strip a:hover { color: var(--brand); }
.nav-strip a.active { color: var(--brand); font-weight: 600; }

/* ===========================================================================
   Header micro-interactions

   Layout, colour, type and spacing are untouched. Every effect below moves
   only transform, box-shadow, opacity or a pseudo-element — none of which
   reflow anything, so nothing in the header shifts as you use it.

   One easing curve throughout: a gentle ease-out that starts quickly and
   settles, with no overshoot. Durations sit between 180ms and 320ms — fast
   enough to feel like a response, slow enough to read as deliberate.
   =========================================================================== */
:root {
  --ease-out-soft: cubic-bezier(.22, .61, .36, 1);
}

/* ---- logo: settles in once, on load ---- */
@keyframes logo-in {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: scale(1); }
}
.site-header .logo {
  animation: logo-in 320ms var(--ease-out-soft) both;
  transform-origin: left center;
}

/* ---- location pill: lifts on hover ---- */
.loc-pill {
  transition: transform 200ms var(--ease-out-soft),
              box-shadow 200ms var(--ease-out-soft),
              border-color 200ms var(--ease-out-soft);
}
.loc-pill:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(30, 27, 75, .10);
}
.loc-pill:active { transform: translateY(0); transition-duration: 90ms; }

/* ---- search: brighter edge on hover, a ring on focus ----
   The ring is a box-shadow, so the field never changes size and the header
   never reflows around it. */
.header-search input {
  transition: border-color 200ms var(--ease-out-soft),
              box-shadow 220ms var(--ease-out-soft);
}
.header-search:hover input:not(:focus) { border-color: #C4B5FD; }
.header-search input:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 4px rgba(109, 40, 217, .12);
}

/* ---- search icon: steps aside as the field takes focus ---- */
.header-search svg {
  transition: transform 220ms var(--ease-out-soft), fill 220ms var(--ease-out-soft);
}
.header-search:focus-within svg { transform: translate(2px, -50%); fill: var(--brand); }

/* ---- the Search button, beside the field ----
   The gradient is the same pair the logo mark uses, which ties the two ends
   of the header together. No icon: the word is the whole button, and an arrow
   sliding out from under it read as a glitch rather than a flourish.

   The movement is a lift, matching the cart button and the location pill, so
   every pressable thing in the header behaves the same way. */
.search-go {
  flex: none; display: flex; align-items: center; justify-content: center;
  padding: 0 22px; border: 0; border-radius: 12px; cursor: pointer;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: #fff; font: inherit; font-size: 14px; font-weight: 700;
  letter-spacing: .01em; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(46, 16, 101, .18);
  transition: filter 200ms var(--ease-out-soft),
              box-shadow 200ms var(--ease-out-soft),
              transform 200ms var(--ease-out-soft);
}
.search-go:hover {
  transform: translateY(-2px);
  filter: brightness(1.1);
  box-shadow: 0 6px 18px rgba(46, 16, 101, .28);
}
.search-go:active { transform: translateY(0); transition-duration: 90ms; }
.search-go:focus-visible { outline: 2px solid var(--brand-dark); outline-offset: 3px; }

/* ---- cart: lifts and grows a hair ---- */
.cart-btn {
  transition: transform 200ms var(--ease-out-soft),
              box-shadow 200ms var(--ease-out-soft),
              border-color 200ms var(--ease-out-soft);
}
.cart-btn:hover {
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 6px 18px rgba(30, 27, 75, .12);
}
.cart-btn:active { transform: translateY(0) scale(1); transition-duration: 90ms; }

/* ---- cart icon: a slight tilt, nothing more ---- */
.cart-btn svg { transition: transform 240ms var(--ease-out-soft); }
.cart-btn:hover svg { transform: rotate(-3deg); }

/* ---- nav: underline grows from the centre ----
   A scaled pseudo-element rather than an animated width, so it is composited
   on the GPU and cannot nudge the text above it. */
.nav-strip a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--brand); border-radius: 2px;
  transform: scaleX(0); transform-origin: center;
  transition: transform 260ms var(--ease-out-soft);
}
.nav-strip a:hover::after,
.nav-strip a:focus-visible::after { transform: scaleX(1); }
/* The current page keeps its underline drawn — same element, already open. */
.nav-strip a.active::after { transform: scaleX(1); }

/* ---- header: a shadow that only exists once you have scrolled ----
   .is-scrolled is set by the small script at the end of includes/header.php. */
.site-header {
  transition: box-shadow 260ms var(--ease-out-soft);
}
.site-header.is-scrolled { box-shadow: 0 4px 18px rgba(30, 27, 75, .07); }

/* ---- opt out ----
   Someone who has asked their system to reduce motion gets the same header
   with none of the movement: no load animation, no lift, no growing rule.
   The focus ring stays, because that one is feedback rather than decoration. */
@media (prefers-reduced-motion: reduce) {
  .site-header .logo { animation: none; }
  .loc-pill,
  .cart-btn,
  .cart-btn svg,
  .search-field svg,
  .search-go,
  .nav-strip a::after { transition: none; }
  .loc-pill:hover,
  .cart-btn:hover,
  .search-go:hover { transform: none; }
  .cart-btn:hover svg { transform: none; }
  .header-search:focus-within svg { transform: translate(0, -50%); }
  .nav-strip a:hover::after { transform: scaleX(1); }
}

/* ===========================================================================
   Hero — entrance, then hover

   Two separate things live here and they never overlap:

     1. A ONE-TIME entrance on load. `both` fill holds the final frame, so
        once it has run the hero is completely static. Nothing loops.
     2. Hover effects, which only exist while the pointer is on a card.

   Every entrance runs 620ms on one ease-out curve, staggered 100ms apart.
   =========================================================================== */
:root {
  --ease-hero: cubic-bezier(.16, .84, .44, 1);
}

@keyframes hero-rise {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-rise-lg {
  from { opacity: 0; transform: translateY(25px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-pop {
  from { opacity: 0; transform: scale(.92); }
  to   { opacity: 1; transform: none; }
}

.hero h1.hero-in { animation: hero-rise 620ms var(--ease-hero) both; }
.hero .cat-card.hero-in {
  animation: hero-rise-lg 620ms var(--ease-hero) both;
  animation-delay: 150ms;
}

/* The three tiles arrive left to right, once the card that holds them has. */
.cat-grid .cat-tile {
  animation: hero-pop 620ms var(--ease-hero) both;
  animation-delay: calc(300ms + var(--i, 0) * 100ms);
}

/* Top-left, top-right, bottom-left, bottom-right — DOM order is grid order,
   so one counter drives both. */
.hero-collage .collage-cell.hero-in {
  animation: hero-rise 620ms var(--ease-hero) both;
  animation-delay: calc(220ms + var(--i, 0) * 100ms);
}

/* --------------------------------------------------- appliance card hover */
.cat-tile { cursor: pointer; }
.cat-tile.soon { cursor: default; }

.cat-tile .emoji.has-img {
  transition: transform 300ms var(--ease-hero), box-shadow 300ms var(--ease-hero);
}
.cat-tile:not(.soon):hover .emoji.has-img {
  transform: translateY(-4px);
  box-shadow: 0 12px 22px rgba(30, 27, 75, .13);
}

/* The artwork inside grows; the coloured rounded square behind it does not,
   because only the drawing group is scaled and never the backing <rect>. */
.cat-tile .emoji svg { display: block; width: 100%; height: auto; }
.cat-tile .emoji svg > g {
  transform-box: view-box; transform-origin: 45.5px 45.5px;
  transition: transform 300ms var(--ease-hero);
}
.cat-tile:not(.soon):hover .emoji svg > g { transform: translateY(-2px) scale(1.04); }

/* ---- refrigerator: frost sparkles wake up ---- */
.ic-sparks .ic-spark {
  opacity: 0; transform-box: view-box; transform-origin: center;
  transition: opacity 280ms var(--ease-hero), transform 280ms var(--ease-hero);
  transform: scale(.4);
}
.cat-tile:hover .ic-sparks .ic-spark { opacity: .9; transform: scale(1); }
.cat-tile:hover .ic-sparks .ic-spark:nth-child(2) { transition-delay: 70ms; }
.cat-tile:hover .ic-sparks .ic-spark:nth-child(3) { transition-delay: 140ms; }

/* ---- washing machine: the water turns inside the drum ---- */
@keyframes drum-turn { to { transform: rotate(360deg); } }
.ic-drum { transform-box: view-box; transform-origin: 45.5px 54px; }
/* Only while hovered — idle, there is no animation on the element at all. */
.cat-tile:hover .ic-drum { animation: drum-turn 3.2s linear infinite; }

/* ---- microwave: the readout pulses ---- */
@keyframes display-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}
.ic-display { transition: opacity 300ms var(--ease-hero); }
.cat-tile:hover .ic-display { animation: display-pulse 1.6s ease-in-out infinite; }

/* -------------------------------------------------- appliance card: picked
   Pressing a tile confirms the choice before the page changes: a press-in and
   a highlight ring, ~320ms, then the browser navigates. Short enough to feel
   instant, long enough to register.

   There was a third part — a tick badge in the corner of the artwork. It has
   been dropped: sitting over the illustration it read as a checkbox waiting to
   be ticked rather than as confirmation of a tap already made. */
.cat-tile .emoji.has-img { position: relative; }

/* The press. Deliberately quick — a slow squash feels like lag, not feedback. */
.cat-tile.is-picked .emoji.has-img {
  transform: scale(.98);
  transition-duration: 100ms;
  box-shadow: 0 0 0 2px var(--brand), 0 10px 24px rgba(46, 16, 101, .20);
}
/* Held while the browser is on its way to the next page, so the tile does not
   spring back and look like the click was dropped. */
.cat-tile.is-leaving .emoji.has-img { transform: scale(1); transition-duration: 220ms; }

/* Coming back, the tile that was chosen is marked without any animation. */
.cat-tile.was-picked .emoji.has-img { box-shadow: 0 0 0 2px var(--brand); }

/* ------------------------------------------------------ collage card hover */
.collage-cell {
  transition: box-shadow 400ms var(--ease-hero);
  /* A ring rather than a border, so no 1px is added to the box and the grid
     cannot shift when it appears. */
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
}
.collage-cell:hover {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .30),
              0 14px 30px rgba(30, 27, 75, .18);
}
.collage-cell img,
.collage-cell video { transition: transform 420ms var(--ease-hero); }
.collage-cell:hover img,
.collage-cell:hover video { transform: scale(1.04); }

/* The scrim deepens rather than appearing, so the label never flickers. */
.collage-cell.has-photo::after,
.collage-cell.has-video::after { transition: opacity 400ms var(--ease-hero); opacity: .82; }
.collage-cell:hover.has-photo::after,
.collage-cell:hover.has-video::after { opacity: 1; }

.collage-cell .c-label {
  opacity: .88;
  transition: transform 400ms var(--ease-hero), opacity 400ms var(--ease-hero);
}
.collage-cell:hover .c-label { transform: translateY(-4px); opacity: 1; }

/* ---- Experts card: one light sweep as it comes into view, then done ----
   .is-lit is added once by the observer in index.php and never removed, and
   the animation has no iteration count, so it cannot repeat. */
@keyframes expert-sweep {
  from { transform: translateX(-120%) skewX(-12deg); }
  to   { transform: translateX(220%)  skewX(-12deg); }
}
.collage-cell.is-experts .c-media::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 42%;
  background: linear-gradient(100deg,
              rgba(255,255,255,0), rgba(255,255,255,.16), rgba(255,255,255,0));
  transform: translateX(-120%) skewX(-12deg);
  pointer-events: none;
}
.collage-cell.is-experts.is-lit .c-media::after {
  animation: expert-sweep 1.5s cubic-bezier(.33, 0, .2, 1) 1 both;
}

/* ------------------------------------------------------------- opt out ---- */
@media (prefers-reduced-motion: reduce) {
  .hero h1.hero-in,
  .hero .cat-card.hero-in,
  .cat-grid .cat-tile,
  .hero-collage .collage-cell.hero-in { animation: none; opacity: 1; transform: none; }

  .cat-tile .emoji.has-img,
  .cat-tile .emoji svg > g,
  .collage-cell,
  .collage-cell img,
  .collage-cell video,
  .collage-cell .c-label,
  .ic-sparks .ic-spark,
  .ic-display { transition: none; }

  /* The looping ones are dropped outright, hover or not. */
  .cat-tile:hover .ic-drum,
  .cat-tile:hover .ic-display,
  .collage-cell.is-experts.is-lit .c-media::after { animation: none; }

  .cat-tile:not(.soon):hover .emoji svg > g { transform: none; }
  .collage-cell:hover img,
  .collage-cell:hover video { transform: none; }
  .collage-cell:hover .c-label { transform: none; }
}

/* ===========================================================================
   Hero (homepage)
   =========================================================================== */
.hero { padding: 52px 0 44px; }
.hero-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 56px; align-items: start; }

/* Sized to stay on one line at every width, measured rather than guessed.
   Two columns here, so the heading only gets about half the page: just past
   the 900px breakpoint the column is at its narrowest relative to the text,
   which is what sets the 2.5vw slope. Below 900 the grid is a single column
   and the heading can grow faster — see the override in that breakpoint. */
.hero h1 {
  font-size: clamp(20px, 2.5vw, 32px);
  letter-spacing: -0.035em;
  margin-bottom: 22px;
}

.cat-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 22px; box-shadow: var(--shadow); }
.cat-card h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 600; margin-bottom: 16px; }
/* Three live appliances, so three columns — and each icon runs the full width
   of its tile instead of floating in the middle of one. */
.cat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px 14px; }
.cat-tile {
  display: flex; flex-direction: column; align-items: stretch; gap: 10px; text-align: center;
  transition: transform .12s;
  position: relative;
}
.cat-tile:hover { transform: translateY(-3px); }
.cat-tile:hover span { color: var(--brand); }
/* The artwork ships with its own pastel ground, so it BECOMES the tile rather
   than sitting inside a second bordered box drawn around it. */
.cat-tile .emoji { line-height: 0; }
.cat-tile .emoji.has-img {
  width: 100%; background: none; padding: 0;
  border-radius: var(--radius-sm); overflow: hidden;
}
.cat-tile .emoji img { display: block; width: 100%; height: auto; }
/* Real photos (service_photo()) instead of the drawn icons, cropped to one
   square so the three tiles line up whatever shape each photo is. */
.cat-tile .emoji img.cat-photo { aspect-ratio: 1 / 1; object-fit: cover; background: var(--bg-soft); }
/* A category with no artwork yet still needs a ground of its own to sit on. */
.cat-tile .emoji:not(.has-img) {
  display: grid; place-items: center; aspect-ratio: 1 / 1;
  background: var(--bg-soft); border-radius: var(--radius-sm); color: var(--brand);
}
.cat-tile .emoji:not(.has-img) svg { width: 44%; height: 44%; }
.cat-tile span { font-size: 13px; font-weight: 600; line-height: 1.3; transition: color .15s; }
.cat-tile.soon { opacity: .55; pointer-events: none; }
.cat-tile.soon::after {
  content: "Soon"; position: absolute; top: 6px; right: 6px;
  font-size: 9px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  background: var(--ink); color: #fff; padding: 2px 5px; border-radius: 4px;
}

.hero-collage { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 168px 168px; gap: 14px; }
.collage-cell {
  border-radius: var(--radius); overflow: hidden; position: relative;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(150deg, var(--brand), var(--brand-dark));
}
/* Four tiles walking down the violet ramp — dark indigo, then the bright
   accent, then the deepest purple — so they read as one family at a glance.
   All four are dark enough to carry the #fff label set on .collage-cell. */
.collage-cell:nth-child(2) { background: linear-gradient(150deg, var(--indigo), var(--sidebar)); }
.collage-cell:nth-child(3) { background: linear-gradient(150deg, var(--accent), var(--brand)); }
.collage-cell:nth-child(4) { background: linear-gradient(150deg, var(--brand), var(--sidebar)); }
.collage-cell .c-emoji { font-size: 46px; filter: drop-shadow(0 3px 8px rgba(0,0,0,.25)); }
.collage-cell .c-ico { width: 52px; height: 52px; color: #fff; opacity: .95; }
/* .c-media is the clip box. The card itself never moves or scales — the
   picture zooms inside it, which is why the corners stay exactly put. */
.collage-cell .c-media { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; }
.collage-cell img,
.collage-cell video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Photographs and video both vary, so the label gets its own scrim rather than
   relying on whatever happens to be in the bottom-left of the frame. */
.collage-cell.has-photo::after,
.collage-cell.has-video::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(16,10,28,.66), rgba(16,10,28,0) 58%);
}
/* A looping clip is decoration. Someone who has asked their system to stop
   animation should get a still frame, not a video they cannot pause. */
@media (prefers-reduced-motion: reduce) {
  .collage-cell video { display: none; }
}
.collage-cell .c-label {
  position: absolute; left: 14px; bottom: 12px; z-index: 2;
  font-size: 12.5px; font-weight: 600; opacity: .95;
}
/* Four equal cells. The first used to span two rows, which pushed the fourth
   onto a stubby implicit row where its label sat under the icon. */
.hero-collage .collage-cell { min-height: 0; }

/* ---------------------------------------------------------------- trustbar */
.trustbar { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--bg-soft); }
.trustbar-inner { display: flex; gap: 56px; padding: 26px 0; }
.trust-item .num { font-size: 27px; font-weight: 800; letter-spacing: -0.03em; display: flex; align-items: center; gap: 6px; }
.trust-item .num .star { width: 20px; height: 20px; fill: var(--star); }
.trust-item .lbl { font-size: 13.5px; color: var(--muted); }

/* ===========================================================================
   Sections
   =========================================================================== */
.section { padding: 52px 0; }
.section.tinted { background: var(--bg-soft); }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 26px; }
.section-head h2 { font-size: 26px; }
.section-head p  { color: var(--muted); margin-top: 6px; font-size: 14.5px; }
.section-head a  { color: var(--brand); font-weight: 600; font-size: 14px; white-space: nowrap; }

/* ---- "See all appliances →" ----
   The words shift 1px and the arrow 5px, so the gap between them opens as you
   hover — the arrow reads as leading rather than the whole label sliding. */
.see-all {
  position: relative; display: inline-flex; align-items: baseline; gap: 6px;
  transition: color 220ms var(--ease-out-soft);
}
.see-all:hover { color: var(--brand-dark); }
.see-all-t, .see-all-a { transition: transform 220ms var(--ease-out-soft); }
.see-all-t { position: relative; }
.see-all:hover .see-all-t { transform: translateX(1px); }
.see-all:hover .see-all-a { transform: translateX(5px); }

/* Grows from the left, and spans the words only — measured off the text
   itself, so it stays right whatever the label is changed to. An underline
   running on past a moving arrow would look detached from it. */
.see-all-t::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1.5px;
  background: currentColor; border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 220ms var(--ease-out-soft);
}
.see-all:hover .see-all-t::after,
.see-all:focus-visible .see-all-t::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .see-all, .see-all-t, .see-all-a, .see-all-t::after { transition: none; }
  .see-all:hover .see-all-t,
  .see-all:hover .see-all-a { transform: none; }
}

/* Appliance tiles used on home + hub */
.appliance-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.appliance-card {
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: #fff;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  display: flex; flex-direction: column;
}
.appliance-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: transparent; }
/* A ratio, not a fixed height.
   At 158px tall the box was far shorter than the photos are: the 4:3 shots
   lost 38% and the square microwave lost 54%, which is why every appliance
   looked sliced in half. 4:3 matches the photographs, so the landscape ones
   now show whole and the square one crops evenly around its centre. */
.appliance-thumb {
  aspect-ratio: 4 / 3; display: grid; place-items: center; font-size: 54px;
  background: linear-gradient(150deg, var(--brand), var(--brand-dark)); color: #fff;
  overflow: hidden;
}
.appliance-thumb img {
  width: 100%; height: 100%; object-fit: cover;
  /* Appliances sit slightly low in these frames; favouring the top keeps the
     machine in view rather than the ceiling. */
  object-position: center 42%;
}
.appliance-card:nth-child(2) .appliance-thumb { background: linear-gradient(150deg, var(--accent), var(--brand)); }
.appliance-card:nth-child(3) .appliance-thumb { background: linear-gradient(150deg, var(--indigo), var(--sidebar)); }
.appliance-body { padding: 18px 20px 20px; flex: 1; display: flex; flex-direction: column; }
.appliance-body h3 { font-size: 17.5px; margin-bottom: 8px; }
.appliance-body .desc { color: var(--muted); font-size: 14px; margin: 8px 0 16px; flex: 1; }
.appliance-meta { display: flex; align-items: center; justify-content: space-between; gap: 12px;
                  padding-top: 14px; border-top: 1px solid var(--line-soft); }
.appliance-meta .price { font-weight: 700; font-size: 15px; }
.appliance-meta .price small { font-weight: 400; color: var(--muted); font-size: 12px; display: block; }

/* ===========================================================================
   Service page
   =========================================================================== */
.crumbs { padding: 16px 0; font-size: 13px; color: var(--muted); display: flex; gap: 8px; flex-wrap: wrap; }
.crumbs a:hover { color: var(--brand); }

.svc-layout { display: grid; grid-template-columns: minmax(0,1fr) 348px; gap: 40px; align-items: start; padding-bottom: 60px; }

.svc-hero { border: 1px solid var(--line); border-radius: var(--radius); padding: 26px; margin-bottom: 26px; display: flex; gap: 24px; }
.svc-hero-icon {
  width: 88px; height: 88px; border-radius: var(--radius); flex: none; display: grid; place-items: center;
  font-size: 40px; background: linear-gradient(150deg, var(--brand), var(--brand-dark));
  color: #fff;   /* the ground is dark purple — never let this inherit --ink */
}
.svc-hero h1 { font-size: 30px; margin-bottom: 8px; }
.svc-hero .tagline { color: var(--muted); margin: 10px 0 14px; font-size: 14.5px; }
.svc-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px; background: var(--bg-soft);
  border: 1px solid var(--line); font-size: 12.5px; font-weight: 500; color: var(--ink-2);
}
.chip.brand { background: var(--bg-tint); border-color: color-mix(in srgb, var(--brand) 25%, #fff); color: var(--brand-dark); }

.cover-banner {
  display: flex; align-items: center; gap: 16px; margin-bottom: 30px;
  padding: 18px 22px; border-radius: var(--radius);
  background: linear-gradient(100deg, var(--brand), var(--brand-dark)); color: #fff;
}
/* --- CTA banner: energy pulse --------------------------------------------
   Deliberately NOT the scan used above. That section informs; this one asks
   for the booking, so the motion resolves toward the button rather than
   travelling past it: sweep, confirm, ripple, then the button answers.
   Trust -> confirmation -> action. */
.cover-banner { position: relative; overflow: hidden; }

/* An almost imperceptible drift on the purple ground. 22s, so it reads as a
   surface catching light rather than as something animating. */
@keyframes cb-drift {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}
.cover-banner {
  background: linear-gradient(100deg, var(--brand), var(--brand-dark), var(--brand));
  background-size: 200% 100%;
  animation: cb-drift 22s ease-in-out infinite;
}

/* ---- the light sweep ---- */
@keyframes cb-sweep { from { transform: translateX(-60%) skewX(-14deg); } to { transform: translateX(320%) skewX(-14deg); } }
.cover-banner .cb-sweep {
  position: absolute; top: 0; bottom: 0; left: 0; width: 30%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.16), transparent);
  transform: translateX(-60%) skewX(-14deg); pointer-events: none;
}
.cover-banner.is-pulsing .cb-sweep { animation: cb-sweep 1400ms cubic-bezier(.22, 1, .36, 1); }

/* ---- the confirmation mark ---- */
.cover-banner .cb-icon { font-size: 26px; position: relative; display: inline-flex; flex: none; }
.cover-banner .cb-check { width: 30px; height: 30px; transform: scale(.82); }
.cover-banner .cb-check circle,
.cover-banner .cb-check path { stroke-dasharray: 70; stroke-dashoffset: 70; }

@keyframes cb-draw { to { stroke-dashoffset: 0; } }
@keyframes cb-grow { to { transform: scale(1); } }
@keyframes cb-ring {
  from { transform: scale(.6); opacity: .55; }
  to   { transform: scale(2.2); opacity: 0; }
}
.cover-banner .cb-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.75); opacity: 0; pointer-events: none;
}
.cover-banner.is-pulsing .cb-check { animation: cb-grow 640ms cubic-bezier(.22, 1, .36, 1) 380ms forwards; }
.cover-banner.is-pulsing .cb-check circle { animation: cb-draw 620ms cubic-bezier(.22, 1, .36, 1) 380ms forwards; }
.cover-banner.is-pulsing .cb-check path   { animation: cb-draw 520ms cubic-bezier(.22, 1, .36, 1) 820ms forwards; }
.cover-banner.is-pulsing .cb-ring         { animation: cb-ring 900ms cubic-bezier(.22, 1, .36, 1) 700ms; }
/* Settled state, so it never sits half-drawn — this is what a visitor with
   reduced motion, or one who never scrolls it into view, sees. */
.cover-banner.is-pulsed .cb-check { transform: scale(1); }
.cover-banner.is-pulsed .cb-check circle,
.cover-banner.is-pulsed .cb-check path { stroke-dashoffset: 0; }

/* ---- the ripple running to the button ---- */
@keyframes cb-ripple { from { transform: translateX(0); opacity: .5; } to { transform: translateX(100%); opacity: 0; } }
.cover-banner.is-pulsing::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 34%;
  background: linear-gradient(90deg, rgba(255,255,255,.14), transparent);
  pointer-events: none;
  animation: cb-ripple 900ms cubic-bezier(.22, 1, .36, 1) 1150ms;
}

/* ---- the button answers, then settles ---- */
.cover-banner .btn {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  transition: transform 350ms cubic-bezier(.22, 1, .36, 1),
              box-shadow 350ms cubic-bezier(.22, 1, .36, 1);
}
.cover-banner.is-arrived .btn {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .22), 0 0 0 4px rgba(255, 255, 255, .18);
}
.cover-banner .btn-arrow {
  width: 16px; height: 16px; flex: none;
  opacity: 0; transform: translateX(-6px);
  transition: opacity 300ms cubic-bezier(.22, 1, .36, 1),
              transform 300ms cubic-bezier(.22, 1, .36, 1);
}
.cover-banner .btn:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(0,0,0,.2); }
.cover-banner .btn:hover .btn-arrow { opacity: 1; transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .cover-banner { animation: none; background-position: 0 50%; }
  .cover-banner .cb-sweep,
  .cover-banner .cb-ring,
  .cover-banner.is-pulsing::after { display: none; }
  .cover-banner .cb-check { transform: scale(1); animation: none !important; }
  .cover-banner .cb-check circle,
  .cover-banner .cb-check path { stroke-dashoffset: 0; animation: none !important; }
  .cover-banner .btn, .cover-banner .btn-arrow { transition: none; }
  .cover-banner .btn:hover { transform: none; }
}
.cover-banner h4 { font-size: 16px; margin-bottom: 2px; }
.cover-banner p { font-size: 13.5px; opacity: .9; }
.cover-banner .btn { margin-left: auto; background: #fff; color: var(--brand-dark); }

.group-title { font-size: 20px; margin: 32px 0 16px; }
.group-title:first-of-type { margin-top: 0; }

/* --- the bookable service card ------------------------------------------ */
.svc-card {
  display: flex; gap: 20px; padding: 20px 0;
  border-bottom: 1px solid var(--line-soft);
}
.svc-card:last-child { border-bottom: 0; }
.svc-card-main { flex: 1; min-width: 0; }
.svc-card h4 { font-size: 16px; margin-bottom: 7px; }
.svc-card-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 9px; }
.svc-card .price-line { font-weight: 700; font-size: 15.5px; }
.svc-card .price-line .pfx { font-weight: 500; color: var(--ink-2); font-size: 14px; }
.svc-card .dot { color: var(--line); }
.svc-card .desc { color: var(--muted); font-size: 13.5px; margin-bottom: 8px; }
.svc-card .note {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--ink-2); background: var(--bg-soft);
  padding: 5px 10px; border-radius: 7px; border: 1px solid var(--line-soft);
}
.svc-card .opts { font-size: 13px; color: var(--brand); font-weight: 600; margin-top: 9px; display: inline-block; }

.svc-card-side { width: 132px; flex: none; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.svc-card-thumb {
  width: 132px; height: 92px; border-radius: var(--radius-sm); display: grid; place-items: center;
  font-size: 34px; background: var(--bg-soft); border: 1px solid var(--line-soft);
}

/* add / qty stepper */
.add-btn {
  width: 100%; padding: 9px 0; border: 1.5px solid var(--brand); border-radius: var(--radius-sm);
  color: var(--brand); background: #fff; font-weight: 700; font-size: 14px;
  transition: background .15s;
}
.add-btn:hover { background: var(--bg-tint); }

.stepper {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  border: 1.5px solid var(--brand); border-radius: var(--radius-sm); overflow: hidden;
}
.stepper button { width: 34px; height: 36px; color: var(--brand); font-size: 18px; font-weight: 700; line-height: 1; }
.stepper button:hover { background: var(--bg-tint); }
.stepper .qty { font-weight: 700; color: var(--brand); font-size: 14px; }

/* --- how it works -------------------------------------------------------- */
/* ---------------------------------------------------------- service journey
   The four cards read as one process: a signal travels the rail behind them
   and lights each in turn. Layout, type, colour and card size are untouched —
   everything below moves opacity, transform or a pseudo-element only, so the
   text never shifts and stays readable at every moment. */
.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; position: relative; }

/* The rail, threaded through the badge centres (20px card padding + half of
   the 28px badge = 34px from the top). */
.jr-line {
  position: absolute; left: 0; right: 0; top: 34px; height: 2px;
  background: var(--line); border-radius: 2px; overflow: hidden; pointer-events: none;
}
.jr-fill {
  position: absolute; left: 0; top: 0; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--brand), var(--accent));
  border-radius: 2px;
  transition: width 900ms cubic-bezier(.22, 1, .36, 1), height 900ms cubic-bezier(.22, 1, .36, 1);
}
/* A light particle drifting the finished rail — the only thing that keeps
   moving, and it is two pixels wide. */
@keyframes jr-drift { from { transform: translateX(-40px); } to { transform: translateX(100%); } }
.steps.is-complete .jr-line::after {
  content: ""; position: absolute; top: 0; left: 0; width: 40px; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.85), transparent);
  animation: jr-drift 3.4s linear infinite;
}

.jr-dot {
  position: absolute; left: 0; top: 0; z-index: 2; width: 10px; height: 10px;
  border-radius: 50%; background: var(--brand); opacity: 0; pointer-events: none;
  box-shadow: 0 0 0 4px rgba(109, 40, 217, .16), 0 0 14px 2px rgba(109, 40, 217, .45);
  transition: transform 900ms cubic-bezier(.22, 1, .36, 1), opacity 300ms ease;
}

.step {
  padding: 20px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff;
  position: relative;
  /* Softened until the signal arrives. */
  opacity: .35;
  transition: opacity 700ms cubic-bezier(.22, 1, .36, 1),
              transform 700ms cubic-bezier(.22, 1, .36, 1),
              border-color 700ms cubic-bezier(.22, 1, .36, 1),
              box-shadow 700ms cubic-bezier(.22, 1, .36, 1);
}
/* Before the journey starts nothing is dimmed for someone without JS. */
.steps:not(.is-running) .step { opacity: 1; }

.step.is-done { opacity: 1; }
.step.is-lit {
  opacity: 1; transform: translateY(-6px);
  border-color: #C4B5FD;
  box-shadow: 0 12px 28px rgba(46, 16, 101, .12);
}

.step .n {
  width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center;
  background: var(--bg-tint); color: var(--brand-dark); font-weight: 800; font-size: 13px; margin-bottom: 12px;
  position: relative; transform: scale(.8);
  transition: transform 700ms cubic-bezier(.22, 1, .36, 1),
              background 700ms cubic-bezier(.22, 1, .36, 1),
              color 700ms cubic-bezier(.22, 1, .36, 1);
}
.steps:not(.is-running) .step .n,
.step.is-done .n { transform: scale(1); }
.step.is-lit .n { transform: scale(1); background: var(--brand); color: #fff; }

.step .n-num, .step .n-check { grid-area: 1 / 1; }
.step .n-check {
  width: 16px; height: 16px; opacity: 0;
  stroke-dasharray: 26; stroke-dashoffset: 26;
}

/* ---- step 1: a ripple leaves the badge as the request lands ---- */
@keyframes jr-ripple {
  from { transform: scale(.7); opacity: .55; }
  to   { transform: scale(2.6); opacity: 0; }
}
.step .n::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--brand); opacity: 0; pointer-events: none;
}
.step.is-lit .n::after { animation: jr-ripple 900ms cubic-bezier(.22, 1, .36, 1); }

/* ---- step 2: a scanning sweep, like an inspection running ---- */
@keyframes jr-scan { to { transform: rotate(360deg); } }
.step[data-step="2"] .n::before {
  content: ""; position: absolute; inset: -5px; border-radius: 10px;
  border: 2px solid transparent; border-top-color: var(--brand);
  opacity: 0; transition: opacity 300ms ease;
}
.step[data-step="2"].is-lit .n::before { opacity: 1; animation: jr-scan 1.1s linear infinite; }

/* ---- steps 3 and 4: the tick draws itself ---- */
@keyframes jr-draw { to { stroke-dashoffset: 0; } }
.step[data-step="3"].is-lit .n-num,
.step[data-step="4"].is-lit .n-num,
.step[data-step="3"].is-done .n-num,
.step[data-step="4"].is-done .n-num { opacity: 0; transition: opacity 260ms ease; }
.step[data-step="3"].is-lit .n-check,
.step[data-step="4"].is-lit .n-check,
.step[data-step="3"].is-done .n-check,
.step[data-step="4"].is-done .n-check {
  opacity: 1; animation: jr-draw 620ms cubic-bezier(.22, 1, .36, 1) forwards;
}
.step[data-step="3"].is-done .n,
.step[data-step="4"].is-done .n { background: var(--brand); color: #fff; }

.step h4 { font-size: 15px; margin-bottom: 6px; }
.step p  { font-size: 13.5px; color: var(--muted); }

/* ---- hover, once the journey has run ---- */
.step { cursor: default; }
.steps.is-complete .step:hover {
  transform: translateY(-6px); border-color: #C4B5FD;
  box-shadow: 0 12px 28px rgba(46, 16, 101, .12);
}
.steps.is-complete .step:hover .n { transform: scale(1.08); }

/* A small light beside the number on hover.
   Drawn on the CARD, not on the badge: .n::before is already the step-2 scan
   ring, and two rules fighting over one pseudo-element would have meant the
   ring vanished the moment you hovered step 2.
   Positioned off the card's own padding — 20px in, plus the 28px badge, puts
   its centre on the badge's midline. */
.step::after {
  content: ""; position: absolute; left: 54px; top: 34px;
  width: 5px; height: 5px; margin-top: -2.5px;
  border-radius: 50%; background: var(--accent);
  opacity: 0; transition: opacity 260ms ease; pointer-events: none;
}
.steps.is-complete .step:hover::after { opacity: 1; }

/* ---- phones: the journey runs down the page ---- */
@media (max-width: 760px) {
  .steps { grid-template-columns: 1fr; gap: 14px; }
  .jr-line { left: 34px; right: auto; top: 0; bottom: 0; width: 2px; height: auto; }
  .jr-fill { width: 100% !important; height: 0; }
  .steps.is-complete .jr-line::after { display: none; }   /* drift is horizontal only */
  .step.is-lit { transform: translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
  .step, .step .n, .jr-fill, .jr-dot { transition: none; }
  .step { opacity: 1; }
  .step .n { transform: scale(1); }
  .step.is-lit { transform: none; }
  .step.is-lit .n::after,
  .step[data-step="2"].is-lit .n::before,
  .steps.is-complete .jr-line::after { animation: none; }
  .step .n-check { animation: none !important; stroke-dashoffset: 0; }
  .steps.is-complete .step:hover { transform: none; }
}

/* Reviews on a service page: a plain grid, not the homepage carousel. There
   are only ever a handful per appliance, and a slider for three cards is
   machinery with nothing to do. */
.svc-reviews { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
.svc-reviews .rev-card { box-shadow: none; }
@media (max-width: 720px) { .svc-reviews { grid-template-columns: 1fr; } }

/* The hero takes a photograph rather than the flat drawing where one exists,
   so it needs to fill the frame instead of sitting inside it. */
/* A photograph gets a frame shaped like the photograph.
   The 16:9 frame below is right for the flat illustrations, but the technician
   shots are 4:3 — dropped into 16:9 with object-fit:cover the browser removed
   a quarter of the height, half of it off the top, and took the technician's
   head with it. Matching the frame to the source crops nothing at all.
   object-position is a fallback for any future shot that is not exactly 4:3:
   it favours the upper part of the frame, where a person's head is. */
.uc-hero-media.has-shot img {
  width: 100%; height: auto; display: block;
  aspect-ratio: 4 / 3; object-fit: cover; object-position: center 35%;
}

/* --- review carousel -----------------------------------------------------
   A scroll-snap track, not a transform slider. The browser then gives us
   touch swipe, momentum, keyboard arrows and a scrollbar for free, and the
   JS only has to call scrollBy() — nothing to keep in sync and nothing that
   breaks if a script fails to run. */

/* Half stars without a half-star glyph: two rows, the gold one clipped to
   --fill of the width, so 3.5 renders as exactly three and a half.
   Both rows are pseudo-element content — see star_row() for why they are not
   text. The element itself is empty. */
.stars {
  position: relative; display: inline-block; vertical-align: -1px;
  line-height: 1; font-size: 14px; letter-spacing: 2px; white-space: nowrap;
}
.stars::before,
.stars::after { content: "★★★★★"; display: block; }
.stars::before { color: #DDD6FE; }
.stars::after {
  position: absolute; left: 0; top: 0; overflow: hidden;
  width: var(--fill, 0%); color: var(--star);
}

.rev-sample-note {
  display: flex; align-items: flex-start; gap: 9px;
  background: #fdf8ec; border: 1px solid #f0debb; border-radius: var(--radius-sm);
  padding: 11px 14px; margin-bottom: 18px;
  font-size: 13px; line-height: 1.55; color: #7a5a10;
}
.rev-sample-note svg { flex: none; margin-top: 1px; }

.rev-nav { display: flex; gap: 8px; }
.rev-arrow {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  border: 1px solid var(--line); background: #fff; color: var(--brand);
  display: grid; place-items: center; cursor: pointer;
  transition: background 220ms var(--ease-out-soft),
              border-color 220ms var(--ease-out-soft),
              transform 220ms var(--ease-out-soft);
}
.rev-arrow svg { width: 19px; height: 19px; }
.rev-arrow:hover { background: var(--bg-tint); border-color: var(--brand); transform: translateY(-2px); }
.rev-arrow:active { transform: translateY(0); transition-duration: 90ms; }
.rev-arrow[disabled] { opacity: .35; cursor: default; transform: none; background: #fff; border-color: var(--line); }

.rev-carousel { position: relative; }
.rev-track {
  display: flex; gap: 20px;
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  /* Room for the cards' hover lift and shadow, which would otherwise be
     clipped by the scroll container. */
  padding: 6px 2px 14px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.rev-track::-webkit-scrollbar { display: none; }
.rev-track:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; border-radius: var(--radius); }

/* Three across on desktop, and the gap is taken out of each card so three
   really do fit rather than spilling a sliver of a fourth. */
.rev-card {
  flex: 0 0 calc((100% - 40px) / 3); scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 14px;
  padding: 22px; border: 1px solid var(--line); border-radius: var(--radius);
  background: #fff; box-shadow: var(--shadow);
  transition: transform 280ms var(--ease-out-soft),
              box-shadow 280ms var(--ease-out-soft),
              border-color 280ms var(--ease-out-soft);
}
.rev-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: #C4B5FD;
}

/* Reviewer first, then the words — a name and a face give the quote a source
   before you read it. */
.rev-who { display: flex; align-items: center; gap: 11px; }
.rev-av { width: 42px; height: 42px; flex: none; line-height: 0; }
/* Works for both the photo and the drawn fallback — object-fit keeps a
   non-square photo from squashing inside the circle. */
.rev-face {
  width: 42px; height: 42px; border-radius: 50%; display: block;
  object-fit: cover; background: var(--bg-tint);
}
.rev-id { min-width: 0; }
.rev-id strong { display: block; font-size: 14px; }
.rev-rate { display: flex; align-items: center; gap: 7px; margin-top: 2px; }
.rev-score { font-size: 12.5px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.rev-when { margin-left: auto; font-size: 11.5px; color: var(--muted); white-space: nowrap; align-self: flex-start; }

.rev-text {
  flex: 1; font-size: 14px; line-height: 1.65; color: var(--ink-2);
  /* Clamped, so every card in a row ends at the same place however long the
     review runs. */
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}

.rev-tag {
  align-self: flex-start;
  background: var(--bg-tint); color: var(--brand-dark);
  border-radius: 999px; padding: 5px 12px;
  font-size: 12px; font-weight: 600;
}

/* --- leave a review ------------------------------------------------------ */
/* The form column is the narrower of the two — it holds four short fields,
   and giving it the extra width was what made it tower over the copy. */
.rev-share { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: 36px; align-items: center; }

.rev-eyebrow {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--bg-tint); color: var(--brand-dark);
  border-radius: 999px; padding: 6px 13px; margin-bottom: 12px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
}
.rev-share-copy h2 { font-size: 27px; margin-bottom: 10px; text-wrap: balance; }
.rev-share-copy > p { font-size: 14.5px; line-height: 1.65; color: var(--ink-2); max-width: 44ch; }

.rev-why { list-style: none; display: grid; gap: 14px; }
.rev-why li { display: flex; align-items: center; gap: 11px; }
.rev-why .ico {
  width: 36px; height: 36px; border-radius: 10px; flex: none;
  background: var(--bg-tint); color: var(--brand);
  display: grid; place-items: center;
}
.rev-why .ico svg { width: 16px; height: 16px; }
.rev-why strong { display: block; font-size: 13.5px; }
.rev-why li > span:last-child { font-size: 12.5px; color: var(--muted); line-height: 1.4; }

/* Compact on purpose. This is four short fields; at full form sizing it ran
   taller than the panel beside it and the section lost its balance. */
.rev-form-card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px;
}
.rev-form-card h3 { font-size: 16.5px; }
.rev-form-sub { font-size: 12.5px; color: var(--muted); margin-top: 3px; }

.rev-form { display: grid; gap: 12px; }
.rev-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.rev-form label { display: block; font-size: 12px; font-weight: 700; margin-bottom: 5px; }
.rev-form .req { color: var(--accent); }
.rev-form input, .rev-form textarea, .rev-form select {
  width: 100%; border: 1.5px solid var(--line); border-radius: 9px;
  padding: 9px 12px; font: inherit; font-size: 13.5px; outline: none;
  background: #fff; color: var(--ink); transition: border-color 200ms var(--ease-out-soft);
}
.rev-form input:focus, .rev-form textarea:focus, .rev-form select:focus { border-color: var(--brand); }
.rev-form textarea { resize: vertical; min-height: 74px; line-height: 1.55; }
.rev-form .err { display: block; font-size: 12px; color: #c0392b; margin-top: 4px; font-weight: 600; }
.rev-form .has-error input, .rev-form .has-error textarea { border-color: #c0392b; }
.rev-form .field { position: relative; }
.rev-form .btn { padding: 11px 18px; font-size: 14px; }
.rv-count { position: absolute; right: 10px; bottom: 8px; font-size: 11px; color: var(--muted); }

/* Radios in reverse DOM order, laid back out with row-reverse. The checked
   star and every one BEFORE it light up via ~, which only reaches forward —
   reversing the source is what makes that work with no JS at all. */
/* position: relative contains the absolute radios below. They are only 1px
   here and this row is not inside a scroller, so they cause no harm today —
   but without a positioned parent they belong to the document rather than to
   this row, which is exactly the defect that widened the checkout page. */
.rv-stars { position: relative; display: flex; flex-direction: row-reverse; justify-content: flex-end; align-items: center; gap: 4px; }
/* 16px for the same reason as the day/slot radios — see the note there. The
   star is a <label>; this hidden radio is what actually takes focus, and a
   focused control under 16px zooms mobile Safari in and leaves it there. */
.rv-stars input { position: absolute; opacity: 0; width: 1px; height: 1px; font-size: 16px; }
.rv-stars label {
  margin: 0; cursor: pointer; line-height: 0; color: var(--brand);
  transition: transform 160ms var(--ease-out-soft);
}
.rv-stars label svg {
  width: 25px; height: 25px; display: block;
  fill: none;                                   /* hollow until chosen */
  transition: fill 160ms var(--ease-out-soft), color 160ms var(--ease-out-soft);
}
.rv-stars input:checked ~ label svg,
.rv-stars label:hover svg,
.rv-stars label:hover ~ label svg { fill: var(--star); color: var(--star); }
.rv-stars label:hover { transform: scale(1.1); }
.rv-stars input:focus-visible + label { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 6px; }
.rv-stars-hint { order: 9; margin-left: 10px; font-size: 12.5px; color: var(--muted); }

/* --- illustration + reasons, side by side --- */
.rev-art-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: center; margin-top: 20px; }
.rev-art { width: 100%; max-width: 260px; height: auto; display: block; }

/* --- form header, with the trust badge --- */
.rev-form-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 14px; }
.rev-trust {
  flex: none; text-align: center; background: var(--bg-tint);
  border-radius: 9px; padding: 7px 11px;
}
.rev-trust .stars { font-size: 13px; }
.rev-trust span { display: block; font-size: 10px; color: var(--brand-dark); font-weight: 600; margin-top: 2px; }

@media (max-width: 1040px) {
  .rev-art-row { grid-template-columns: 1fr; gap: 18px; }
  .rev-art { max-width: 320px; }
}
@media (max-width: 560px) {
  .rev-form-head { flex-direction: column; }
  .rev-trust { align-self: flex-start; }
  .rv-stars label svg { width: 30px; height: 30px; }
}

.rev-form-note { font-size: 11.5px; color: var(--muted); text-align: center; margin-top: -4px; }

.rev-thanks { text-align: center; padding: 18px 6px; }
.rev-thanks .ico { display: block; color: var(--green); line-height: 0; margin-bottom: 12px; }
.rev-thanks h3 { font-size: 18px; margin-bottom: 7px; }
.rev-thanks p { font-size: 13.5px; color: var(--ink-2); line-height: 1.6; margin-bottom: 16px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 900px) {
  .rev-share { grid-template-columns: 1fr; gap: 28px; }
  .rev-share-copy h2 { font-size: 25px; }
}
@media (max-width: 560px) {
  .rev-form-row { grid-template-columns: 1fr; }
  .rev-form-card { padding: 20px; }
}

.rev-dots { display: flex; justify-content: center; gap: 7px; margin-top: 4px; }
.rev-dot {
  width: 7px; height: 7px; border-radius: 999px; border: 0; padding: 0;
  background: #DDD6FE; cursor: pointer;
  transition: width 260ms var(--ease-out-soft), background 260ms var(--ease-out-soft);
}
.rev-dot.on { width: 22px; background: var(--brand); }

@media (max-width: 900px) {
  .rev-card { flex-basis: calc((100% - 20px) / 2); }
}
@media (max-width: 640px) {
  .rev-card { flex-basis: 100%; padding: 18px; }
  .rev-track { gap: 14px; }
  .rev-nav { display: none; }      /* swipe is the gesture on a phone */
}

@media (prefers-reduced-motion: reduce) {
  .rev-track { scroll-behavior: auto; }
  .rev-card, .rev-arrow, .rev-dot { transition: none; }
  .rev-card:hover { transform: none; }
}

/* --- about page ----------------------------------------------------------
   A reading page, so the measure is capped near 68 characters and the type
   carries the structure. Headings get the space; only the two panels and the
   step numbers take a fill. */
.about-wrap { max-width: 760px; }
.about-wrap p { font-size: 15.5px; line-height: 1.75; color: var(--ink-2); margin-bottom: 16px; }
.about-wrap h2 {
  font-size: 22px; margin: 38px 0 14px; text-wrap: balance;
  padding-top: 30px; border-top: 1px solid var(--line);
}
.about-wrap h2:first-of-type { padding-top: 0; border-top: 0; }
.about-lede { font-size: 17px !important; line-height: 1.7 !important; color: var(--ink) !important; }

/* The one-line statements of intent. A left rule rather than a box: it is a
   pull-quote in a page of prose, not a separate object. */
.about-quote {
  border-left: 3px solid var(--brand); padding: 4px 0 4px 18px;
  font-size: 17px !important; font-weight: 600; color: var(--ink) !important;
  margin: 4px 0 18px !important;
}

.about-steps { list-style: none; display: grid; gap: 18px; margin: 4px 0 8px; }
.about-steps li { display: flex; gap: 15px; align-items: flex-start; }
.about-steps .n {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  background: var(--brand); color: #fff; display: grid; place-items: center;
  font-size: 14px; font-weight: 700; margin-top: 1px;
}
.about-steps h3 { font-size: 16px; margin-bottom: 5px; }
.about-steps p { margin-bottom: 0 !important; font-size: 14.5px !important; }

.about-two { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin: 6px 0 8px; }
.about-panel {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg-soft); padding: 22px;
}
.about-panel h2 { margin: 0 0 10px; padding: 0; border: 0; font-size: 18px; }
.about-panel p { font-size: 14.5px !important; margin-bottom: 12px !important; }

.about-list { list-style: none; display: grid; gap: 9px; }
.about-list li {
  position: relative; padding-left: 22px; font-size: 14.5px; line-height: 1.5; color: var(--ink-2);
}
.about-list li::before {
  content: ''; position: absolute; left: 4px; top: 8px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand);
}

.about-note { font-size: 16px !important; color: var(--ink) !important; margin: 18px 0 0 !important; }

.about-why { display: grid; grid-template-columns: 1fr 1fr; gap: 20px 26px; margin: 4px 0 8px; }
.about-why-item { display: flex; gap: 13px; align-items: flex-start; }
.about-why-item .ico { color: var(--brand); flex: none; margin-top: 2px; }
.about-why-item h3 { font-size: 15.5px; margin-bottom: 4px; }
.about-why-item p { font-size: 14px !important; margin-bottom: 0 !important; }

@media (max-width: 720px) {
  .about-two, .about-why { grid-template-columns: 1fr; }
}

/* --- contact / help form -------------------------------------------------
   Form on the left with the room, the faster routes beside it rather than
   underneath — someone who would rather call should not have to scroll past
   a form to find the number. */
.contact-grid { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: 30px; align-items: start; }
.contact-form-col { min-width: 0; }
.contact-form { display: grid; gap: 18px; }

/* Off-screen rather than display:none — some bots skip inputs that are not
   rendered, and this one is meant to be found and filled. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.contact-form label { display: block; font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 7px; }
.contact-form input, .contact-form textarea {
  width: 100%; border: 1.5px solid var(--line); border-radius: var(--radius-sm);
  padding: 12px 14px; font: inherit; font-size: 15px; outline: none;
  background: #fff; color: var(--ink); transition: border-color .15s;
}
.contact-form input:focus, .contact-form textarea:focus { border-color: var(--brand); }
.contact-form textarea { resize: vertical; min-height: 128px; line-height: 1.6; }
.contact-form .hint { display: block; font-size: 12.5px; color: var(--muted); margin-top: 6px; }
.contact-form .err  { display: block; font-size: 12.5px; color: #c0392b; margin-top: 6px; font-weight: 600; }
.contact-form .has-error input, .contact-form .has-error textarea { border-color: #c0392b; }

/* The phone field carries no +91 prefix. An absolutely-positioned prefix sat
   on top of the placeholder rather than beside it, and the country code was
   decoration anyway — the server strips a pasted 91 and validates the ten
   digits either way. */
#c_phone { letter-spacing: .04em; }

.contact-done { text-align: center; padding: 40px 24px; border: 1px solid var(--line);
                border-radius: var(--radius); background: var(--bg-soft); }
.contact-done .ico { color: var(--green); display: block; margin-bottom: 12px; line-height: 0; }
.contact-done h2 { font-size: 21px; margin-bottom: 8px; }
.contact-done p  { font-size: 14.5px; color: var(--ink-2); margin-bottom: 18px; line-height: 1.65; }

.contact-side { display: grid; gap: 14px; }
.contact-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; background: #fff; }
.contact-card h3 { font-size: 15px; margin-bottom: 6px; }
.contact-card p  { font-size: 13.5px; color: var(--muted); line-height: 1.55; margin-bottom: 12px; }
/* "What happens next" — numbered because it really is a sequence. */
.contact-steps { list-style: none; counter-reset: cs; display: grid; gap: 11px; }
.contact-steps li {
  counter-increment: cs; position: relative; padding-left: 30px;
  font-size: 13.5px; line-height: 1.55; color: var(--ink-2);
}
.contact-steps li::before {
  content: counter(cs); position: absolute; left: 0; top: -1px;
  width: 21px; height: 21px; border-radius: 50%;
  background: var(--bg-tint); color: var(--brand);
  font-size: 11.5px; font-weight: 700; display: grid; place-items: center;
}

.btn-block { width: 100%; justify-content: center; }
.footer-cta { margin-top: 14px; display: inline-flex; align-items: center; gap: 8px; }

@media (max-width: 860px) {
  .contact-grid { grid-template-columns: 1fr; gap: 22px; }
}

/* --- legal / policy pages ------------------------------------------------
   Shared by terms, privacy, cancellation and refund. A legal page is read in
   fragments — someone arrives looking for one clause — so the numbers hang in
   the margin and every heading carries an id worth linking to. */
.legal-wrap { max-width: 780px; }
.legal-wrap p { font-size: 15px; line-height: 1.75; color: var(--ink-2); margin-bottom: 14px; }
.legal-lede { font-size: 16.5px !important; color: var(--ink) !important; }

.legal-wrap h2 {
  position: relative; font-size: 19px; margin: 34px 0 12px;
  padding-top: 26px; border-top: 1px solid var(--line); text-wrap: balance;
}
.legal-wrap h2 span {
  color: var(--brand); font-variant-numeric: tabular-nums; margin-right: 7px;
}
/* Wide enough to hang the number outside the measure; below that it stays
   inline so it never collides with the heading text. */
@media (min-width: 1000px) {
  .legal-wrap h2 span { position: absolute; left: -62px; margin-right: 0; text-align: right; width: 46px; }
}
.legal-wrap h2:target { scroll-margin-top: 90px; }

.legal-list { list-style: none; display: grid; gap: 8px; margin: 0 0 16px; }
.legal-list li {
  position: relative; padding-left: 20px; font-size: 15px; line-height: 1.65; color: var(--ink-2);
}
.legal-list li::before {
  content: ''; position: absolute; left: 3px; top: 9px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--brand);
}

/* The clauses that protect the reader rather than the company. Given a wash
   so they are not lost in a wall of grey text. */
.legal-callout {
  background: var(--bg-tint); border-left: 3px solid var(--brand);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 13px 16px; color: var(--ink) !important; margin: 4px 0 16px !important;
}

.legal-contact { display: flex; flex-wrap: wrap; gap: 12px; margin: 4px 0 20px; }
.legal-contact a {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: 999px; padding: 9px 16px;
  font-size: 14.5px; font-weight: 600; color: var(--brand); background: #fff;
}
.legal-contact a:hover { border-color: var(--brand); background: var(--bg-tint); }

.legal-sign {
  margin-top: 26px !important; padding-top: 20px;
  border-top: 1px solid var(--line); color: var(--ink) !important;
}

/* --- "the difference": smart service scan --------------------------------
   A beam crosses the three cards and each validates as it passes. The motion
   is technical on purpose — this section informs, where the CTA below it
   persuades, and the two should not move the same way.
   Layout, type, colour and card size are untouched throughout. */
.promise { position: relative; }

@keyframes scan-cross {
  from { transform: translateX(-8%); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  to   { transform: translateX(108%); opacity: 0; }
}
.scan-beam {
  position: absolute; top: -6px; bottom: -6px; left: 0; width: 90px;
  pointer-events: none; opacity: 0; z-index: 2; border-radius: 90px;
  background: linear-gradient(90deg,
              transparent, rgba(124, 58, 237, .10), rgba(124, 58, 237, .22),
              rgba(124, 58, 237, .10), transparent);
}
.promise.is-scanning .scan-beam { animation: scan-cross 1900ms cubic-bezier(.22, 1, .36, 1); }

.promise-item {
  position: relative;
  transition: transform 400ms cubic-bezier(.22, 1, .36, 1),
              box-shadow 400ms cubic-bezier(.22, 1, .36, 1),
              border-color 400ms cubic-bezier(.22, 1, .36, 1);
}
/* The border brightens as the beam arrives, then settles back. */
.promise-item.is-checked { border-color: #C4B5FD; }
.promise.is-scanned .promise-item { border-color: var(--line); }

/* A soft glow leaving the icon on arrival. */
@keyframes scan-glow {
  from { transform: scale(.6); opacity: .5; }
  to   { transform: scale(2.4); opacity: 0; }
}
.promise-item .ico { position: relative; }
.promise-item .ico::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  background: radial-gradient(circle, rgba(124,58,237,.35), transparent 70%);
  opacity: 0; pointer-events: none;
}
.promise-item.is-checked .ico::after { animation: scan-glow 900ms cubic-bezier(.22, 1, .36, 1); }

/* The icons draw themselves in as they are reached. */
@keyframes scan-draw { to { stroke-dashoffset: 0; } }
.promise-item .ico svg path,
.promise-item .ico svg circle,
.promise-item .ico svg rect {
  stroke-dasharray: 120; stroke-dashoffset: 120;
}
.promise-item.is-checked .ico svg path,
.promise-item.is-checked .ico svg circle,
.promise-item.is-checked .ico svg rect {
  animation: scan-draw 700ms cubic-bezier(.22, 1, .36, 1) forwards;
}
/* Nothing to draw for someone who never triggers it, or with JS off. */
.promise:not(.is-scanning):not(.is-scanned) .promise-item:not(.is-checked) .ico svg * { stroke-dashoffset: 0; }

/* ---- card 2: a highlight crosses the calendar ---- */
@keyframes ico-sweep { from { transform: translateX(-120%); } to { transform: translateX(220%); } }
.ico-sweep {
  position: absolute; inset: 0; border-radius: 5px; overflow: hidden; opacity: 0;
  background: linear-gradient(90deg, transparent, rgba(124,58,237,.45), transparent);
  transform: translateX(-120%); pointer-events: none;
}
.promise-item.is-checked .ico-sweep { opacity: 1; animation: ico-sweep 820ms cubic-bezier(.22, 1, .36, 1) 380ms; }

/* ---- card 3: a line resolves into a tick ---- */
.ico-tick {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0;
  stroke-dasharray: 26; stroke-dashoffset: 26; pointer-events: none;
}
.promise-item.is-checked .ico-tick {
  opacity: 1;
  animation: scan-draw 520ms cubic-bezier(.22, 1, .36, 1) 520ms forwards;
  transition: opacity 200ms ease 500ms;
}

/* ---- hover: this card only ---- */
@keyframes ico-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}
.promise-item:hover {
  transform: translateY(-7px);
  box-shadow: 0 14px 30px rgba(46, 16, 101, .13);
  border-color: #C4B5FD;
}
.promise-item .ico { transition: transform 350ms cubic-bezier(.22, 1, .36, 1); }
.promise-item:hover .ico {
  transform: scale(1.08);
  animation: ico-float 2.4s ease-in-out infinite;
  filter: drop-shadow(0 3px 8px rgba(124, 58, 237, .35));
}

@media (prefers-reduced-motion: reduce) {
  .promise.is-scanning .scan-beam,
  .promise-item.is-checked .ico::after,
  .promise-item:hover .ico { animation: none; }
  .promise-item .ico svg * { stroke-dashoffset: 0 !important; animation: none !important; }
  .ico-tick { opacity: 1; stroke-dashoffset: 0; animation: none !important; }
  .ico-sweep { display: none; }
  .promise-item, .promise-item .ico { transition: none; }
  .promise-item:hover { transform: none; }
}

/* --- promise / benefits -------------------------------------------------- */
.promise { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.promise-item { display: flex; gap: 13px; align-items: flex-start; padding: 18px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.promise-item .ico { font-size: 22px; line-height: 1; }
.promise-item h4 { font-size: 14.5px; margin-bottom: 4px; }
.promise-item p  { font-size: 13px; color: var(--muted); }

.benefit-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }

/* --- rating summary ------------------------------------------------------ */
.rating-summary { display: grid; grid-template-columns: 190px 1fr; gap: 40px; align-items: center;
                  padding: 26px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.rating-big { text-align: center; }
.rating-big .val { font-size: 46px; font-weight: 800; letter-spacing: -0.04em; line-height: 1; }
.rating-big .stars { display: flex; justify-content: center; gap: 3px; margin: 8px 0 6px; }
.rating-big .stars .star { width: 16px; height: 16px; fill: var(--star); }
.rating-bars { display: grid; gap: 7px; }
.rating-bar { display: grid; grid-template-columns: 16px 1fr 46px; gap: 12px; align-items: center; font-size: 12.5px; color: var(--muted); }
.rating-bar .track { height: 7px; background: var(--line-soft); border-radius: 999px; overflow: hidden; }
.rating-bar .fill  { height: 100%; background: var(--star); border-radius: 999px; }
.rating-bar .cnt { text-align: right; }

.review-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-top: 20px; }
.review-card { padding: 20px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.review-top { display: flex; align-items: center; gap: 12px; margin-bottom: 11px; }
.avatar { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
          background: var(--bg-tint); color: var(--brand-dark); font-weight: 700; font-size: 15px; flex: none; }
.review-card .name { font-weight: 600; font-size: 14.5px; }
.review-card .loc  { font-size: 12.5px; color: var(--muted); }
.review-card .body { font-size: 13.5px; color: var(--ink-2); }
.review-stars { display: flex; gap: 2px; margin-left: auto; }
.review-stars .star { width: 13px; height: 13px; fill: var(--star); }
.review-stars .star.off { fill: #DDD6FE; }

/* --- problems / tips ----------------------------------------------------- */
.list-cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
.list-card { display: flex; gap: 14px; padding: 18px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.list-card .idx {
  width: 26px; height: 26px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--bg-tint); color: var(--brand-dark); font-size: 12.5px; font-weight: 700;
}
.list-card h4 { font-size: 14.5px; margin-bottom: 4px; }
.list-card p  { font-size: 13.5px; color: var(--muted); }

/* --- FAQ accordion ------------------------------------------------------- */
.faq { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: #fff; }
.faq-item + .faq-item { border-top: 1px solid var(--line-soft); }
.faq-q {
  width: 100%; display: flex; align-items: center; gap: 16px; justify-content: space-between;
  padding: 18px 22px; text-align: left; font-weight: 600; font-size: 14.5px;
}
.faq-q:hover { background: var(--bg-soft); }
.faq-q .caret { width: 18px; height: 18px; flex: none; fill: var(--muted); transition: transform .2s ease; }
.faq-item.open .faq-q .caret { transform: rotate(180deg); }
.faq-a { display: none; padding: 0 22px 20px; font-size: 13.8px; color: var(--muted); max-width: 76ch; }
.faq-item.open .faq-a { display: block; }

/* --- SEO block ----------------------------------------------------------- */
.seo-block { padding: 26px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-soft); }
.seo-block h3 { font-size: 18px; margin-bottom: 10px; }
.seo-block p  { color: var(--muted); font-size: 14px; max-width: 90ch; }

/* ===========================================================================
   Sticky cart rail
   =========================================================================== */
.rail { position: sticky; top: 92px; display: grid; gap: 16px; }
.rail-card { border: 1px solid var(--line); border-radius: var(--radius); background: #fff; overflow: hidden; }
.rail-head { padding: 16px 18px; border-bottom: 1px solid var(--line-soft); display: flex; align-items: center; justify-content: space-between; }
.rail-head h3 { font-size: 15.5px; }
.rail-head button { font-size: 12.5px; color: var(--muted); }
.rail-head button:hover { color: var(--accent); }
.rail-body { padding: 8px 18px 18px; }

.rail-empty { padding: 30px 18px; text-align: center; }
.rail-empty .ico { font-size: 32px; margin-bottom: 10px; opacity: .5; }
.rail-empty p { font-size: 13.5px; color: var(--muted); }

.rail-item { display: flex; gap: 12px; align-items: flex-start; padding: 13px 0; border-bottom: 1px solid var(--line-soft); }
.rail-item:last-of-type { border-bottom: 0; }
.rail-item .ri-name { font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.rail-item .ri-sub  { font-size: 12px; color: var(--muted); margin-top: 2px; }
.rail-item .ri-price { margin-left: auto; font-weight: 700; font-size: 13.5px; white-space: nowrap; }
.rail-stepper { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--line); border-radius: 7px; margin-top: 7px; }
.rail-stepper button { width: 26px; height: 26px; font-size: 15px; color: var(--brand); font-weight: 700; }
.rail-stepper .q { font-size: 12.5px; font-weight: 700; min-width: 16px; text-align: center; }

.rail-totals { padding-top: 14px; margin-top: 6px; border-top: 1px dashed var(--line); display: grid; gap: 8px; }
.rail-row { display: flex; justify-content: space-between; font-size: 13.5px; color: var(--ink-2); }
.rail-row.total { font-size: 16.5px; font-weight: 800; color: var(--ink); padding-top: 8px; border-top: 1px solid var(--line-soft); }
.rail-note { font-size: 11.5px; color: var(--muted); margin-top: 10px; line-height: 1.45; }

.rail-usp { padding: 16px 18px; display: grid; gap: 11px; }
.rail-usp div { display: flex; gap: 10px; align-items: center; font-size: 13px; color: var(--ink-2); }
.rail-usp .ico { font-size: 15px; }

/* Mobile sticky checkout bar */
.mobile-bar {
  display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  background: #fff; border-top: 1px solid var(--line); padding: 12px 16px;
  box-shadow: 0 -4px 20px rgba(30, 27, 75,.08);
}
.mobile-bar .mb-inner { display: flex; align-items: center; gap: 14px; }
.mobile-bar .mb-total { font-weight: 800; font-size: 16px; }
.mobile-bar .mb-total small { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); }
.mobile-bar .btn { margin-left: auto; }

/* ===========================================================================
   Cart + checkout pages
   =========================================================================== */
.page-head { padding: 34px 0 24px; }
.page-head h1 { font-size: 30px; margin-bottom: 6px; }
.page-head p  { color: var(--muted); }

.checkout-layout { display: grid; grid-template-columns: minmax(0,1fr) 348px; gap: 40px; align-items: start; padding-bottom: 70px; }

.panel { border: 1px solid var(--line); border-radius: var(--radius); background: #fff; margin-bottom: 20px; }
.panel-head { padding: 18px 22px; border-bottom: 1px solid var(--line-soft); display: flex; align-items: center; gap: 11px; }
.panel-head .n { width: 24px; height: 24px; border-radius: 50%; background: var(--brand); color: #fff;
                 display: grid; place-items: center; font-size: 12px; font-weight: 700; flex: none; }
.panel-head h3 { font-size: 16px; }
.panel-body { padding: 22px; }

.field { margin-bottom: 16px; }
.field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.field .req { color: var(--accent); }
.field input, .field textarea, .field select {
  width: 100%; padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff; outline: none; transition: border-color .15s;
}
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--brand); }
.field textarea { min-height: 84px; resize: vertical; }
.field .err { color: #c0392b; font-size: 12.5px; margin-top: 5px; display: block; }
.field.has-error input, .field.has-error textarea { border-color: #c0392b; }
.field.has-error input:focus, .field.has-error textarea:focus {
  border-color: #c0392b; box-shadow: 0 0 0 3px rgba(192, 57, 43, .12);
}
/* Quiet confirmation that a completed field is accepted — a green rule, not
   a tick and a message. Nothing to read when nothing is wrong. */
.field.is-ok input { border-color: #7fc9b4; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

/* The date strip, the time picker and the map-pin field used to sit here.
   They moved to assets/css/checkout.css — checkout.php is the only page that
   renders them, so they now load only where they are used. The components
   AROUND them (.panel, .field, .rail, .alert) stay in this file, because
   cart.php and booking-success.php render those too. */

.pay-opt { display: flex; align-items: center; gap: 13px; padding: 15px 17px;
           border: 1.5px solid var(--line); border-radius: var(--radius-sm); cursor: pointer; margin-bottom: 11px; }
.pay-opt:has(input:checked) { border-color: var(--brand); background: var(--bg-tint); }
.pay-opt input { accent-color: var(--brand); width: 17px; height: 17px; }
.pay-opt .ico { font-size: 19px; }
.pay-opt .t { font-size: 14px; font-weight: 600; }
.pay-opt .s { font-size: 12.5px; color: var(--muted); }

.alert { padding: 14px 18px; border-radius: var(--radius-sm); font-size: 13.5px; margin-bottom: 20px; }
.alert-error { background: #fdf3f2; border: 1px solid #f3c9c3; color: #a03024; }

/* success */
.success-card { max-width: 640px; margin: 50px auto 70px; border: 1px solid var(--line);
                border-radius: var(--radius); background: #fff; overflow: hidden; }
.success-top { padding: 38px 32px 30px; text-align: center; background: var(--bg-tint); border-bottom: 1px solid var(--line); }
.success-top .tick { width: 58px; height: 58px; margin: 0 auto 16px; border-radius: 50%;
                     background: var(--brand); color: #fff; display: grid; place-items: center; font-size: 28px; }
.success-top h1 { font-size: 25px; margin-bottom: 7px; }
.success-top p { color: var(--muted); font-size: 14.5px; }
.success-ref { display: inline-block; margin-top: 14px; padding: 8px 16px; background: #fff;
               border: 1px dashed var(--brand); border-radius: 8px; font-weight: 700; letter-spacing: .04em; }
.success-body { padding: 28px 32px 32px; }

/* Tracking call-to-action on the confirmation page. */
.track-cta { display: flex; align-items: center; gap: 14px; text-decoration: none;
             padding: 16px 18px; margin-bottom: 10px; border-radius: 12px;
             background: var(--brand); color: #fff;
             box-shadow: 0 4px 16px rgba(14,124,102,.22); }
.track-cta:hover { background: var(--brand-dark); }
.track-ico { font-size: 22px; line-height: 1; }
.track-txt { flex: 1; min-width: 0; }
.track-txt strong { display: block; font-size: 15.5px; }
.track-txt small  { display: block; font-size: 12.5px; opacity: .88; margin-top: 2px; }
.track-go { font-size: 20px; opacity: .9; }
.track-hint { color: var(--muted); font-size: 12.5px; margin: 0 0 22px; text-align: center; }
.kv { display: grid; grid-template-columns: 132px 1fr; gap: 10px 18px; font-size: 14px; margin-bottom: 22px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-weight: 500; }

/* ===========================================================================
   Footer
   =========================================================================== */
.site-footer { border-top: 1px solid var(--line); background: var(--bg-soft); padding: 52px 0 30px; margin-top: auto; }
/* About block, then three link columns. Was repeat(3, …) beside it for four
   columns; the "For professionals" column is gone, so this is now
   about + Services + Company + For customers. */
.footer-grid { display: grid; grid-template-columns: 1.5fr repeat(3, 1fr); gap: 40px; margin-bottom: 38px; }
.footer-col h4 { font-size: 13px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-bottom: 14px; }
.footer-col li { margin-bottom: 9px; }
.footer-col a { font-size: 13.5px; color: var(--ink-2); }
.footer-col a:hover { color: var(--brand); }
.footer-about p { font-size: 13.5px; color: var(--muted); margin: 12px 0 16px; max-width: 34ch; }
.footer-contact { font-size: 13.5px; color: var(--ink-2); display: grid; gap: 6px; }
.footer-bottom { border-top: 1px solid var(--line); padding-top: 22px; display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; font-size: 12.5px; color: var(--muted); }
.footer-seo { border-top: 1px solid var(--line); margin-top: 26px; padding-top: 22px; }
.footer-seo h4 { font-size: 12.5px; color: var(--muted); margin-bottom: 10px; }
.footer-seo .links { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.footer-seo a { font-size: 12.5px; color: var(--muted); }
.footer-seo a:hover { color: var(--brand); }

/* ===========================================================================
   Toast
   =========================================================================== */
.toast {
  position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 20px);
  background: var(--ink); color: #fff; padding: 13px 22px; border-radius: 999px;
  font-size: 13.5px; font-weight: 500; z-index: 200;
  opacity: 0; pointer-events: none; transition: opacity .22s ease, transform .22s ease;
  box-shadow: var(--shadow-lg);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ===========================================================================
   Responsive
   =========================================================================== */
@media (max-width: 1080px) {
  .svc-layout, .checkout-layout { grid-template-columns: 1fr; }
  .rail { position: static; }
  .rail-card.cart-rail { display: none; }
  .mobile-bar { display: block; }
  body.has-mobile-bar { padding-bottom: 78px; }
  .steps { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; gap: 34px; }
  /* One column now, so the heading has the full page width and can climb
     faster. Capped at 28px so the jump across the breakpoint stays small. */
  .hero h1 { font-size: clamp(18px, 5vw, 28px); }
  .hero-collage { grid-template-rows: 132px 132px; }
  .appliance-grid { grid-template-columns: 1fr 1fr; }
  .promise, .benefit-grid { grid-template-columns: 1fr 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 30px; }
  .header-search { display: none; }
  .trustbar-inner { gap: 34px; }
}

@media (max-width: 640px) {
  /* Set the variable, never .wrap's padding directly — .nav-strip ul and
     .header-inner both cancel this exact value, and hard-coding it here left
     them cancelling 24px against a 16px gutter between 481 and 640px. */
  :root { --gutter: 16px; }
  .hero { padding: 30px 0 26px; }
  .cat-grid { grid-template-columns: repeat(3, 1fr); }
  .appliance-grid, .promise, .benefit-grid, .list-cards, .review-grid { grid-template-columns: 1fr; }
  .steps { grid-template-columns: 1fr; }
  .rating-summary { grid-template-columns: 1fr; gap: 22px; }
  .field-row { grid-template-columns: 1fr; gap: 0; }

  /* ---- checkout on a phone ------------------------------------------------
     The layout already stacks at 1080px; these are the things that stacking
     alone does not fix. */

  /* 16px is not a style choice. Safari zooms the whole page in when you focus
     an input smaller than that and does not zoom back out, so every field
     after the first is typed into a page the user has to pinch to escape. */
  .field input, .field textarea, .field select { font-size: 16px; padding: 12px 13px; }
  .field textarea { min-height: 76px; }
  .field label { font-size: 12.5px; }

  /* Sticky made sense in a side column. Stacked, it means the bill summary
     detaches and follows you up the page over the form above it. */
  .rail { position: static; top: auto; gap: 12px; }

  .panel { margin-bottom: 14px; }
  .panel-head { padding: 14px 16px; }
  .panel-body { padding: 16px; }

  /* The day strip's phone sizing and the pin-map block moved to
     assets/css/checkout.css, next to the base rules they override. */

  .svc-hero { flex-direction: column; gap: 16px; }
  .svc-hero h1 { font-size: 24px; }
  .svc-card { flex-direction: column-reverse; gap: 14px; }
  .svc-card-side { width: 100%; flex-direction: row; align-items: center; gap: 12px; }
  .svc-card-thumb { width: 84px; height: 62px; font-size: 26px; }
  .svc-card .add-btn, .svc-card .stepper { width: auto; min-width: 116px; margin-left: auto; }
  .cover-banner { flex-wrap: wrap; }
  .cover-banner .btn { margin-left: 0; width: 100%; }

  /* The pill used to be a static label, so hiding it on a cramped phone
     header cost nothing. It is now the ONLY way into the location picker —
     hidden, a phone customer cannot set the address the technician drives to,
     on exactly the device whose GPS is worth having. So it drops to its own
     full-width row instead of disappearing. */
  .header-inner { flex-wrap: wrap; }
  .loc-pill {
    order: 3; flex-basis: 100%; max-width: none;
    margin-top: 2px; padding: 9px 12px; font-size: 12px;
    background: var(--bg-soft, #F5F3FF); border-color: transparent;
  }
  .section { padding: 34px 0; }
  .section-head h2 { font-size: 21px; }
  .trustbar-inner { gap: 26px; }
  .trust-item .num { font-size: 21px; }
}

/* ===========================================================================
   Service page — offset three-column layout
   [ narrow title rail ] [ main content card ] [ sidebar ]
   =========================================================================== */
.uc-page { max-width: 1520px; margin: 0 auto; padding: 0 24px; }

/* ------------------------------------------------------------------- hero */
.uc-hero {
  display: grid; grid-template-columns: 380px minmax(0, 1fr);
  gap: 44px; align-items: start; padding: 44px 0 40px;
}

.uc-title-row { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 14px; }
.uc-title-row h1 { font-size: 42px; letter-spacing: -0.035em; flex: 1; min-width: 0; }

.uc-badges { display: grid; gap: 7px; justify-items: stretch; flex: none; }
.badge-instant, .badge-eta {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  padding: 7px 13px; border-radius: 7px; font-size: 13.5px; font-weight: 600; white-space: nowrap;
}
.badge-instant { background: var(--green-badge, #0e7c66); color: #fff; }
.badge-instant svg { width: 13px; height: 13px; fill: currentColor; }
.badge-eta { background: #fff; color: var(--ink); border: 1px solid var(--line); }

.uc-rating { display: inline-flex; align-items: center; gap: 6px; font-size: 15px; margin-bottom: 22px; }
.uc-rating .star { width: 15px; height: 15px; fill: var(--star); }
.uc-rating span { text-decoration: underline; text-underline-offset: 3px; }

.uc-warranty {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 17px 20px; border-radius: 12px; background: var(--bg-soft);
  font-size: 15px; font-weight: 500; margin-bottom: 26px;
  transition: background .15s;
}
.uc-warranty:hover { background: #EDE9FE; }
.uc-warranty .shield { width: 20px; height: 20px; flex: none; fill: none; stroke: var(--ink-2); stroke-width: 1.8; }
.uc-warranty .chev   { width: 18px; height: 18px; flex: none; margin-left: auto;
                       fill: none; stroke: var(--ink-2); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

.uc-hero-cta { padding: 13px 30px; font-size: 15.5px; }

/* Capped, because the column it sits in grows with the page: at a 1500px
   window it would otherwise hand this illustration a ~1000px canvas and the
   artwork would tower over the booking controls beside it. Left-aligned and
   sized to the main body column, so it lines up with the "Select a service"
   card directly below rather than floating off-centre above it. */
.uc-hero-media {
  position: relative; border-radius: 16px; overflow: hidden;
  background: var(--bg-soft);
  max-width: 600px; margin-inline: 0;
}
.uc-hero-media img { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }

.uc-scrollbar {
  position: absolute; left: 22px; right: 22px; bottom: 14px;
  height: 3px; border-radius: 999px; background: rgba(255,255,255,.42);
}
.uc-scrollbar span { display: block; width: 58%; height: 100%; border-radius: 999px; background: #fff; }

/* The appliance types in place of the photograph — the homepage's category
   tiles, one column per type up to four (service.php sets --cols, so three
   microwave types fill the row instead of leaving a gap), each opening its
   booking popup. The .cat-grid rules further down set three columns at every
   phone width; these doubled classes outrank them. Each photo fills its column
   rather than the homepage's fixed 82px, which four columns on a phone could
   not hold. */
.uc-types { background: #fff; }
.cat-grid.cat-grid-4 { grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); }
.cat-grid-4 .cat-tile { padding-inline: 2px; }
.cat-grid-4 .cat-tile .emoji.has-img { width: 100%; max-width: 100%; }
/* Some washing machine photos are taller than wide, with the machine in the
   lower part of the frame, and a square crop from the middle cut its feet
   off. Square photos are not cropped, so this moves only the tall ones. */
.cat-grid-4 .cat-tile .emoji img.cat-photo { object-position: 50% 75%; }

/* Beside the title, the card takes the whole column — no 600px cap, which
   left the four photos at thumbnail size with half the row empty — and its
   heading and names grow to match. Phones keep the compact row above. */
@media (min-width: 941px) {
  .uc-types { padding: 26px 28px 28px; }
  .uc-types h3 { font-size: 14px; margin-bottom: 18px; }
  .cat-grid.cat-grid-4 { gap: 20px; }
  .cat-grid-4 .cat-tile span:not(.emoji) { font-size: 16px; }
}

/* ------------------------------------------------------------------- body */
.uc-body {
  display: grid; grid-template-columns: 380px minmax(0, 1fr) 400px;
  gap: 44px; align-items: start; padding-bottom: 64px;
}
.uc-rail-left { min-height: 1px; }

.uc-main { display: grid; gap: 24px; min-width: 0; }

/* The service page's middle column is about 530px wide, so the four-across
   steps grid and the three-across benefits grid from the homepage were giving
   each card roughly 115px — one word per line, cards four times taller than
   they needed to be. Both go two-up here.

   Scoped to .uc-main rather than fixed globally: the homepage journey rail is
   drawn as one horizontal line through four badges, and letting that grid
   wrap would leave the rail running through empty space. */
.uc-main .steps { grid-template-columns: repeat(2, 1fr); gap: 14px; }
.uc-main .step { padding: 18px; }
.uc-main .benefit-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }

@media (max-width: 1150px) {
  .uc-main .steps,
  .uc-main .benefit-grid { grid-template-columns: 1fr; }
}

.uc-card {
  border: 1px solid var(--line); border-radius: 16px; background: #fff;
  padding: 32px 40px 36px;
}
.uc-card-title { font-size: 28px; letter-spacing: -0.03em; margin-bottom: 6px; }
.uc-card > .steps, .uc-card > .list-cards, .uc-card > .benefit-grid,
.uc-card > .rating-summary, .uc-card > .faq { margin-top: 24px; }
.uc-card > .review-grid { margin-top: 16px; }
.uc-card > p { margin-top: 12px; }
.uc-card .rating-summary { border: 0; padding: 0; }

.uc-group {
  font-size: 17px; font-weight: 700; margin: 26px 0 -6px;
  padding-top: 22px; border-top: 1px solid var(--line-soft);
}
.uc-card-title + .uc-group { border-top: 0; padding-top: 0; margin-top: 22px; }

/* ------------------------------------------------------- bookable row */
.uc-row {
  display: flex; gap: 36px; align-items: flex-start;
  padding: 30px 0; border-bottom: 1px solid var(--line-soft);
}
.uc-row:last-child { border-bottom: 0; padding-bottom: 4px; }
.uc-row-main { flex: 1; min-width: 0; }
.uc-row-main h4 { font-size: 17px; margin-bottom: 9px; }

.uc-row-rating { display: inline-flex; align-items: center; gap: 7px; font-size: 14px; color: var(--ink-2); }
.uc-row-rating .star { width: 14px; height: 14px; }
.uc-row-rating span:last-child { text-decoration: underline; text-underline-offset: 3px; }
.star-badge {
  width: 17px; height: 17px; border-radius: 50%; flex: none;
  background: var(--brand); display: grid; place-items: center;
}
.star-badge .s { width: 9px; height: 9px; fill: #fff; }

.uc-row-price { font-size: 15px; font-weight: 600; margin-top: 9px; }
.uc-row-price .uc-dur { font-weight: 400; color: var(--muted); }

.uc-dash { border: 0; border-top: 1px dashed #DDD6FE; margin: 16px 0; }

.uc-bullets { list-style: disc; padding-left: 19px; display: grid; gap: 7px; }
.uc-bullets li { font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.uc-bullets li::marker { color: #A78BFA; }

.uc-details { margin-top: 14px; font-size: 14px; font-weight: 600; color: var(--brand); }
.uc-details:hover { text-decoration: underline; }

/* thumbnail + overlapping Add button */
.uc-row-side { width: 160px; flex: none; display: flex; flex-direction: column; align-items: center; }
.uc-thumb {
  width: 160px; height: 160px; border-radius: 12px; background: #EDE9FE;
  display: grid; place-items: center; overflow: hidden;
}
.uc-thumb img { width: 118px; height: 118px; object-fit: contain; }
/* A photograph (service.php marks it) fills the whole square instead. The
   extra class outranks every smaller-screen .uc-thumb img size below. */
.uc-thumb img.uc-photo { width: 100%; height: 100%; object-fit: cover; }

.uc-add-wrap { position: relative; z-index: 2; margin-top: -21px; }
.uc-add-wrap .add-btn {
  width: 104px; padding: 10px 0; border-radius: 9px;
  background: #fff; box-shadow: 0 2px 8px rgba(30, 27, 75,.10);
}
.uc-add-wrap .stepper { width: 104px; background: #fff; box-shadow: 0 2px 8px rgba(30, 27, 75,.10); }
.uc-add-wrap .stepper button { width: 30px; height: 38px; }

.uc-options { font-size: 13px; color: var(--muted); margin-top: 10px; }
.uc-options.is-added { color: var(--brand); font-weight: 600; }

/* ===========================================================================
   Service popup
   =========================================================================== */
/* overflow-y, NOT the overflow shorthand.

   The shorthand sets both axes, which overwrote the `overflow-x: clip` guard
   set on body at the top of this file — every time a modal opened, the page
   quietly swapped to a different containment mode from the rest of the site.
   `hidden` also makes the body a scroll container, which is what silently
   breaks `position: sticky` on anything inside it. Only the vertical axis
   needs locking here: the point is to stop the page behind the modal from
   scrolling. */
body.modal-open { overflow-y: hidden; }

.uc-modal { position: fixed; inset: 0; z-index: 120; display: flex;
            align-items: flex-start; justify-content: center; padding: 62px 20px 20px; }
.uc-modal-backdrop { position: absolute; inset: 0; background: rgba(12, 14, 20, .62); }

.uc-modal-sheet {
  position: relative; width: 100%; max-width: 560px; max-height: calc(100vh - 82px);
  background: #fff; border-radius: 16px; overflow: hidden;
  box-shadow: 0 24px 70px rgba(0,0,0,.34);
  display: flex; flex-direction: column;
  animation: uc-pop .2s ease;
}
@keyframes uc-pop { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* Sits over the hero rather than floating above the sheet: the sheet is
   overflow:hidden so it would clip anything with a negative offset, which is
   exactly what used to happen — the close button rendered nowhere. */
.uc-modal-x {
  position: absolute; top: 12px; right: 12px; z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; background: #fff;
  display: grid; place-items: center; box-shadow: 0 2px 10px rgba(0,0,0,.22);
}
.uc-modal-x svg { width: 17px; height: 17px; fill: var(--ink); }
.uc-modal-x:hover { background: #EDE9FE; }

.uc-modal-scroll { overflow-y: auto; overscroll-behavior: contain; flex: 1; min-height: 0; }

/* sticky bar that appears once something on this card is selected */
.uc-modal-foot {
  flex: none; display: flex; align-items: center; gap: 12px;
  padding: 14px 18px; border-top: 1px solid var(--line); background: #fff;
  box-shadow: 0 -4px 16px rgba(30, 27, 75,.07);
}
.uc-foot-count {
  width: 28px; height: 28px; flex: none; border-radius: 50%;
  background: var(--bg-soft); color: var(--ink);
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.uc-foot-total { font-size: 16px; font-weight: 700; }
.uc-foot-done { margin-left: auto; min-width: 168px; padding: 13px 28px; font-size: 15px; }

.uc-modal-hero { position: relative; background: var(--bg-soft); }
.uc-modal-hero img { width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; }

.uc-modal-pad { padding: 20px 24px; }
.uc-modal-title { font-size: 21px; letter-spacing: -0.02em; margin-bottom: 8px; }
.uc-modal-h2 { font-size: 22px; letter-spacing: -0.02em; margin-bottom: 18px; }
.uc-modal-sep { height: 1px; background: var(--line); }
.uc-modal-sep.hide, .uc-modal-options-wrap.hide { display: none; }

/* rate-card strip */
.uc-ratecard {
  display: flex; align-items: center; gap: 12px; margin-top: 15px;
  padding: 13px 15px; border-radius: 10px; background: var(--bg-soft);
}
.uc-ratecard:hover { background: #EDE9FE; }
.uc-cover { display: inline-flex; align-items: center; gap: 5px; font-size: 14.5px; color: var(--brand-dark); }
.uc-cover svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }
.uc-cover b { font-weight: 800; }
.uc-ratecard-label { font-size: 14px; color: var(--ink); }
.uc-ratecard .chev { width: 18px; height: 18px; margin-left: auto; flex: none;
                     fill: none; stroke: var(--ink-2); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

/* Issue options, three to a row and as many rows as there are options, so
   every one is in view at once. They used to sit in one sideways-scrolling
   strip, and the customer had to page through it with an arrow to find their
   problem. The arrows stay in the markup: app.js shows them only when the row
   overflows, which a wrapping grid never does. */
.uc-modal-options-wrap { padding-left: 0; padding-right: 0; }
.uc-opt-scroller { position: relative; }
.uc-opt-row {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px;
  padding: 2px 24px 4px;
}

.uc-opt {
  min-width: 0; padding: 14px;
  border: 1px solid var(--line); border-radius: 11px; background: #fff;
  display: flex; flex-direction: column;
}
.uc-opt h4 { font-size: 14.5px; line-height: 1.3; margin-bottom: 7px; }
.uc-opt-rating { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--muted); }
.uc-opt-rating .star { width: 12px; height: 12px; fill: var(--star); flex: none; }
.uc-opt-price { font-size: 14px; font-weight: 600; margin: 9px 0 12px; }
.uc-opt-price small { display: block; font-size: 11.5px; font-weight: 500; color: var(--muted); margin-bottom: 2px; }
.uc-opt .add-btn, .uc-opt .stepper { width: 100%; margin-top: auto; }
.uc-opt .stepper { background: color-mix(in srgb, var(--brand) 9%, #fff); }
.uc-opt .stepper button { height: 34px; }
.uc-opt .stepper button:hover { background: color-mix(in srgb, var(--brand) 16%, #fff); }

.uc-opt-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 40px; height: 40px; border-radius: 50%; background: #fff;
  display: grid; place-items: center; box-shadow: 0 2px 12px rgba(30, 27, 75,.20);
}
.uc-opt-nav svg { width: 19px; height: 19px; fill: none; stroke: var(--ink);
                  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.uc-opt-nav:hover { background: #EDE9FE; }
.uc-opt-prev { left: 10px; }
.uc-opt-next { right: 10px; }

/* our process */
/* margin, padding: the reset above covers ul only, so an ol kept the
   browser's 40px indent and 1em margins and the steps sat off the heading. */
.uc-process { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.uc-process li { display: flex; gap: 16px; position: relative; padding-bottom: 22px; }
.uc-process li:last-child { padding-bottom: 0; }
.uc-process li::before {
  content: ""; position: absolute; left: 14px; top: 30px; bottom: 6px;
  width: 1.5px; background: var(--line);
}
.uc-process li:last-child::before { display: none; }
.uc-process .n {
  width: 29px; height: 29px; flex: none; border-radius: 50%; position: relative; z-index: 1;
  border: 1.5px solid var(--line); background: #fff;
  display: grid; place-items: center; font-size: 13px; font-weight: 600; color: var(--ink-2);
}
.uc-process h4 { font-size: 14.5px; margin-bottom: 3px; }
.uc-process p  { font-size: 13.5px; color: var(--muted); line-height: 1.5; }

/* brand wall */
.uc-brands { display: grid; grid-template-columns: repeat(3, 1fr); gap: 11px; }
/* Near-white rather than the lilac wash: brand logos are drawn for a white
   ground, and a tinted tile shifts their colours. The border keeps the tile
   readable against the card without darkening it. */
.uc-brand {
  height: 78px; border-radius: 10px;
  background: #F5F3FF; border: 1px solid var(--line-soft);
  display: grid; place-items: center; padding: 10px;
}
.uc-brand span {
  font-size: 14.5px; font-weight: 700; letter-spacing: -0.01em; color: var(--ink-2); text-align: center;
}
.uc-brand img { max-height: 34px; width: auto; object-fit: contain; }
.uc-brand.is-more span { font-weight: 500; color: var(--muted); }
.uc-brand-note { margin-top: 15px; font-size: 12.5px; color: var(--muted); line-height: 1.55; }

/* On a phone this becomes a bottom sheet. A centred dialog is a desktop
   convention; on a handset the sheet rises from the bottom edge, keeps its
   Done bar within thumb reach, and rounds only its top corners. The popup
   itself is the same one the desktop opens — only its presentation changes. */
@media (max-width: 640px) {
  .uc-modal { align-items: flex-end; padding: 0; }
  .uc-modal-sheet {
    width: 100%; max-width: none;
    max-height: 88vh;
    max-height: 88dvh;          /* dvh discounts the browser chrome on iOS */
    border-radius: 20px 20px 0 0;
    animation: uc-sheet-up .26s cubic-bezier(.32, .72, 0, 1);
  }
  .uc-modal-pad { padding: 20px 18px; }
  /* The option row brings its own side padding; without this the .uc-modal-pad
     above adds a second one and the cards sit inset from the title. */
  .uc-modal-options-wrap { padding-left: 0; padding-right: 0; }
  .uc-modal-title { font-size: 21px; }
  .uc-modal-h2 { font-size: 22px; }
  .uc-opt-row { padding: 2px 18px 4px; }

  /* The arrows are a mouse affordance; a phone never needs them. */
  .uc-opt-nav { display: none; }

  .uc-modal-foot { padding: 12px 14px calc(12px + env(safe-area-inset-bottom)); gap: 10px; }
  .uc-foot-done { min-width: 0; flex: 1; padding: 12px 18px; }
  .uc-brands { gap: 10px; }
  .uc-brand { height: 80px; }
  .uc-brand span { font-size: 14px; }
}

@keyframes uc-sheet-up { from { transform: translateY(100%); } to { transform: none; } }

/* ===========================================================================
   Phone home — brand band on top, primary nav under the thumb
   Both blocks are phone-only. On desktop the header already carries location,
   search and navigation, so shipping them twice would just be clutter.
   =========================================================================== */
.m-top, .tabbar { display: none; }

@media (max-width: 640px) {

  /* ---------------------------------------------------------- search band
     White, continuing the header above it rather than a purple slab: the bar
     reads as one block of brand, location, search — not two stacked bands in
     different colours. */
  .m-top {
    display: block; margin-bottom: 18px;
    background: #fff; border-bottom: 1px solid var(--line);
  }
  /* Space above, so the field is clearly its own control rather than a second
     line of the address sitting right on top of it. */
  .m-top .wrap { padding-top: 12px; padding-bottom: 13px; }

  /* Field and button on one row, the same arrangement the desktop header uses,
     and the same micro-interactions: the edge brightens on hover, a ring blooms
     on focus, and the magnifier steps aside as you start typing. */
  .m-search { display: flex; align-items: stretch; gap: 8px; }
  .m-search-field { position: relative; flex: 1; min-width: 0; }
  .m-search svg {
    position: absolute; left: 13px; top: 50%; transform: translate(0, -50%);
    width: 17px; height: 17px; fill: none; stroke: var(--muted);
    stroke-width: 2; stroke-linecap: round;
    transition: transform 220ms var(--ease-out-soft), stroke 220ms var(--ease-out-soft);
  }
  .m-search-field:focus-within svg { transform: translate(2px, -50%); stroke: var(--brand); }

  .m-search-go {
    flex: none; padding: 0 18px; border: 0; border-radius: 10px; cursor: pointer;
    background: linear-gradient(135deg, var(--brand), var(--brand-dark));
    color: #fff; font: inherit; font-size: 14px; font-weight: 700; white-space: nowrap;
    box-shadow: 0 1px 2px rgba(46, 16, 101, .2);
    transition: filter 200ms var(--ease-out-soft), box-shadow 200ms var(--ease-out-soft),
                transform 200ms var(--ease-out-soft);
  }
  .m-search-go:active { transform: scale(.97); filter: brightness(1.08); }

  .m-search input {
    width: 100%; height: 44px; padding: 0 14px 0 38px;
    font-size: 16px;              /* anything smaller and iOS zooms on focus,
                                     so the box shrinks and the text doesn't */
    /* Brand-tinted edge rather than the grey hairline — the search field is
       the one thing on this bar you are meant to reach for. */
    border: 1.5px solid #C4B5FD; border-radius: 10px; background: #fff; outline: none;
    transition: border-color 200ms var(--ease-out-soft), box-shadow 220ms var(--ease-out-soft);
  }
  /* A ring rather than a thicker border, so the field never changes size and
     the row cannot shift as you tap into it. */
  .m-search input:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 4px rgba(109, 40, 217, .12);
  }

  @media (prefers-reduced-motion: reduce) {
    .m-search svg, .m-search input, .m-search-go { transition: none; }
    .m-search-field:focus-within svg { transform: translate(0, -50%); }
  }

  .hero { padding-top: 0; }

  /* The homepage header used to take the brand colour so it and the search
     band below read as one purple slab. Both are white now: the brand name,
     the address and the search field are one block of the same surface, which
     is calmer and lets the purple mean "this is a control" instead.

     The nav strip still steps aside on the homepage — the category grid right
     below it says the same thing. */
  body.is-home .site-header { border-bottom-color: transparent; }
  body.is-home .nav-strip { display: none; }
  body.is-home .m-top { margin-top: -1px; }

  /* Round, so it reads as an action next to the wordmark rather than a second
     button competing with Search below it. */
  .cart-btn {
    width: 38px; height: 38px; padding: 0; border-radius: 50%;
    justify-content: center; flex: none;
  }
  .cart-btn span:not(.cart-badge) { display: none; }   /* icon only */
  .cart-btn .cart-badge { top: -4px; right: -4px; }

  /* The header wraps to two rows so the address sits under the brand name
     rather than taking a row of its own. Applies to EVERY phone page, not just
     the homepage — the same .loc-pill the desktop header shows, stripped of
     its box and shrunk, never a second copy.

     Kept deliberately quiet: it is a status line confirming where a booking
     would land, not something to compete with the search field below it. */
  /* var(--gutter) on the sides, NOT 0.
     This is .wrap as well as .header-inner, and the shorthand used to write a
     horizontal 0 here, silently cancelling the page gutter for the header row
     alone. Two consequences: the brand and the cart sat hard against the
     screen edges while everything below them was inset, and .cart-badge —
     which hangs 4px outside the cart button by design — had no padding left
     to hang into, so it ended up past the viewport. Three pixels of overflow
     was enough to make Chrome widen the layout viewport and shift the whole
     page sideways. */
  .header-inner { flex-wrap: wrap; height: auto; padding: 9px var(--gutter) 6px; }
  .loc-pill {
    display: flex; order: 3; flex-basis: 100%; width: auto; max-width: none;
    padding: 0 0 0 44px;        /* 44px lines the pin up under the logo text */
    gap: 4px; min-height: 0;
    border: 0; background: none;
    font-size: 10.5px; line-height: 1.45; opacity: .85;
  }
  .loc-pill .pin,
  .loc-pill .caret { width: 11px; height: 11px; opacity: .7; }
  .loc-pill .caret { margin-left: 0; }
  .loc-pill span { min-width: 0; }
  .loc-pill:hover { transform: none; box-shadow: none; }

  /* The homepage header used to be purple, so this line was forced white.
     It is white-on-white now that the header is not — removed. The pin takes
     the brand colour so it reads as the location marker it is. */
  .loc-pill .pin { color: var(--brand); opacity: 1; }

  /* The four photo tiles are desktop decoration. On a phone they pushed the
     services — the thing people came to book — most of a screen further down. */
  .hero-collage { display: none; }

  /* ------------------------------------------------- app-style category tiles
     The desktop rules above already run the artwork full width, so a phone
     only has to drop the card chrome around the grid and shrink the label. */
  .cat-card { border: 0; padding: 0; background: none; box-shadow: none; }
  .cat-card h3 { margin-bottom: 12px; }
  .cat-grid { gap: 16px 10px; }
  .cat-tile { gap: 8px; }
  .cat-tile:hover { transform: none; }
  .cat-tile span { font-size: 12.5px; font-weight: 500; line-height: 1.3; }

  /* ------------------------------------------------------------ tab bar */
  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 65;
    background: #fff; border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -2px 14px rgba(30, 27, 75, .07);
  }
  .tabbar a {
    position: relative; flex: 1;
    display: grid; justify-items: center; align-content: center; gap: 3px;
    min-height: 56px; padding: 8px 4px;
    font-size: 11px; font-weight: 500; color: var(--muted);
  }
  .tabbar a.active { color: var(--brand); font-weight: 700; }
  .tabbar svg { width: 22px; height: 22px; fill: currentColor; }
  .tab-badge {
    position: absolute; top: 6px; left: 50%; margin-left: 3px;
    min-width: 17px; height: 17px; padding: 0 4px; border-radius: 999px;
    background: var(--accent); color: #fff;
    font-size: 10.5px; font-weight: 700; display: grid; place-items: center;
  }

  /* ---------------------------------------------- service cards on a phone
     Text left, photo right, and the whole card short enough that all three
     services sit in one screen. Stacked full-width, one card filled the
     viewport on its own and you had to scroll to learn a second one existed.

     DOM order is thumb-then-body, so the columns are assigned explicitly
     rather than reversed — no `order`, nothing for a screen reader or the tab
     sequence to disagree with. */
  .appliance-grid { grid-template-columns: 1fr; gap: 12px; }
  .appliance-card {
    display: grid; grid-template-columns: minmax(0, 1fr) 96px;
    gap: 12px; padding: 12px; align-items: start;
  }
  .appliance-card:hover { transform: none; }

  .appliance-thumb {
    grid-column: 2; grid-row: 1;
    aspect-ratio: 1; height: auto; border-radius: 10px;
  }
  .appliance-thumb img { object-position: center 45%; }

  .appliance-body { grid-column: 1; grid-row: 1; padding: 2px 0 0; gap: 0; }
  .appliance-body h3 { font-size: 15px; margin-bottom: 4px; }
  .appliance-body .desc {
    font-size: 12.5px; line-height: 1.5; margin: 0 0 10px;
    /* Two lines keeps every card the same height whatever the copy runs to. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Price and button below the text, still in the left column. */
  .appliance-meta {
    padding-top: 9px; border-top: 1px solid var(--line-soft);
    gap: 10px; flex-wrap: wrap;
  }
  .appliance-meta .price { font-size: 14px; }
  .appliance-meta .price small { font-size: 11px; }
  .appliance-meta .btn { padding: 7px 14px; font-size: 12.5px; }

  /* Room for the tab bar, which is fixed and would otherwise cover the last
     few lines of every page. */
  body { padding-bottom: calc(58px + env(safe-area-inset-bottom)); }

  /* No bar, no gap. Without this the checkout ends in 58px of empty space
     reserved for something that is not there. */
  body.no-tabbar { padding-bottom: env(safe-area-inset-bottom); }

  /* Service and checkout already pin a total/CTA bar to the bottom. Two
     stacked bars would eat a third of the screen, so the contextual one wins. */
  body.has-mobile-bar .tabbar { display: none; }
}

@media (max-width: 360px) {
  .cat-grid { grid-template-columns: repeat(3, 1fr); }
  .tabbar a { font-size: 10.5px; }
}

/* ---------------------------------------------------------------- sidebar */
.uc-side { position: sticky; top: 104px; display: grid; gap: 20px; }
.uc-side-card { border: 1px solid var(--line); border-radius: 16px; background: #fff; padding: 24px; }

.uc-promise { position: relative; }
.uc-promise h3 { font-size: 21px; margin-bottom: 18px; padding-right: 84px; }
.uc-quality { position: absolute; top: 20px; right: 20px; width: 76px; height: 76px; }
.uc-promise ul { display: grid; gap: 13px; }
.uc-promise li { display: flex; align-items: center; gap: 12px; font-size: 15px; }
.uc-promise li svg { width: 19px; height: 19px; flex: none; fill: var(--ink); }

.uc-cart-empty { text-align: center; padding: 18px 0 10px; }
.uc-cart-empty img { margin: 0 auto 14px; width: 110px; height: auto; }
.uc-cart-empty p { font-size: 15px; color: var(--muted); }

.uc-cart-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.uc-cart-head h3 { font-size: 17px; }
.uc-cart-head button { font-size: 12.5px; color: var(--muted); }
.uc-cart-head button:hover { color: var(--accent); }

.uc-side-card .rail-usp { padding: 0; }

/* ------------------------------------------------------------ responsive */
@media (max-width: 1400px) {
  .uc-body { grid-template-columns: 300px minmax(0, 1fr) 360px; gap: 32px; }
  .uc-hero { grid-template-columns: 300px minmax(0, 1fr); gap: 32px; }
  .uc-title-row h1 { font-size: 36px; }
}

@media (max-width: 1150px) {
  /* drop the empty title rail, keep main + sidebar */
  .uc-body { grid-template-columns: minmax(0, 1fr) 340px; }
  .uc-rail-left { display: none; }
}

@media (max-width: 940px) {
  .uc-hero { grid-template-columns: 1fr; gap: 24px; padding: 26px 0 30px; }
  .uc-hero-media, .uc-types { order: -1; }
  .uc-body { grid-template-columns: 1fr; }
  .uc-side { position: static; }
  .uc-cart-card { display: none; }
  .uc-card { padding: 26px 24px 30px; }
  .uc-card-title { font-size: 23px; }
}

@media (max-width: 640px) {
  .uc-page { padding: 0 16px; }
  .uc-title-row { flex-direction: column; gap: 12px; }
  .uc-title-row h1 { font-size: 28px; }
  .uc-badges { grid-auto-flow: column; justify-content: start; gap: 8px; }
  .uc-hero-cta { width: 100%; }
  .uc-card { padding: 22px 18px 26px; border-radius: 14px; }
  .uc-card-title { font-size: 21px; }
  .uc-row { gap: 16px; }
  .uc-row-side { width: 108px; }
  .uc-thumb { width: 108px; height: 108px; }
  .uc-thumb img { width: 80px; height: 80px; }
  .uc-add-wrap .add-btn, .uc-add-wrap .stepper { width: 92px; }
}

/* ===========================================================================
   Phone finishing pass
   Covers the 320–640px range, including narrow Android devices and phones
   with a safe-area inset.  The wider breakpoints above retain the tablet and
   desktop layouts.
   =========================================================================== */
@media (max-width: 640px) {
  /* The sideways-scroll guard used to live here, which meant it only applied
     below 640px — a tablet or a half-width desktop window had none. It is a
     base rule now, near the top of this file. */
  body { font-size: 14px; }

  /* auto, not a fixed 68px. On a phone the header wraps to two rows (brand,
     then the address — see .loc-pill above), and a fixed height left the
     address row hanging below the white bar, over the page, once you
     scrolled. min-height keeps the header at least as tall as before. */
  .header-inner { height: auto; min-height: 68px; gap: 12px; }
  .logo { font-size: 17px; min-width: 0; }
  .logo-text { min-width: 0; }
  .logo-text small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .header-actions { margin-left: auto; }
  .cart-btn { padding: 9px 11px; gap: 6px; }
  /* Runs to the screen edges so the last item is visibly cut off — that is
     what tells you the strip scrolls. The negative margin MUST cancel the
     wrap's gutter exactly; derived from the same variable so it always does,
     whatever the breakpoint sets it to. */
  .nav-strip ul {
    gap: 20px;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
  }
  .nav-strip a { padding: 11px 0; }

  .hero-collage { grid-template-rows: 112px 112px; gap: 10px; }
  .collage-cell .c-emoji { font-size: 36px; }
  .collage-cell .c-label { left: 10px; bottom: 8px; font-size: 11px; }
  .cat-card { padding: 16px; }
  .cat-tile { min-height: 92px; padding: 12px 6px; }

  .trustbar-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 12px; padding: 20px 0; }
  .trust-item .lbl { font-size: 12px; line-height: 1.35; }
  .section-head { align-items: flex-start; flex-direction: column; gap: 8px; margin-bottom: 20px; }
  .section-head a { min-height: 44px; display: inline-flex; align-items: center; }
  .page-head { padding: 24px 0 18px; }
  .page-head h1 { font-size: 25px; }

  /* .appliance-body / .appliance-meta sizing is not set here any more: below
     640px the card turns horizontal and owns its own padding and button size.
     Redeclaring them in this later block silently undid that. */
  .appliance-meta { align-items: flex-end; }
  .panel-head, .panel-body { padding-left: 16px; padding-right: 16px; }
  .success-card { margin: 28px auto 46px; }
  .success-top, .success-body { padding: 24px 18px; }
  .kv { grid-template-columns: 1fr; gap: 3px; }
  .kv dd { margin-bottom: 10px; }

  .mobile-bar { padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); }
  body.has-mobile-bar { padding-bottom: calc(78px + env(safe-area-inset-bottom)); }
  .mobile-bar .btn { padding: 11px 15px; }
  .toast { bottom: calc(18px + env(safe-area-inset-bottom)); max-width: calc(100% - 32px); text-align: center; }

  .uc-ratecard { align-items: flex-start; flex-wrap: wrap; }
  .uc-ratecard .chev { margin-left: auto; }
  .uc-warranty { padding: 14px; }
}

@media (max-width: 480px) {
  /* .wrap reads this; .nav-strip ul cancels it. Never set one without the other. */
  :root { --gutter: 14px; }
  .uc-page { padding-left: 14px; padding-right: 14px; }
  .header-inner { gap: 8px; }
  .logo-mark { width: 32px; height: 32px; }
  .logo-text small { display: none; }
  .cart-btn span:not(.cart-badge) { display: none; }
  .cart-btn svg { width: 19px; height: 19px; }
  .cart-btn { padding: 9px 10px; }
  /* The .nav-strip ul override that used to sit here is gone — it hard-coded
     -14px, which then failed to follow .wrap down to 12px at 360px. It now
     tracks --gutter automatically. */

  .cat-grid { gap: 8px; }
  .cat-tile span { font-size: 11.5px; }
  .cover-banner { padding: 16px; }
  .cover-banner .cb-icon { display: none; }
  .svc-card { padding: 16px 0; }
  .svc-card .note { align-items: flex-start; }

  /* Cart rows include inline desktop alignment styles, so make their controls
     a full-width second row on a phone. */
  .checkout-layout .svc-card > div:last-child {
    width: 100%; flex-direction: row !important; align-items: center !important;
    justify-content: space-between;
  }
  .checkout-layout .svc-card > div:last-child .rail-stepper { margin: 0; }

  .uc-hero { padding-top: 20px; }
  .uc-title-row h1 { font-size: 25px; }
  .uc-badges { grid-auto-flow: row; grid-template-columns: 1fr 1fr; width: 100%; }
  .badge-instant, .badge-eta { padding: 7px 8px; font-size: 12px; }
  .uc-card { padding: 20px 14px 24px; }
  .uc-row { flex-direction: column; gap: 12px; }
  .uc-row-side { width: 100%; flex-direction: row; justify-content: space-between; align-items: center; }
  .uc-thumb { width: 88px; height: 88px; }
  .uc-thumb img { width: 66px; height: 66px; }
  .uc-add-wrap { margin-top: 0; }
  .uc-add-wrap .add-btn, .uc-add-wrap .stepper { width: 112px; }
}

@media (max-width: 360px) {
  /* This 12px is the one that broke it: .wrap shrank here, .nav-strip ul was
     still cancelling 14px, and the strip ended 2px past the viewport. */
  :root { --gutter: 12px; }
  .uc-page { padding-left: 12px; padding-right: 12px; }
  .logo { font-size: 16px; }
  .cart-btn { padding: 8px; }
  /* Three appliances, three across. Two columns here left the third tile on a
     row of its own. minmax(0, …) lets a column narrow below the 82px photo on
     a 320px phone, and max-width lets the photo narrow with it, instead of the
     row pushing the page sideways. */
  .cat-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cat-tile .emoji.has-img { max-width: 100%; }
  .footer-grid { grid-template-columns: 1fr; gap: 24px; }
  .footer-bottom { gap: 8px; }
  .mobile-bar .mb-inner { gap: 8px; }
  .mobile-bar .btn { padding: 10px 11px; font-size: 13px; }
  .uc-modal-foot { flex-wrap: wrap; }
  .uc-foot-total { flex: 1; }
  .uc-foot-done { flex-basis: 100%; }
}

/* ===========================================================================
   Touch targets
   The breakpoints above size the layout; this sizes what a thumb has to hit.
   Measured in a 375px viewport, these links came back between 16px and 39px
   tall, against the 44px Apple's HIG and Material both ask for.

   Two techniques, picked per case: links that own their row (footer,
   breadcrumb) get real height, because there the extra space reads as
   deliberate. Links sitting inside a dense card grow via padding plus an
   equal negative margin — the hit area expands while the page looks
   identical, so the service list doesn't stretch out.
   =========================================================================== */
@media (max-width: 640px) {
  /* iOS zooms the page whenever a focused field is under 16px, which throws
     the checkout sideways mid-form. The visible size barely changes. */
  .field input, .field textarea, .field select { font-size: 16px; }

  .logo, .cart-btn { min-height: 44px; }

  .footer-col a, .footer-contact a {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  .footer-col li  { margin-bottom: 0; }
  .footer-contact { gap: 0; }
  .footer-seo .links a { display: inline-flex; align-items: center; min-height: 44px; }

  /* Padding grows the target, the negative margin gives back the space. */
  .crumbs a      { padding: 11px 0; margin: -11px 0; }
  .appliance-body h3 a { display: inline-block; padding: 11px 0; margin: -11px 0; }
  .uc-row-rating { padding: 11px 0; margin: -11px 0; }
  .uc-rating     { padding: 11px 0; margin: -11px 0 11px; }
  .uc-details    { padding: 10px 0; margin: 4px 0 -10px; }

  .collage-cell .c-ico { width: 40px; height: 40px; }
}

/* ===========================================================================
   Service page — compact phone layout
   Choosing a service is the whole job of this page, so everything around the
   list gives back vertical space: a shorter hero, tighter cards, and rows that
   stay side-by-side instead of stacking. Roughly two more options per screen.
   =========================================================================== */
@media (max-width: 640px) {
  .uc-hero { padding: 12px 0 16px; gap: 14px; }
  .uc-hero-media img { aspect-ratio: 16 / 9; }
  .uc-title-row { gap: 8px; margin-bottom: 8px; }
  .uc-title-row h1 { font-size: 22px; }
  .badge-instant, .badge-eta { padding: 5px 10px; font-size: 12px; }
  .uc-rating { font-size: 13px; margin-bottom: 10px; }
  .uc-warranty { padding: 10px 12px; font-size: 13.5px; }
  .uc-hero-cta { padding: 11px 18px; font-size: 14.5px; }

  .uc-main { gap: 14px; }
  .uc-card { padding: 16px 14px 18px; border-radius: 14px; }
  .uc-card-title { font-size: 18px; }
  .uc-group { font-size: 15px; margin: 16px 0 -4px; padding-top: 14px; }

  /* Row stays horizontal — stacking the thumbnail under the copy cost a whole
     extra screen for no gain, and the thumbnail reads fine at this size. */
  .uc-row { flex-direction: row; align-items: flex-start; gap: 12px; padding: 14px 0; }
  .uc-row-main h4 { font-size: 15px; margin-bottom: 5px; }
  .uc-row-rating { font-size: 12.5px; }
  .uc-row-price { font-size: 13.5px; margin-top: 6px; }
  .uc-dash { margin: 9px 0; }
  .uc-bullets { gap: 4px; padding-left: 16px; }
  .uc-bullets li { font-size: 12.5px; line-height: 1.45; }
  .uc-details { font-size: 13px; }

  .uc-row-side { width: 96px; flex: none; flex-direction: column; align-items: center; }
  .uc-thumb { width: 96px; height: 96px; border-radius: 10px; }
  .uc-thumb img { width: 72px; height: 72px; }
  .uc-add-wrap { margin-top: -16px; }
  .uc-add-wrap .add-btn, .uc-add-wrap .stepper { width: 82px; }
  .uc-add-wrap .add-btn { padding: 8px 0; font-size: 13px; }
  .uc-add-wrap .stepper button { height: 34px; }
  .uc-options { font-size: 12px; margin-top: 6px; }

  .uc-process li { padding-bottom: 16px; }
  .uc-process h4 { font-size: 14px; }
  .uc-process p { font-size: 13px; }
  .uc-brand { height: 66px; }
}

@media (max-width: 360px) {
  .uc-row-side { width: 84px; }
  .uc-thumb { width: 84px; height: 84px; }
  .uc-thumb img { width: 62px; height: 62px; }
  .uc-add-wrap .add-btn, .uc-add-wrap .stepper { width: 76px; }
}


/* ===========================================================================
   Phone density
   One pass across every page: sections, cards and headings all give back
   vertical space so more of each page lands on the first screen. The touch
   targets set earlier are deliberately untouched — this trims padding and
   type size, never the area a thumb has to hit.
   =========================================================================== */
@media (max-width: 640px) {

  /* ---- shared rhythm ---------------------------------------------------- */
  .section { padding: 22px 0; }
  .section-head { margin-bottom: 14px; gap: 4px; }
  .section-head h2 { font-size: 18px; }
  .section-head p { font-size: 12.5px; margin-top: 3px; }
  .page-head { padding: 14px 0 10px; }
  .page-head h1 { font-size: 21px; }
  .page-head p { font-size: 13px; }
  .crumbs { padding: 8px 0; font-size: 12px; }

  /* ---- home: hero, categories, collage ---------------------------------- */
  .hero { padding: 12px 0 18px; }
  .hero-grid { gap: 20px; }
  .hero h1 { margin-bottom: 14px; }
  .cat-card h3 { font-size: 12px; margin-bottom: 10px; }
  .cat-grid { gap: 12px 8px; }
  /* Same 82px square whether the tile carries a flat icon or a fallback
     emoji, so the live and coming-soon rows sit on one grid. */
  .cat-tile .emoji { width: 82px; height: 82px; margin: 0 auto; font-size: 32px; }
  .cat-tile .emoji.has-img { width: 82px; height: auto; }
  .cat-tile span { font-size: 11.5px; }
  .hero-collage { grid-template-rows: 96px 96px; gap: 8px; }
  .collage-cell .c-ico { width: 32px; height: 32px; }
  .collage-cell .c-label { left: 10px; bottom: 8px; font-size: 11px; }

  /* ---- trust bar -------------------------------------------------------- */
  .trustbar-inner { padding: 14px 0; gap: 12px 10px; }
  .trust-item .num { font-size: 17px; }
  .trust-item .lbl { font-size: 11.5px; }

  /* ---- appliance cards ----------------------------------------------------
     Sized once, in the horizontal-card block earlier in this file. This is a
     second max-width:640px block, so anything redeclared here would simply
     replace it — which is exactly what was happening before. Only the
     fallback glyph size is set, and nothing else touches that. */
  .appliance-thumb { font-size: 40px; }

  /* ---- leave a review -----------------------------------------------------
     At desktop sizing this ran to nearly three screens on a phone: a full-width
     illustration, then three benefit rows, then the form. Everything below
     tightens the type and sets the picture beside the reasons instead of
     above them, which is most of the height back. */
  .rev-share { gap: 18px; }
  .rev-eyebrow { padding: 5px 10px; margin-bottom: 9px; font-size: 10px; }
  .rev-share-copy h2 { font-size: 21px; margin-bottom: 8px; }
  .rev-share-copy > p { font-size: 13px; line-height: 1.55; }

  /* Picture left, reasons right — the picture was the single biggest block. */
  .rev-art-row { grid-template-columns: 96px minmax(0, 1fr); gap: 14px; margin-top: 14px; align-items: center; }
  .rev-art { max-width: 96px; }

  .rev-why { gap: 10px; }
  .rev-why .ico { width: 28px; height: 28px; border-radius: 8px; }
  .rev-why .ico svg { width: 14px; height: 14px; }
  .rev-why strong { font-size: 12.5px; }
  .rev-why li > span:last-child { font-size: 11.5px; line-height: 1.35; }

  /* ---- the form ----
     Six stacked rows plus a header ran longer than a phone screen. The height
     comes back from three places: the two short fields share a row, the
     decorative trust badge goes, and the gaps and paddings tighten. Nothing
     is removed that a person has to fill in. */
  .rev-form-card { padding: 13px; }
  .rev-form-head { margin-bottom: 9px; gap: 8px; }
  .rev-form-card h3 { font-size: 14.5px; }
  .rev-form-sub { font-size: 11px; margin-top: 2px; }

  /* Decoration, not a control — the first thing to go when space is short. */
  .rev-trust { display: none; }

  .rev-form { gap: 8px; }
  /* Name and number are both short: side by side even on a narrow handset,
     which is a whole row saved. This deliberately overrides the 560px rule
     further up that stacked them. */
  .rev-form-row { grid-template-columns: 1fr 1fr; gap: 8px; }
  .rev-form label { font-size: 11px; margin-bottom: 3px; }
  .rev-form input, .rev-form textarea, .rev-form select { padding: 7px 10px; font-size: 12.5px; }
  .rev-form textarea { min-height: 52px; line-height: 1.45; }
  .rev-form .btn { padding: 9px 14px; font-size: 13px; }
  .rv-count { right: 8px; bottom: 6px; font-size: 10px; }

  /* The rating still has to be tappable, so the stars stay large while
     everything around them shrinks. */
  .rv-stars { gap: 3px; }
  .rv-stars label svg { width: 26px; height: 26px; }
  .rv-stars-hint { margin-left: 7px; font-size: 11px; }
  .rev-form-note { font-size: 10px; margin-top: -2px; }

  /* ---- the cards below ---- */
  .rev-card { padding: 15px; gap: 11px; }
  .rev-av, .rev-face { width: 36px; height: 36px; }
  .rev-id strong { font-size: 13px; }
  .rev-text { font-size: 13px; line-height: 1.55; -webkit-line-clamp: 3; }
  .rev-tag { font-size: 11px; padding: 4px 10px; }
  .rev-when { font-size: 10.5px; }

  /* ---- steps, promise, closing banner ----------------------------------- */
  .steps { gap: 10px; }
  .step { padding: 14px; }
  .step h4 { font-size: 14.5px; }
  .step p { font-size: 12.5px; }
  .promise { gap: 10px; }
  .promise-item { padding: 13px; gap: 11px; }
  .promise-item h4 { font-size: 14.5px; }
  .promise-item p { font-size: 12.5px; }
  .cover-banner { padding: 14px; gap: 10px; }
  .cover-banner h4 { font-size: 15px; }
  .cover-banner p { font-size: 12.5px; }

  /* ---- cart and checkout ------------------------------------------------ */
  .panel { margin-bottom: 12px; }
  .panel-head { padding-top: 14px; padding-bottom: 12px; }
  .panel-head h3 { font-size: 16px; }
  .panel-body { padding-top: 14px; padding-bottom: 16px; }
  .field { margin-bottom: 12px; }
  .field label { font-size: 12.5px; margin-bottom: 4px; }
  /* 10px keeps the box near 44px tall at the 16px font iOS needs. */
  .field input, .field select { padding: 10px 12px; }
  .field textarea { padding: 10px 12px; min-height: 64px; }
  /* .day-opt and .slot-opt sizing moved to assets/css/checkout.css. */
  .pay-opt { padding: 12px 14px; margin-bottom: 9px; }
  .svc-card { padding: 14px 0; }
  .rail-card { margin-bottom: 12px; }

  /* ---- booking confirmation --------------------------------------------- */
  .success-card { margin: 18px auto 30px; }
  .success-top, .success-body { padding: 18px 16px; }

  /* ---- reviews and lists ------------------------------------------------ */
  .review-card { padding: 14px; }
  .list-card { padding: 14px; gap: 12px; }

  /* ---- footer ------------------------------------------------------------ */
  .site-footer { padding: 26px 0 18px; }
  .footer-grid { margin-bottom: 20px; gap: 18px; }
  .footer-col h4 { font-size: 12px; margin-bottom: 6px; }
  .footer-about p { font-size: 12.5px; margin: 8px 0 10px; }
  .footer-seo { margin-top: 16px; padding-top: 14px; }
  .footer-bottom { padding-top: 14px; font-size: 11.5px; }
}

/* ===========================================================================
   Bottom sheet — compact phone layout
   The sheet that opens from Add / View details had two screens of scroll, a
   third of it spent on a hero repeating the illustration already behind it.
   Everything here is trimmed so the options and the process fit in one view.
   =========================================================================== */
@media (max-width: 640px) {
  .uc-modal-sheet { max-height: 92vh; max-height: 92dvh; }

  /* Repeats the page hero, so it earns much less room here. */
  .uc-modal-hero img { aspect-ratio: 21 / 9; }
  .uc-scrollbar { left: 16px; right: 16px; bottom: 10px; }

  .uc-modal-pad { padding: 13px 16px; }
  .uc-modal-options-wrap { padding-left: 0; padding-right: 0; }   /* see above */
  .uc-modal-title { font-size: 17.5px; margin-bottom: 5px; }
  .uc-modal-h2 { font-size: 17.5px; margin-bottom: 11px; }

  .uc-ratecard { margin-top: 10px; padding: 9px 11px; gap: 9px; border-radius: 9px; }
  .uc-cover { font-size: 12.5px; gap: 4px; }
  .uc-cover svg { width: 13px; height: 13px; }
  .uc-ratecard-label { font-size: 12px; }
  .uc-ratecard .chev { width: 16px; height: 16px; }

  /* option cards, three to a row: a little less padding so a name keeps
     most of its card's width */
  .uc-opt-row { gap: 8px; padding: 2px 16px 4px; }
  .uc-opt { padding: 10px 9px; border-radius: 10px; }
  .uc-opt h4 { font-size: 13px; margin-bottom: 5px; }
  /* nowrap: "4.79 (13K reviews)" was breaking onto a second line and giving
     each card a ragged extra row of height. */
  .uc-opt-rating { font-size: 11px; white-space: nowrap; }
  .uc-opt-rating .star { width: 11px; height: 11px; }
  .uc-opt-price { font-size: 13px; margin: 6px 0 10px; }
  /* One line: "Booking / charge" split in two made every card a row taller. */
  .uc-opt-price small {
    font-size: 10.5px; margin-bottom: 1px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .uc-opt .add-btn { padding: 7px 0; font-size: 12.5px; }
  .uc-opt .stepper button { height: 30px; }

  /* our process */
  .uc-process li { padding-bottom: 13px; gap: 12px; }
  .uc-process .n { width: 24px; height: 24px; font-size: 11.5px; }
  .uc-process li::before { left: 11.5px; top: 26px; }
  .uc-process h4 { font-size: 13.5px; }
  .uc-process p { font-size: 12.5px; line-height: 1.45; }

  /* brand wall */
  .uc-brands { gap: 8px; }
  .uc-brand { height: 56px; border-radius: 8px; padding: 7px; }
  .uc-brand span { font-size: 12px; }
  .uc-brand img { max-height: 26px; }
  .uc-brand-note { margin-top: 10px; font-size: 11.5px; line-height: 1.5; }

  /* sticky footer bar */
  .uc-modal-foot { padding: 9px 12px calc(9px + env(safe-area-inset-bottom)); gap: 9px; }
  .uc-foot-count { width: 24px; height: 24px; font-size: 12px; }
  .uc-foot-total { font-size: 14.5px; }
  .uc-foot-done { padding: 10px 16px; font-size: 14px; }
}

/* ===========================================================================
   Sign-in panel

   Opens when someone adds a service while signed out. Deliberately small and
   quiet: one field, one button, and the reason it is asking. A full-page login
   screen at this moment would read as a wall in front of the booking rather
   than a step in it.
   =========================================================================== */
.auth-overlay {
  position: fixed; inset: 0; z-index: 220;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(30, 27, 75, .55);
  animation: auth-fade 180ms var(--ease-out-soft) both;
}
/* The UA sheet gives [hidden] display:none, but .auth-overlay's own display
   outranks it — so the hidden state has to be stated here. */
.auth-overlay[hidden] { display: none; }

.auth-modal {
  position: relative;
  width: 100%; max-width: 400px;
  background: #fff; border-radius: 18px;
  padding: 30px 26px 24px;
  box-shadow: 0 24px 70px rgba(30, 27, 75, .3);
  animation: auth-rise 240ms var(--ease-out-soft) both;
}

.auth-x {
  position: absolute; top: 12px; right: 14px;
  border: 0; background: none; font-size: 26px; line-height: 1;
  color: var(--muted); cursor: pointer; padding: 2px 6px; border-radius: 8px;
}
.auth-x:hover { color: var(--ink); background: var(--bg-soft); }

.auth-head { text-align: center; margin-bottom: 20px; }
.auth-mark {
  display: inline-grid; place-items: center;
  width: 48px; height: 48px; border-radius: 14px; margin-bottom: 12px;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: #fff;
}
.auth-head h2 { font-size: 20px; margin: 0 0 6px; letter-spacing: -0.02em; }
.auth-head p  { font-size: 13.5px; line-height: 1.5; color: var(--muted); margin: 0; }

.auth-form .field { margin-bottom: 16px; }
.auth-form label {
  display: block; font-size: 13px; font-weight: 600;
  color: var(--ink); margin-bottom: 7px;
}

/* The +91 sits inside the field's border rather than beside it, so the whole
   control reads as one number and the focus ring wraps all of it. */
.auth-phone {
  display: flex; align-items: stretch;
  border: 1.5px solid var(--line); border-radius: 11px;
  background: #fff; overflow: hidden;
  transition: border-color 200ms var(--ease-out-soft),
              box-shadow 220ms var(--ease-out-soft);
}
.auth-phone:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 4px rgba(109, 40, 217, .12);
}
.auth-cc {
  display: grid; place-items: center; padding: 0 12px;
  background: var(--bg-soft); border-right: 1.5px solid var(--line);
  font-size: 15px; font-weight: 600; color: var(--muted);
}
.auth-phone input {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: none;
  padding: 13px 14px;
  font: inherit;
  font-size: 16px;            /* under 16px and iOS zooms the page on focus */
  letter-spacing: .06em;
}
.field.has-error .auth-phone { border-color: #c0392b; }

.auth-form .err { display: block; font-size: 12.5px; color: #c0392b; margin-top: 6px; }

.auth-form .btn-block { padding: 13px; font-size: 15px; }
.auth-form .btn-block:disabled { opacity: .7; cursor: wait; }

.auth-fine {
  margin: 14px 0 0; text-align: center;
  font-size: 11.5px; line-height: 1.55; color: var(--muted);
}
.auth-fine a { color: var(--brand); font-weight: 600; }

@keyframes auth-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes auth-rise {
  from { opacity: 0; transform: translateY(14px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* ---- the footer account line ---- */
.foot-account { display: inline-flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.foot-account strong { font-weight: 600; }
.foot-account button {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-weight: 600; color: inherit;
  text-decoration: underline; text-underline-offset: 3px;
}
.foot-account button:hover { opacity: .8; }

/* On a phone it becomes a bottom sheet, the same shape as the service popup —
   the thumb is already down there and a centred dialog is not. */
@media (max-width: 560px) {
  .auth-overlay { align-items: flex-end; padding: 0; }
  .auth-modal {
    max-width: none; border-radius: 20px 20px 0 0;
    padding: 26px 20px calc(20px + env(safe-area-inset-bottom));
    animation: uc-sheet-up 260ms cubic-bezier(.32, .72, 0, 1) both;
  }
  .auth-head h2 { font-size: 18.5px; }
  .auth-head p  { font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-overlay, .auth-modal { animation: none; }
  .auth-phone { transition: none; }
}

/* The checkout's sign-in strip (.co-signin, .linkish) moved to
   assets/css/checkout.css. The .auth-* panel above stays here: it is rendered
   by includes/footer.php on every page, not just the checkout. */

/* ===========================================================================
   Phone footer — the three link columns in one row
   Stacked one per line, with every link 44px tall, the footer ran to more
   than a screen on a phone. Services, Company and For customers now sit side
   by side under the brand block, in smaller type. Last in the file on
   purpose: it has to beat the earlier phone rules (the one-column grid at
   360px and the 44px link height at 640px).
   =========================================================================== */
@media (max-width: 640px) {
  .footer-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px 12px; }
  .footer-about { grid-column: 1 / -1; }
  .footer-col h4 { font-size: 10.5px; letter-spacing: .05em; margin-bottom: 4px; }
  .footer-col a { font-size: 12px; line-height: 1.3; min-height: 30px; }
}

/* ===========================================================================
   Phone header — the address right under the company name
   It sat about 20px below the name: the first row is 44px tall for the
   cart's touch target, and the header's gap added 8px more. Pulled up so it
   reads as a subtitle of the name. Only at 480px and below, where the tagline
   under the name is hidden — with the tagline showing, this would overlap it.
   The logo and the cart are lifted above the address row, so a tap on their
   lower edge still reaches them and not the address.
   =========================================================================== */
@media (max-width: 480px) {
  .header-inner { row-gap: 0; }
  .loc-pill { margin-top: -10px; }
  .site-header .logo, .header-actions { position: relative; z-index: 1; }
}

/* ===========================================================================
   The apps — customer app and partner app (index.php #apps)
   One light panel: the words and two store-style badges on the left, a phone
   drawn in CSS on the right. Light on purpose — the closing CTA just above is
   a purple band, and a second one would read as the same banner twice. The
   badges are near-black like the app-store badges people already read as
   "download", and they stay side by side at every width, a phone included.
   =========================================================================== */
.apps-panel {
  position: relative; overflow: hidden; isolation: isolate;
  padding: 44px 360px 44px 48px; border-radius: 22px;
  background:
    radial-gradient(circle at 84% 78%, rgba(124, 58, 237, .20), transparent 40%),
    linear-gradient(120deg, var(--surface) 45%, var(--bg-soft));
  border: 1px solid var(--line); box-shadow: var(--shadow);
}
/* A dot grid that fades in toward the phone, so the text side stays clean. */
.apps-panel::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(rgba(124, 58, 237, .20) 1.2px, transparent 1.7px) 0 0 / 18px 18px;
  -webkit-mask-image: linear-gradient(to left, #000 15%, transparent 60%);
          mask-image: linear-gradient(to left, #000 15%, transparent 60%);
}

.apps-kicker {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: 999px;
  background: var(--bg-tint); color: var(--brand);
  font-size: 12px; font-weight: 700; letter-spacing: .02em;
}
.apps-kicker svg { width: 14px; height: 14px; }
.apps-copy h2 { font-size: 32px; line-height: 1.15; letter-spacing: -.02em; margin: 14px 0 8px; }
.apps-copy > p { font-size: 15.5px; line-height: 1.55; color: var(--muted); max-width: 460px; }

.apps-badges { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }
.app-badge {
  display: flex; align-items: center; gap: 12px;
  min-width: 220px; padding: 11px 16px 11px 14px; border-radius: 14px;
  background: #140C28; color: #fff; border: 1px solid rgba(255, 255, 255, .06);
  box-shadow: 0 8px 20px rgba(20, 12, 40, .20);
  transition: transform 180ms var(--ease-out-soft, ease), background 180ms ease, box-shadow 180ms ease;
}
.app-badge:hover { transform: translateY(-2px); background: var(--sidebar); box-shadow: 0 12px 28px rgba(46, 16, 101, .30); }
.app-badge:active { transform: translateY(0); }
.app-badge:focus-visible { outline: 3px solid var(--brand); outline-offset: 3px; }
/* Android's own green, the cue that tells a phone owner this one is theirs. */
.app-badge-logo { width: 28px; height: 28px; flex: none; fill: #3DDC84; }
.app-badge-txt { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.app-badge-txt small {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: rgba(255, 255, 255, .62);
}
.app-badge-txt strong { font-size: 17px; font-weight: 700; letter-spacing: -.01em; }
.app-badge-dl {
  width: 18px; height: 18px; flex: none; margin-left: auto; opacity: .75;
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}

.apps-copy > .apps-note { margin-top: 16px; font-size: 12.5px; }

/* ---- the phone ---- */
.apps-phone {
  position: absolute; right: 88px; bottom: -86px;
  width: 200px; height: 360px; padding: 9px; border-radius: 36px;
  background: #140C28;
  box-shadow: 0 28px 60px rgba(46, 16, 101, .32), inset 0 0 0 1.5px rgba(255, 255, 255, .10);
  transform: rotate(-7deg);
}
.apps-phone::before {
  content: ''; position: absolute; top: 16px; left: 50%; z-index: 1;
  width: 56px; height: 7px; margin-left: -28px; border-radius: 7px; background: #140C28;
}
.apps-phone-screen {
  display: flex; flex-direction: column; gap: 9px; height: 100%; overflow: hidden;
  padding: 34px 14px 14px; border-radius: 28px; background: var(--bg);
}
.apps-phone-bar { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; font-size: 12.5px; font-weight: 800; color: var(--ink); }
.apps-phone-logo {
  display: grid; place-items: center; width: 22px; height: 22px; border-radius: 7px;
  background: linear-gradient(135deg, var(--brand), var(--brand-dark)); color: #fff; font-size: 12px;
}
.apps-phone-line { display: block; height: 7px; border-radius: 4px; background: #D8CEFB; }
.apps-phone-line-soft { background: #EAE4FC; }
.apps-phone-card {
  display: flex; align-items: center; gap: 9px; padding: 9px; border-radius: 12px;
  background: var(--surface); box-shadow: 0 1px 3px rgba(30, 27, 75, .08);
}
.apps-phone-thumb { width: 28px; height: 28px; flex: none; border-radius: 8px; background: linear-gradient(135deg, #EDE9FE, #C4B5FD); }
.apps-phone-lines { display: flex; flex-direction: column; gap: 6px; flex: 1; }
.apps-phone-ok {
  display: flex; align-items: center; gap: 6px; margin-top: 2px; padding: 8px 10px; border-radius: 10px;
  background: var(--green); color: #fff; font-size: 10.5px; font-weight: 700;
}
.apps-phone-ok svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }

/* No room for the phone beside the words: the words take the panel. */
@media (max-width: 900px) {
  .apps-panel { padding-right: 48px; }
  .apps-phone { display: none; }
}

@media (max-width: 640px) {
  .apps-panel { padding: 22px 14px 18px; border-radius: 18px; text-align: center; }
  .apps-panel::before { -webkit-mask-image: linear-gradient(to bottom, #000, transparent 70%); mask-image: linear-gradient(to bottom, #000, transparent 70%); opacity: .6; }
  .apps-kicker { padding: 4px 10px; font-size: 11px; }
  .apps-kicker svg { width: 12px; height: 12px; }
  .apps-copy h2 { font-size: 20px; margin: 10px 0 6px; }
  .apps-copy > p { font-size: 13px; margin: 0 auto; }
  /* Two across on a phone too — each badge takes half the row. */
  .apps-badges { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 16px; }
  .app-badge {
    min-width: 0; gap: 8px; padding: 10px; border-radius: 12px; text-align: left;
    box-shadow: 0 4px 10px rgba(20, 12, 40, .16);
  }
  .app-badge-logo { width: 22px; height: 22px; }
  .app-badge-txt small { font-size: 8.5px; letter-spacing: .03em; white-space: nowrap; }
  .app-badge-txt strong { font-size: 13.5px; white-space: nowrap; }
  .app-badge-dl { display: none; }
  .apps-copy > .apps-note { margin-top: 16px; font-size: 11.5px; }
}
/* The smallest phones: "For" goes, so "Professionals" keeps one line. */
@media (max-width: 360px) {
  .app-badge { padding: 9px 8px; gap: 6px; }
  .app-badge-logo { width: 18px; height: 18px; }
  .app-badge-for { display: none; }
  .app-badge-txt strong { font-size: 12.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .app-badge { transition: none; }
  .app-badge:hover { transform: none; }
}
