/* ============================================================
   X-Pro Design System — Components
   Reusable classes mirroring the "X-Pro London Ontario" page.
   Depends on tokens.css + base.css.
   ============================================================ */

/* ---------- Status dot ---------- */
@keyframes xpDot {
  0%   { box-shadow: 0 0 0 0 rgba(22, 163, 74, .5); }
  70%  { box-shadow: 0 0 0 7px rgba(22, 163, 74, 0); }
  100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); }
}
.xp-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--xp-success);
  flex: none;
}
.xp-dot--pulse { animation: xpDot 2s infinite; }

/* ---------- Buttons ---------- */
.xp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  font-size: 16px;
  line-height: 1;
  padding: 15px 28px;
  border: none;
  border-radius: var(--xp-radius);
  cursor: pointer;
  transition: background var(--xp-transition), color var(--xp-transition), transform var(--xp-transition-fast);
  white-space: nowrap;
}
.xp-btn svg { display: block; }

.xp-btn--primary {
  background: var(--xp-brand);
  color: var(--xp-ink);
  box-shadow: var(--xp-shadow-brand);
}
.xp-btn--primary:hover { background: var(--xp-brand-hover); }

.xp-btn--dark { background: var(--xp-ink); color: var(--xp-white); }
.xp-btn--dark:hover { background: #1d2740; }

.xp-btn--outline {
  background: var(--xp-white);
  color: var(--xp-ink);
  border: 1.5px solid var(--xp-ink);
}
.xp-btn--outline:hover { background: var(--xp-ink); color: var(--xp-white); }

.xp-btn--ghost { background: var(--xp-white); color: var(--xp-ink); }
.xp-btn--ghost:hover { background: var(--xp-surface-2); }

.xp-btn--sm { font-size: 15px; padding: 11px 20px; border-radius: var(--xp-radius); }
.xp-btn--lg { font-size: 17px; padding: 16px 30px; border-radius: var(--xp-radius-md); }
.xp-btn--block { width: 100%; }

/* Amber-on-dark CTA glow variant */
.xp-btn--primary.xp-btn--lg { box-shadow: 0 12px 26px rgba(245, 182, 28, .4); }

/* ---------- Phone pill (header) ---------- */
.xp-phone-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--xp-space-2);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-extrabold);
  font-size: 15px;
  color: var(--xp-ink);
  background: var(--xp-surface-2);
  padding: 10px 15px;
  border-radius: var(--xp-radius);
}

/* ---------- Icon tile / well ---------- */
.xp-icon-tile {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px; height: 52px;
  border-radius: var(--xp-radius-md);
  background: var(--xp-brand-tint);
}
.xp-icon-tile--brand { background: var(--xp-brand); }
.xp-icon-tile--sm { width: 30px; height: 30px; border-radius: var(--xp-radius-sm); }
.xp-icon-tile--md { width: 50px; height: 50px; }

/* ---------- Badges & chips ---------- */
.xp-rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--xp-white);
  border-radius: var(--xp-radius-md);
  padding: 10px 14px;
  box-shadow: 0 10px 24px rgba(16, 24, 40, .18);
}
.xp-stars { display: inline-flex; align-items: center; color: var(--xp-brand); line-height: 1; }

.xp-trust-item {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: var(--xp-weight-semibold);
  font-size: 13.5px;
  color: var(--xp-ink);
}

/* Hero copy column. .xp-display and .xp-lead are margin:0 by default, so
   without these the amber .xp-underline (a 4px border-bottom) lands on the
   lead paragraph's first line. */
.xp-hero__copy .xp-display { margin: 16px 0 20px; }

/* The hero band runs tighter than the standard section rhythm: its height is
   set by the reserved 6:5 media box, not by the copy column. */
#top { padding-block: 64px; }

/* Green-check list row (common problems / benefits) */
.xp-check-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius);
  padding: 15px 17px;
  box-shadow: var(--xp-shadow-xs);
  font-weight: var(--xp-weight-semibold);
  font-size: 14.5px;
  color: var(--xp-ink);
}
.xp-check-badge {
  flex: none;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--xp-success-tint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Bare check row (no card) for benefit panels */
.xp-check-line {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 15px;
  font-weight: var(--xp-weight-semibold);
  color: var(--xp-ink);
}

/* Location / area pill */
.xp-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-pill);
  padding: 9px 15px;
  font-weight: var(--xp-weight-semibold);
  font-size: 13.5px;
  color: var(--xp-ink);
}

/* ---------- Cards ---------- */
.xp-card {
  background: var(--xp-surface);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-lg);
  padding: 26px;
  box-shadow: var(--xp-shadow-sm);
}
.xp-card--dark { background: var(--xp-ink); border-color: var(--xp-ink); color: var(--xp-on-dark-2); }
.xp-card--flat { background: var(--xp-surface-1); box-shadow: none; }

/* Service card — icon + label, lifts on hover */
.xp-service {
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-lg);
  padding: 20px;
  box-shadow: var(--xp-shadow-sm);
  transition: transform var(--xp-transition-fast), box-shadow var(--xp-transition-fast), border-color var(--xp-transition-fast);
}
.xp-service:hover {
  transform: translateY(-3px);
  box-shadow: var(--xp-shadow-md);
  border-color: var(--xp-brand);
}
.xp-service__title { display: block; font-family: var(--xp-font-display); font-weight: var(--xp-weight-extrabold); font-size: 16.5px; color: var(--xp-ink); }
.xp-service__desc  { display: block; font-size: 13px; color: var(--xp-ink-500); margin-top: 2px; }

/* Feature card — amber icon, title, copy */
.xp-feature { padding: 26px; }
.xp-feature .xp-h3 { margin: 16px 0 6px; }
.xp-feature p { font-size: 14px; color: var(--xp-ink-700); line-height: var(--xp-leading-normal); margin: 0; }

/* Numbered step */
.xp-step { padding: 28px 24px; }
.xp-step--dark { background: var(--xp-ink); border-color: var(--xp-ink); }
.xp-step--dark .xp-h3 { color: var(--xp-white); }
.xp-step--dark p { color: var(--xp-on-dark-2); }
.xp-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  border-radius: var(--xp-radius);
  background: var(--xp-brand);
  color: var(--xp-ink);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-extrabold);
  font-size: 15px;
}
.xp-step .xp-h3 { font-size: 18px; margin: 16px 0 6px; }
.xp-step p { font-size: 14px; color: var(--xp-ink-700); line-height: var(--xp-leading-normal); margin: 0; }

/* Brand tile */
/* Brand tiles — one manufacturer wordmark per tile. Wordmarks are inline SVG
   (see the Brands section of the style guide) so they stay crisp at any size
   and need no binary assets. Lay them out with .xp-brand-grid: flex-wrap keeps
   a short final row optically centred, which auto-fit grid columns cannot do. */
.xp-brand-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}

.xp-brand-tile {
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  padding: 18px 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 88px;
  flex: 1 1 160px;
  max-width: 200px;
  box-shadow: var(--xp-shadow-xs);
  transition: transform .15s, box-shadow .15s, border-color .15s;
}

.xp-brand-tile:hover {
  transform: translateY(-3px);
  box-shadow: var(--xp-shadow-lg);
  border-color: var(--xp-brand);
}

/* Height is set per-brand inline — each wordmark has its own optical weight. */
.xp-brand-tile__logo {
  width: auto;
  max-width: 100%;
  display: block;
}

/* Review card */
.xp-review { padding: 26px; box-shadow: 0 6px 20px rgba(16, 24, 40, .05); }
.xp-review__head { display: flex; align-items: center; justify-content: space-between; }
.xp-review__text { font-size: 15px; line-height: var(--xp-leading-relaxed); color: var(--xp-ink-700); margin: 14px 0 18px; }
.xp-review__author { display: flex; align-items: center; gap: 11px; }
.xp-review__name { font-weight: var(--xp-weight-bold); font-size: 14px; color: var(--xp-ink); }
.xp-review__meta { font-size: 13px; color: var(--xp-ink-500); }

/* Avatar */
.xp-avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  background: var(--xp-ink);
  color: var(--xp-brand);
}
.xp-avatar--brand { background: var(--xp-brand); color: var(--xp-ink); }
.xp-avatar--slate { background: var(--xp-ink-700); color: var(--xp-white); }

/* ---------- Form controls ---------- */
.xp-field { display: flex; flex-direction: column; gap: 6px; }
.xp-label { font-size: var(--xp-text-label); font-weight: var(--xp-weight-bold); color: var(--xp-ink-700); }
.xp-input, .xp-select, .xp-textarea {
  padding: 13px;
  border: 1.5px solid var(--xp-border-input);
  border-radius: var(--xp-radius);
  /* 16px is a floor, not a taste: iOS Safari zooms the whole page when a
     focused control is smaller, which throws the layout off on every phone. */
  font-size: 16px;
  font-weight: var(--xp-weight-semibold);
  color: var(--xp-ink);
  background: var(--xp-white);
  width: 100%;
}
.xp-input::placeholder, .xp-textarea::placeholder { color: var(--xp-ink-500); font-weight: var(--xp-weight-medium); }
.xp-input:focus, .xp-select:focus, .xp-textarea:focus {
  outline: none;
  border-color: var(--xp-brand);
  box-shadow: 0 0 0 3px rgba(245, 182, 28, .18);
}
.xp-textarea { resize: vertical; font-weight: var(--xp-weight-medium); }
.xp-select {
  appearance: none;
  background: var(--xp-white) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23667085' stroke-width='2.2'><path d='M2 4l4 4 4-4'/></svg>") no-repeat right 12px center;
  padding-right: 34px;
}

/* ---------- FAQ (native <details>) ---------- */
.xp-faq {
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  overflow: hidden;
  box-shadow: var(--xp-shadow-xs);
}
.xp-faq > summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 22px;
  cursor: pointer;
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  font-size: 16.5px;
  line-height: var(--xp-leading-snug);
  color: var(--xp-ink);
}
.xp-faq > summary::-webkit-details-marker { display: none; }
.xp-faq > summary .xp-faq__chevron { flex: none; transition: transform var(--xp-transition); }
.xp-faq[open] > summary .xp-faq__chevron { transform: rotate(180deg); }
.xp-faq__body { padding: 0 22px 20px; margin: 0; font-size: 15px; line-height: var(--xp-leading-relaxed); color: var(--xp-ink-700); }
/* The cards are separate rounded panels, not one fused stack. */
.xp-faq + .xp-faq { margin-top: var(--xp-space-3); }

/* ---------- CTA banner ---------- */
.xp-cta {
  position: relative;
  overflow: hidden;
  background: var(--xp-ink);
  border-radius: var(--xp-radius-2xl);
  padding: clamp(36px, 5vw, 64px);
  text-align: center;
  color: var(--xp-white);
  box-shadow: 0 30px 60px rgba(16, 24, 40, .28);
}
.xp-cta::before {
  content: "";
  position: absolute;
  top: -40px; right: -40px;
  width: 200px; height: 200px;
  border-radius: 50%;
  background: rgba(245, 182, 28, .12);
}
.xp-cta > * { position: relative; }
/* The banner is centred, so its action row has to be too — .xp-actions is a
   flex row and would otherwise sit hard left. */
.xp-cta .xp-actions { justify-content: center; }

/* ---------- Utility bar ---------- */
.xp-utilitybar { background: var(--xp-ink); color: var(--xp-on-dark-2); font-size: 13px; font-weight: var(--xp-weight-medium); }
.xp-utilitybar a, .xp-utilitybar strong { color: var(--xp-white); font-weight: var(--xp-weight-bold); }

/* ---------- Footer ---------- */
/* The band carried no vertical padding, so the logo sat on its top edge and the
   copyright on its bottom edge. 60px matches the mockup's footer inset. */
.xp-footer { background: var(--xp-ink); color: var(--xp-on-dark-2); padding: 60px 0 28px; }
/* Brand block sits above the link columns, not beside them. */
.xp-footer__brand { max-width: 420px; margin-bottom: var(--xp-space-10); }
.xp-footer h3 {
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  font-size: 15px;
  color: var(--xp-white);
  margin: 0 0 14px;
}
.xp-footer__links { display: flex; flex-direction: column; gap: 9px; font-size: 14px; }
/* The column gap does not reach inside a row, so the status dot needs its own. */
.xp-footer__status { display: flex; align-items: baseline; gap: 7px; }
.xp-footer__links a { transition: color var(--xp-transition-fast); }
.xp-footer__links a:hover { color: var(--xp-brand); }

/* ============================================================
   Page shell — header, navigation, mobile menu, sticky CTA
   Mobile-first: the drawer and the bottom bar are the default,
   and the 900px breakpoint promotes the desktop nav. The design
   expressed this the other way round, inline in the mockup.
   ============================================================ */

.xp-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, .9);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--xp-border);
}
.xp-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--xp-space-4);
  padding: 14px 0;
}
/* AC14: the artwork stays 38px; the link grows to a 44px tap target around it. */
.xp-header__logo { display: inline-flex; align-items: center; min-height: 44px; }
.xp-header__logo img { display: block; height: 38px; width: auto; }

/* Promoted at 900px; hidden below it. */
.xp-nav,
.xp-header .xp-phone-pill { display: none; }

.xp-menutoggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--xp-white);
  border: 1.5px solid var(--xp-border-input);
  border-radius: var(--xp-radius);
  cursor: pointer;
}

.xp-mobilemenu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px var(--xp-gutter) 18px;
  background: var(--xp-white);
  border-top: 1px solid var(--xp-border);
}
/* Toggled by nav.js through the hidden attribute, so no !important. */
.xp-mobilemenu[hidden] { display: none; }
.xp-mobilemenu a {
  padding: 11px 0;
  font-weight: var(--xp-weight-semibold);
  color: var(--xp-ink);
}

/* Fixed call/book bar, mobile only. */
.xp-stickybar {
  display: flex;
  gap: 10px;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--xp-border);
  box-shadow: 0 -6px 20px rgba(16, 24, 40, .1);
}
.xp-stickybar .xp-btn { flex: 1; }
/* Stops the fixed bar covering the end of the page. */
.xp-body { padding-bottom: 76px; }

@media (min-width: 900px) {
  .xp-nav {
    display: flex;
    align-items: center;
    gap: 26px;
  }
  /* AC14: 44px tap targets on touch laptops too. The header already reserves
     this height, so the links grow into it without moving anything. */
  .xp-nav a { display: inline-flex; align-items: center; min-height: 44px; }
  .xp-header .xp-phone-pill { display: inline-flex; }
  .xp-menutoggle,
  .xp-mobilemenu,
  .xp-stickybar { display: none; }
  .xp-body { padding-bottom: 0; }
}

/* ============================================================
   Layout patterns the page needs and the system did not carry
   ============================================================ */

/* Hero, and any other copy-beside-media split. */
.xp-split {
  display: grid;
  gap: var(--xp-space-10);
  align-items: center;
}

/* Booking: dark info panel beside the form, in one rounded shell. */
.xp-booking {
  display: grid;
  overflow: hidden;
  border-radius: var(--xp-radius-2xl);
  box-shadow: var(--xp-shadow-lg);
}
.xp-booking__panel {
  padding: var(--xp-space-8);
  background: var(--xp-dark);
  color: var(--xp-on-dark-1);
}
.xp-booking__form { padding: var(--xp-space-8); background: var(--xp-white); }

@media (min-width: 900px) {
  .xp-split { grid-template-columns: 1fr 1fr; gap: var(--xp-space-12); }
  .xp-booking { grid-template-columns: 1fr 1.15fr; }
}

/* Two-up field pairs. */
.xp-form-row { display: grid; gap: var(--xp-space-4); }
@media (min-width: 600px) {
  .xp-form-row { grid-template-columns: 1fr 1fr; }
}

/* Aspect-ratio media box. The template locks 6:5 for hero and 4:3
   for service photography; reserving the box means the image cannot
   shift the layout when it loads. 6:5 is what the mockup's hero box
   actually renders at (550x460) — reserving a different shape would
   defeat the point of reserving one. */
.xp-media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 6 / 5;
  border-radius: var(--xp-radius-xl);
  background: var(--xp-surface-1);
}
.xp-media--4x3 { aspect-ratio: 4 / 3; }
/* Matches a bare <img> and one wrapped in <picture> for a WebP source. */
.xp-media > img,
.xp-media > picture > img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Standing in until real photography exists. */
.xp-media--placeholder {
  background-image: repeating-linear-gradient(
    -45deg,
    var(--xp-surface-1) 0 14px,
    var(--xp-surface-2) 14px 28px
  );
  border: 1px solid var(--xp-border-muted);
}

/* Service-area map. A hairline so the satellite texture has an edge
   against the white section, the same way the hero photo does. */
.xp-media--map { border: 1px solid var(--xp-border); }

/* Service-area tier: a labelled group of town pills. */
.xp-tier + .xp-tier { margin-top: var(--xp-space-6); }
.xp-tier__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--xp-space-2);
}
.xp-tier__label {
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  color: var(--xp-ink);
}
.xp-tier__note { font-size: var(--xp-text-sm); color: var(--xp-ink-500); }
.xp-tier__towns {
  display: flex;
  flex-wrap: wrap;
  gap: var(--xp-space-2);
  margin-top: var(--xp-space-3);
}

/* Footer columns. */
.xp-footer__grid {
  display: grid;
  gap: var(--xp-space-8);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

/* ============================================================
   Focus — every interactive element, not only form controls
   ============================================================ */
:focus-visible {
  outline: 3px solid var(--xp-brand);
  outline-offset: 2px;
}

/* ============================================================
   Page composition — the small helpers index.html composes with
   ============================================================ */

.xp-utilitybar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--xp-space-2) var(--xp-space-5);
  padding: 9px 0;
  font-size: var(--xp-text-xs);
}
.xp-utilitybar__phone {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: var(--xp-weight-bold);
  color: var(--xp-white);
}
.xp-utilitybar__item { display: inline-flex; align-items: center; gap: 7px; }

/* On a phone the bar keeps the phone number and the availability note; the
   location and hours lines would wrap it to three rows and push the hero
   below the fold. Both still exist in the HTML and in the footer. */
.xp-utilitybar__item:nth-child(2),
.xp-utilitybar__item:nth-child(3) { display: none; }

@media (min-width: 700px) {
  .xp-utilitybar__item:nth-child(2),
  .xp-utilitybar__item:nth-child(3) { display: inline-flex; }
}
@media (min-width: 900px) {
  .xp-utilitybar__item:last-child { margin-left: auto; }
}

.xp-header__actions {
  display: flex;
  align-items: center;
  gap: var(--xp-space-3);
}

/* Button rows: the hero pair and the closing CTA pair. */
.xp-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--xp-space-3);
  margin-top: var(--xp-space-6);
}

.xp-trust-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--xp-space-3) var(--xp-space-5);
  margin-top: var(--xp-space-6);
}

.xp-booking__points {
  display: flex;
  flex-direction: column;
  gap: var(--xp-space-3);
  margin: var(--xp-space-6) 0;
}
/* .xp-check-line is ink for light panels; on this dark one ink is the
   background colour, so the two points rendered as bare ticks. */
.xp-booking__points .xp-check-line { color: var(--xp-on-dark-1); }
.xp-booking__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--xp-space-2);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-extrabold);
  font-size: 19px;
  color: var(--xp-white);
}

.xp-check-list {
  display: flex;
  flex-direction: column;
  gap: var(--xp-space-3);
}

/* Revealed by form.js when the endpoint fails, so the lead is never lost. */
.xp-form-fallback {
  margin: var(--xp-space-4) 0 0;
  padding: var(--xp-space-4);
  border: 1px solid var(--xp-brand);
  border-radius: var(--xp-radius);
  background: var(--xp-brand-tint);
  font-weight: var(--xp-weight-semibold);
  color: var(--xp-ink);
}
.xp-form-fallback a { text-decoration: underline; }

.xp-footer__logo {
  height: 34px;
  width: auto;
  margin-bottom: var(--xp-space-4);
}
/* Raised chip on the dark ground, matching the header's phone pill. */
.xp-footer__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--xp-space-2);
  margin-top: var(--xp-space-4);
  padding: 11px 16px;
  border-radius: var(--xp-radius);
  background: var(--xp-dark-raised);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-extrabold);
  font-size: 16px;
  color: var(--xp-white);
}
.xp-footer__bottom {
  margin-top: var(--xp-space-10);
  padding-top: var(--xp-space-5);
  border-top: 1px solid var(--xp-dark-border);
  font-size: var(--xp-text-xs);
}

/* Grid densities, named rather than set inline so the column behaviour is
   reviewable in one place and assertable at each breakpoint. */
/* Six services, so the column count is set rather than auto-fit: auto-fit put
   four across the 1200px container and left a 4+2 row. 1 / 2 / 3 columns divide
   six evenly at every width. grid-auto-rows: 1fr makes every row as tall as the
   tallest card, so a title that wraps to two lines does not leave its row
   shorter than the others. */
.xp-grid--services {
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;
}
@media (min-width: 600px) { .xp-grid--services { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .xp-grid--services { grid-template-columns: repeat(3, 1fr); } }
.xp-grid--problems { --min: 240px; }
.xp-grid--features { --min: 260px; }
.xp-grid--steps    { --min: 220px; }

/* ---------- Cookie consent ---------- */
/* Sits above the mobile sticky bar so the two never overlap. */
.xp-consent {
  position: fixed;
  z-index: 70;
  left: var(--xp-space-4);
  right: var(--xp-space-4);
  bottom: calc(84px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: var(--xp-space-3);
  padding: var(--xp-space-5);
  background: var(--xp-white);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-lg);
  box-shadow: var(--xp-shadow-xl);
}
/* Needed because the base rule sets display. */
.xp-consent[hidden] { display: none; }
.xp-consent p { margin: 0; font-size: 14px; color: var(--xp-ink-700); }
.xp-consent__actions { display: flex; gap: var(--xp-space-2); justify-content: flex-end; }

@media (min-width: 900px) {
  .xp-consent {
    left: auto;
    right: var(--xp-space-6);
    bottom: var(--xp-space-6);
    max-width: 420px;
  }
}

/* Replaces the form after a successful submission. */
.xp-form-done {
  margin: 0;
  padding: var(--xp-space-6);
  border-radius: var(--xp-radius);
  background: var(--xp-success-tint);
  color: var(--xp-ink);
}

/* ---------- Grid overflow guard ---------- */
/* A grid item defaults to `min-width: auto`, so a child that cannot shrink below
   its intrinsic width - a datetime-local control, a long unbroken string - widens
   its own track and pushes the page into horizontal scrolling. On a 320px phone
   that is the difference between a usable form and a broken one. */
.xp-split > *,
.xp-booking > *,
.xp-form-row > *,
.xp-grid > * { min-width: 0; }

/* The split card's generous padding is desktop framing; on a small phone it eats
   the width the form needs. */
.xp-booking__panel,
.xp-booking__form { padding: var(--xp-space-5); }

@media (min-width: 600px) {
  .xp-booking__panel,
  .xp-booking__form { padding: var(--xp-space-8); }
}

/* ============================================================
   Reduced motion
   The status dot pulses forever, and every component here has a
   transition. `!important` is the standard idiom for this override:
   it has to beat whatever a component declares, and there is no
   other way to reach them all from one rule.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .xp-dot--pulse { animation: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Optional-field marker. #98A2B3 was 2.55:1 on white; --xp-ink-500 is 4.92:1. */
.xp-label__optional { color: var(--xp-ink-500); font-weight: var(--xp-weight-semibold); }

/* ============================================================
   Service page
   A service page is a narrower brief than the homepage: no tour of
   the business, one appliance, one action. It gets a tighter type
   scale and a tighter section rhythm, set here as token overrides
   on the body so every component inherits them without a single
   specificity fight.
   ============================================================ */
.xp-service-page {
  --xp-section-y: 60px;
  --xp-text-display: clamp(34px, 3.6vw, 44px);
  --xp-text-h2: clamp(27px, 2.4vw, 32px);
  --xp-text-h3: 17px;
}
@media (max-width: 599px) {
  .xp-service-page { --xp-section-y: 40px; }
}
/* Hierarchy carried by weight as well as size: 800 display and section
   headings, 700 item titles. Every heading at 800 flattens the page. */
.xp-service-page .xp-h3 { font-weight: var(--xp-weight-bold); }
.xp-service-page #top { padding-block: var(--xp-section-y); }

/* .xp-h2 and .xp-small are both margin:0, so a heading and whatever follows it
   sit flush unless something says otherwise. One rule rather than a margin per
   component, so the gap under every section heading is the same gap. */
.xp-service-page .xp-h2 + * { margin-top: var(--xp-space-2); }
.xp-service-page .xp-h2 + .xp-benefit,
.xp-service-page .xp-h2 + .xp-brandline,
.xp-service-page .xp-h2 + .xp-faq { margin-top: var(--xp-space-6); }

/* Questions stay a readable measure without indenting the section's left edge
   away from every other section on the page. */
.xp-service-page .xp-faq { max-width: 820px; }

/* ---------- Breadcrumb ---------- */
.xp-breadcrumb {
  border-bottom: 1px solid var(--xp-border);
  font-size: var(--xp-text-sm);
  color: var(--xp-ink-500);
}
.xp-breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--xp-space-2);
  list-style: none;
  margin: 0;
  padding: 11px 0;
}
.xp-breadcrumb a { font-weight: var(--xp-weight-semibold); color: var(--xp-ink-700); }
.xp-breadcrumb a:hover { color: var(--xp-ink); }
.xp-breadcrumb [aria-current] { font-weight: var(--xp-weight-bold); color: var(--xp-ink); }
.xp-breadcrumb li + li::before { content: "/"; margin-right: var(--xp-space-2); color: var(--xp-border-input); }

/* ---------- Symptom grid ----------
   One bordered container with 1px gaps, not eight shadowed cards: the
   hairlines come from the background showing through, so eight items
   read as one table rather than eight objects. 4x2 at desktop, 2 at
   tablet, 1 on a small phone. */
.xp-symptoms {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  margin-top: var(--xp-space-6);
  background: var(--xp-border);
  border: 1px solid var(--xp-border);
  border-radius: var(--xp-radius-md);
  overflow: hidden;
}
@media (min-width: 560px) { .xp-symptoms { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1080px) { .xp-symptoms { grid-template-columns: repeat(4, 1fr); } }

.xp-symptom {
  display: flex;
  align-items: flex-start;
  gap: var(--xp-space-3);
  background: var(--xp-white);
  padding: 16px 16px 17px;
}
.xp-symptom svg { flex: none; margin-top: 1px; color: var(--xp-ink); }
.xp-symptom .xp-h3 { margin: 0; }
.xp-symptom p {
  margin: 3px 0 0;
  font-size: var(--xp-text-sm);
  line-height: var(--xp-leading-normal);
  color: var(--xp-ink-500);
}

/* ---------- Link list (homepage symptoms) ----------
   Reads as navigation: a hairline under each row and an arrow at its end, no
   outer box. Two columns from 700px; the list stops at a readable width so a
   row's label and its arrow stay in one glance. */
.xp-linklist {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: var(--xp-space-10);
  max-width: 880px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}
@media (min-width: 700px) { .xp-linklist { grid-template-columns: repeat(2, 1fr); } }
.xp-linklist li { border-bottom: 1px solid var(--xp-border); }
.xp-linklist a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--xp-space-4);
  min-height: 52px;
  padding: 12px 2px;
  font-weight: var(--xp-weight-semibold);
  color: var(--xp-ink);
}
.xp-linklist svg { flex: none; color: var(--xp-ink-500); transition: transform var(--xp-transition-fast), color var(--xp-transition-fast); }
.xp-linklist a:hover { color: var(--xp-ink); }
.xp-linklist a:hover svg { color: var(--xp-ink); transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .xp-linklist a:hover svg { transform: none; } }

/* Why X-Pro: a one-line intro, then three scannable points. */
.xp-why__intro { font-size: var(--xp-text-lead); color: var(--xp-ink-700); margin: var(--xp-space-4) 0 0; }
.xp-why__points { margin: var(--xp-space-6) 0 0; padding: 0; list-style: none; gap: var(--xp-space-4); }
.xp-why__points .xp-check-line { font-weight: var(--xp-weight-regular); color: var(--xp-ink-700); line-height: var(--xp-leading-normal); }
.xp-why__points strong { font-weight: var(--xp-weight-bold); color: var(--xp-ink); }
.xp-why__points svg { flex: none; margin-top: 2px; }

/* Shorter band for sections that are a heading and one list. */
.xp-section--compact { --xp-section-y: 56px; }
@media (max-width: 599px) { .xp-section--compact { --xp-section-y: 40px; } }
.xp-section--compact .xp-section-head { margin-bottom: var(--xp-space-8); }

/* ---------- Compact inline list (washer types) ----------
   Replaces a five-item section. The separator is generated on the
   item, so a wrapped row cannot leave a dangling rule behind. */
.xp-inline-list {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--xp-space-2) var(--xp-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.xp-inline-note {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--xp-space-2) var(--xp-space-3);
  margin-top: var(--xp-space-5);
  font-size: var(--xp-text-sm);
  color: var(--xp-ink-700);
}
.xp-inline-note > strong { font-weight: var(--xp-weight-bold); color: var(--xp-ink); }
.xp-inline-list li { font-weight: var(--xp-weight-semibold); color: var(--xp-ink-700); }
.xp-inline-list li + li::before {
  content: "\00B7";
  margin-right: var(--xp-space-3);
  color: var(--xp-border-input);
}

/* ---------- Asymmetric split (image beside copy) ----------
   Pairs earlier than the 900px shell breakpoint on purpose: stacked, the 4:3
   image is as wide as the viewport, so on a 768px tablet it alone runs 576px
   tall and pushes the three benefits off the screen. Declared after .xp-split
   so it still wins above 900px. */
@media (min-width: 720px) {
  .xp-split--aside {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--xp-space-8);
  }
}
@media (min-width: 900px) {
  .xp-split--aside { gap: var(--xp-space-12); }
}

/* Benefit: a heading and one line, no card, no tile. */
.xp-benefit + .xp-benefit { margin-top: var(--xp-space-6); }
.xp-benefit .xp-h3 { margin: 0; }
.xp-benefit p {
  margin: 5px 0 0;
  font-size: var(--xp-text-body-lg);
  line-height: var(--xp-leading-relaxed);
  color: var(--xp-ink-700);
  max-width: 56ch;
}

/* ---------- Compact process timeline ----------
   Sits at the foot of the Why Choose section, not in a band of its own. */
.xp-process-block {
  margin-top: var(--xp-space-12);
  padding-top: var(--xp-space-8);
  border-top: 1px solid var(--xp-border);
}
.xp-process-block__title { font-size: var(--xp-text-h3); }
@media (max-width: 599px) {
  .xp-process-block { margin-top: var(--xp-space-8); padding-top: var(--xp-space-6); }
}

/* The connector is each step's own border-top with the marker straddling it,
   rather than one line drawn across the whole row. That is the entire reason it
   survives wrapping: at two columns every cell still carries its own rule, so
   nothing is left running into empty space beside a short final row.
   The top margin clears the marker, which is pulled up out of the flow. */
.xp-process {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--xp-space-6) var(--xp-space-5);
  list-style: none;
  margin: calc(var(--xp-space-8) + 14px) 0 0;
  padding: 0;
  counter-reset: xp-step;
}
@media (min-width: 760px) { .xp-process { grid-template-columns: repeat(4, 1fr); } }

.xp-process li {
  border-top: 2px solid var(--xp-border);
  padding-right: var(--xp-space-5);
}
/* Pulled up by half its height so it sits on the rule, with a ring notching the
   line out behind it. The ring is the section background: this block is only
   ever used on white. */
.xp-process li::before {
  counter-increment: xp-step;
  content: "0" counter(xp-step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin-top: -16px;
  border-radius: 50%;
  background: var(--xp-ink);
  color: var(--xp-brand);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-extrabold);
  font-size: var(--xp-text-xs);
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 4px var(--xp-white);
}
.xp-process__label {
  display: block;
  margin-top: var(--xp-space-3);
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  font-size: var(--xp-text-body-lg);
  color: var(--xp-ink);
}
.xp-process__note {
  display: block;
  margin-top: 3px;
  font-size: var(--xp-text-sm);
  line-height: var(--xp-leading-normal);
  color: var(--xp-ink-500);
}

/* ---------- Brand grid ----------
   One list on both pages: the homepage's nineteen names, and the fifteen of them
   that make washing machines on the washer page (a test fails if the washer set
   is not a subset). 8 up on desktop, 5 on tablet, 3 on a phone. Deliberately no
   card, border or shadow per brand. */
.xp-brandgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--xp-space-5) var(--xp-space-4);
  align-items: center;
  list-style: none;
  margin: var(--xp-space-6) 0 0;
  padding: 0;
}
@media (min-width: 620px) { .xp-brandgrid { grid-template-columns: repeat(5, 1fr); } }
@media (min-width: 1000px) { .xp-brandgrid { grid-template-columns: repeat(8, 1fr); } }

/* Names, not logos: set in the site's own type, one weight, one colour, so the
   list reads as a roster rather than nineteen other companies' identities. */
.xp-brandgrid li {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  font-family: var(--xp-font-display);
  font-weight: var(--xp-weight-bold);
  font-size: var(--xp-text-body-lg);
  color: var(--xp-ink-700);
  text-align: center;
}

/* Each logo's display size is its width/height attributes, computed per logo
   for equal visual area. height:auto keeps the ratio when max-width shrinks a
   wide mark inside a narrow cell. */
.xp-brandgrid__logo {
  display: block;
  max-width: 100%;
  height: auto;
  object-fit: contain;
}
/* GE publishes its logo on a white plate; multiply drops the plate into the
   band colour instead of leaving a white box. */
.xp-brandgrid__logo[src$="/ge.png"] { mix-blend-mode: multiply; }

/* Logo wall: centred rows instead of a fixed grid, so a short last row sits
   in the middle rather than hanging off the left edge. */
.xp-brandgrid--wall {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--xp-space-8) var(--xp-space-12);
  max-width: 1040px;
  margin-inline: auto;
}
.xp-brandgrid--wall li { min-height: 44px; }
@media (max-width: 599px) { .xp-brandgrid--wall { gap: var(--xp-space-6) var(--xp-space-8); } }
/* Primary tier: eight brands, so a fixed grid divides them evenly - 4 x 2 on
   desktop, 2 x 4 on a phone - instead of a flex row wrapping 6 + 2. */
.xp-brandgrid--wall:not(.xp-brandgrid--minor) {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  justify-items: center;
  max-width: 880px;
}
@media (min-width: 700px) {
  .xp-brandgrid--wall:not(.xp-brandgrid--minor) { grid-template-columns: repeat(4, 1fr); }
}
/* Secondary tier: smaller logos in centred rows under the primary grid. */
.xp-brandgrid--minor {
  margin-top: var(--xp-space-8);
  padding-top: var(--xp-space-8);
  border-top: 1px solid var(--xp-border-strong);
  /* Narrow enough that nine logos wrap 5 + 4 rather than 7 + 2. */
  max-width: 660px;
  gap: var(--xp-space-6) var(--xp-space-10);
}
/* Wide screens: two rows in total - the eight main brands on one line, the
   nine secondary ones on the next - spread across the full container. */
@media (min-width: 1100px) {
  .xp-brandgrid--wall:not(.xp-brandgrid--minor),
  .xp-brandgrid--minor {
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-between;
    max-width: none;
    gap: var(--xp-space-6);
  }
  /* The secondary row keeps a fixed gap rather than spreading edge to edge,
     so a short row (six washer brands) does not open wide holes. 36px still
     fits the homepage's nine across the 1200px container. */
  .xp-brandgrid--minor { justify-content: center; gap: 36px; }
}

/* The service page is left-aligned throughout, so its wall follows suit. */
.xp-service-page .xp-brandgrid--minor { justify-content: flex-start; }
.xp-service-page .xp-brandgrid__more { text-align: left; }
.xp-brandgrid__more {
  margin: var(--xp-space-8) 0 0;
  text-align: center;
  font-size: var(--xp-text-body);
  color: var(--xp-ink-700);
}

/* Plain town list: no links until the city pages exist. */
.xp-townline {
  margin: var(--xp-space-4) 0 0;
  font-size: var(--xp-text-body-lg);
  line-height: var(--xp-leading-loose);
  color: var(--xp-ink-700);
  max-width: 58ch;
}

/* A section divided from the one above by a rule instead of a background. Used
   where two white sections meet and a third band would add to the striping. */
.xp-section--ruled { border-top: 1px solid var(--xp-border); }
