/* ==========================================================================
   Aarika Fabric Care — stylesheet
   Plain CSS. No build step, no framework. Runs on any normal web host.

    1 Reset            2 Tokens           3 Layout
    4 Type             5 Header & nav     6 Buttons
    7 Cards            8 Hero             9 Services & steps
   10 Packages        11 Reviews         12 Forms
   13 Contact         14 Footer          15 Motion
   16 Responsive      17 Utilities
   ========================================================================== */

/* ---------- 1. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 96px;      /* sticky header clearance for #anchors */
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

/* The browser hides [hidden] with display:none, but that is a single attribute
   selector and loses to any class rule that sets display — ".rate-list li"
   setting flex, for one. The result is JavaScript that reports an element as
   hidden while it is still on screen, which is exactly how the rate card kept
   showing all 220 rows while filtering "worked". Make it win outright. */
[hidden] { display: none !important; }

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: var(--blue-deep); text-decoration: none; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.015em;
  text-wrap: balance;
  color: var(--ink);
}

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ---------- 2. Tokens ---------- */
:root {
  --font-display: Quicksand, "Segoe UI", system-ui, sans-serif;
  --font-body: "Nunito Sans", "Segoe UI", system-ui, sans-serif;

  /* Ink */
  --ink: #2E4A5C;
  --ink-soft: #4E6C7B;
  --ink-faint: #55737F;

  /* Blues — the brand's primary family */
  --blue: #5FA9C4;
  --blue-light: #8FCFE0;
  --blue-pale: #7FBED4;
  --blue-deep: #3A6076;
  --blue-slate: #5A8298;

  /* Grounds */
  --cream: #FDF8F3;
  --white: #FFFFFF;
  --mist: #EAF5F9;
  --sand: #F3EFE7;
  --mist-deep: #DCEEF5;

  /* Supporting accents, used sparingly and always semantically */
  --peach: #DB9C80;
  --peach-pale: #FBEEE6;
  --green: #7CBFA3;
  --green-pale: #E9F5EF;
  --purple: #9A8FC4;

  --line: #E1ECF2;

  --shadow-sm: 0 2px 8px rgba(46, 74, 92, .05);
  --shadow: 0 14px 34px -18px rgba(46, 74, 92, .28);
  --shadow-lg: 0 30px 60px -30px rgba(46, 74, 92, .35);

  --r-pill: 999px;
  --r-lg: clamp(26px, 4vw, 40px);
  --r-md: clamp(18px, 2.6vw, 26px);
  --r-sm: 14px;

  --container: 1180px;
  --gutter: clamp(18px, 4.5vw, 40px);
  --section-y: clamp(56px, 8vw, 100px);

  --ease: cubic-bezier(.22, 1, .36, 1);
}

/* ---------- 3. Layout ---------- */
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: max(var(--gutter), env(safe-area-inset-left));
  padding-inline-end: max(var(--gutter), env(safe-area-inset-right));
}

.section { padding-block: var(--section-y); position: relative; }
.section--tight { padding-block: clamp(34px, 5vw, 60px); }

/* Tinted sections are inset rounded panels sitting on the cream page, not
   full-width bands — the alternating blue and sand give the page its rhythm. */
.panel {
  border-radius: clamp(28px, 4vw, 48px);
  margin-inline: clamp(8px, 2vw, 20px);
  /* The reference gives a tinted panel slightly less height than a plain
     section, so the tint reads as a card rather than a band. */
  padding-block: clamp(52px, 8vw, 96px);
}
.panel--blue { background: var(--mist); }
.panel--sand { background: var(--sand); }

.grid { display: grid; gap: clamp(16px, 2.4vw, 26px); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

.stack { display: grid; gap: 18px; }

/* ---------- 4. Type ---------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--blue-deep);
  margin-bottom: 14px;
}
.eyebrow::before {
  content: "";
  width: 22px;
  height: 2px;
  border-radius: 2px;
  background: var(--blue-light);
}

.h-xl { font-size: clamp(32px, 5.4vw, 58px); line-height: 1.08; }
.h-lg { font-size: clamp(26px, 3.6vw, 40px); }
.h-md { font-size: clamp(20px, 2.2vw, 26px); }

.lede {
  font-size: clamp(16px, 1.1vw + .6rem, 19px);
  color: var(--ink-soft);
  max-width: 60ch;
}
.muted { color: var(--ink-soft); }
.small { font-size: 14.5px; }

.section-head { max-width: 62ch; margin-bottom: clamp(26px, 4vw, 46px); }
.section-head .lede { margin-top: 12px; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow { justify-content: center; }
.section-head--center .lede { margin-inline: auto; }

.hi { font-size: 16px; color: var(--blue-deep); font-weight: 600; }

/* ---------- 5. Header & nav ---------- */
.promo {
  background: #D8ECF4;
  color: var(--blue-deep);
  font-size: 13.5px;
  padding: 9px 0;
}
.promo .wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px 16px;
  flex-wrap: wrap;
  text-align: center;
}
.promo a { color: var(--ink); font-weight: 700; }
.promo .dot { color: var(--blue); }
.promo b { color: var(--ink); }

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(253, 248, 243, .88);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.site-header.is-stuck {
  border-bottom-color: var(--line);
  box-shadow: 0 6px 22px -18px rgba(46, 74, 92, .5);
}

.nav {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 72px;
}

.brand { display: flex; align-items: center; gap: 11px; margin-inline-end: auto; }
.brand-mark {
  width: 42px;
  height: 42px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 13px;
  background: linear-gradient(150deg, var(--mist), var(--mist-deep));
}
.brand-mark svg { width: 25px; height: 25px; }
.brand-text { display: grid; line-height: 1; }
.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -.01em;
  color: var(--ink);
}
.brand-sub {
  font-size: 9.5px;
  letter-spacing: .22em;
  font-weight: 700;
  /* Mid-blue read at 2.4:1 here. Part of the logo lockup, so WCAG exempts it,
     but 9.5px at that contrast is hard work — the deeper blue keeps the look
     and reaches 6.4:1. */
  color: var(--blue-deep);
  margin-top: 3px;
}

.nav-links { display: flex; align-items: center; gap: 4px; }
.nav-links a {
  padding: 9px 14px;
  border-radius: var(--r-pill);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
  transition: color .2s var(--ease), background .2s var(--ease);
}
.nav-links a[aria-current="page"] { color: var(--ink); background: var(--mist); }

.nav-cta { display: flex; align-items: center; gap: 10px; }

.nav-toggle {
  display: none;
  width: 46px;
  height: 46px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: var(--white);
}
.nav-toggle span { position: relative; display: block; width: 18px; height: 2px; border-radius: 2px; background: var(--ink); }
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform .25s var(--ease), top .25s var(--ease);
}
.nav-toggle span::before { top: -6px; }
.nav-toggle span::after { top: 6px; }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after { top: 0; transform: rotate(-45deg); }

/* ---------- 6. Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 14px 26px;
  border: 0;
  border-radius: var(--r-pill);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15.5px;
  line-height: 1.2;
  text-align: center;
  transition: transform .2s var(--ease), box-shadow .25s var(--ease), background .25s var(--ease);
}
.btn svg { width: 18px; height: 18px; flex: none; }

.btn--primary {
  background: var(--blue-deep);
  color: #fff;
  box-shadow: 0 12px 26px -14px rgba(58, 96, 118, .9);
}
.btn--primary:hover { color: #fff; background: #325469; }

.btn--ghost {
  background: var(--white);
  color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.btn--ghost:hover { color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--blue-light); }

.btn--light { background: #fff; color: var(--blue-deep); }
.btn--sm { padding: 10px 18px; font-size: 14px; }
.btn--block { display: flex; width: 100%; }
.btn:active { transform: translateY(1px); }

.btn-row { display: flex; flex-wrap: wrap; gap: 12px; }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-family: var(--font-display);
  color: var(--blue-deep);
}
.link-arrow svg { width: 16px; height: 16px; transition: transform .2s var(--ease); }

/* ---------- 7. Cards ---------- */
.card {
  background: var(--white);
  border-radius: var(--r-md);
  padding: clamp(20px, 2.6vw, 30px);
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--line);
}

.icon-badge {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border-radius: 16px;
  background: var(--mist);
  margin-bottom: 16px;
}
.icon-badge svg { width: 27px; height: 27px; }
.icon-badge--green { background: var(--green-pale); }
.icon-badge--peach { background: var(--peach-pale); }
.icon-badge--lilac { background: #F0EDF9; }

.card h3 { font-size: 18.5px; margin-bottom: 7px; }
.card p { color: var(--ink-soft); font-size: 15px; }

/* Photo frames */
.photo {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--mist);
  box-shadow: var(--shadow);
}
.photo img { width: 100%; height: 100%; object-fit: cover; }
.photo--tall { aspect-ratio: 7 / 5; }
.photo--wide { aspect-ratio: 2 / 1; }
.photo--square { aspect-ratio: 4 / 3; }

/* ---------- 8. Hero ---------- */
.hero {
  position: relative;
  padding-block: clamp(36px, 6vw, 76px) clamp(48px, 7vw, 90px);
  overflow: hidden;
}
.hero > .wrap { position: relative; z-index: 1; }

.hero-grid {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: clamp(30px, 5vw, 64px);
  align-items: center;
}

.hero h1 { font-size: clamp(32px, 5.6vw, 60px); line-height: 1.06; }
.hero .lede { margin-top: 18px; }
.hero .btn-row { margin-top: 26px; }

.hero-points {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 26px;
}
.hero-points li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink-soft);
}
.hero-points svg { width: 17px; height: 17px; flex: none; }

.hero-media { position: relative; }

/* Inner-page hero. Same split as the home page, but the copy column leads and
   the photo supports it, so a section page never competes with the front
   door. The heading also steps down a size — a home page shouts once; five
   more pages shouting is just noise. */
/* The reference sets the inner-page hero tighter than the home page's,
   especially underneath, so a section page starts its content sooner. */
.hero--page { padding-block: clamp(44px, 7vw, 80px) clamp(40px, 6vw, 64px); }
.hero--page .hero-grid { grid-template-columns: 1.1fr .9fr; }
.hero--page h1 { font-size: clamp(30px, 4.4vw, 50px); }
/* The home page leads with its photo on a phone; a section page leads with
   what the page is about. */
.hero--page .hero-media { order: 0; }

/* A single quiet note pinned to the photo — one fact, never a slogan. */
.hero-badge--note {
  left: -14px;
  bottom: 24px;
  max-width: 17.5rem;
  padding: 14px 18px;
  align-items: flex-start;
  gap: 11px;
}
.hero-badge--note svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.hero-badge--note .k {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14.5px;
  line-height: 1.35;
  color: var(--blue-deep);
}
.hero-badge--note .s {
  display: block;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-faint);
  margin-top: 2px;
}

@media (max-width: 520px) {
  .hero-badge--note { left: 8px; right: 8px; bottom: 10px; max-width: none; }
}

.hero-badge {
  position: absolute;
  z-index: 2;
  background: var(--white);
  border-radius: 18px;
  padding: 13px 18px;
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  gap: 12px;
}
/* The offer badge hangs off a string like a laundry tag and sways gently,
   as it did in the original design. The card keeps a punched hole at the top
   where the string meets it. */
.hang {
  position: absolute;
  z-index: 2;
  left: -14px;
  bottom: 26px;
  transform-origin: top center;
  animation: swayTag 6s ease-in-out infinite;
}
.hang-string { width: 2px; height: 26px; background: #D6CFC4; margin: 0 auto; }

.hero-badge--offer {
  position: relative;
  display: block;
  text-align: center;
  padding: 22px 26px 18px;
}
.hero-badge--offer::before {
  content: "";
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #E7EFF3;
}
.hero-badge--offer .big {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 34px;
  /* The original used a pale blue here, which reads at 2.06:1 on white.
     The deeper blue keeps the look and clears AA. */
  color: var(--blue-deep);
  line-height: 1;
  margin-top: 10px;
}
.hero-badge--offer .cap {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.35;
  margin-top: 7px;
}
.hero-badge--turn {
  top: 20px;
  right: -12px;
  background: var(--blue-deep);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  padding: 10px 16px;
  border-radius: var(--r-pill);
}

/* Trust strip */
.trust {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(10px, 1.6vw, 20px);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: clamp(16px, 2.2vw, 24px);
  box-shadow: var(--shadow-sm);
}
.trust li {
  display: flex;
  align-items: center;
  gap: 11px;
  font-weight: 700;
  font-size: 14.5px;
  font-family: var(--font-display);
  color: var(--ink);
}
.trust svg { width: 26px; height: 26px; flex: none; }

/* ---------- 9. Services & steps ---------- */
.service-card { display: flex; flex-direction: column; height: 100%; position: relative; }

/* The whole card is the target, but only the title carries the link, so the
   accessible name is the service rather than the paragraph after it, and the
   card is one stop on the keyboard instead of several. */
.card-link {
  color: inherit;
  text-decoration: none;
}
.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-md);
  /* The link sits inside the heading, so its overlay paints below the
     paragraph and price that follow. Without this the card looks clickable
     and only the title actually is. */
  z-index: 1;
}
.card-link:focus-visible { outline: none; }
.card-link:focus-visible::after {
  outline: 2px solid var(--blue-deep);
  outline-offset: 3px;
}
.service-card .price {
  margin-top: auto;
  padding-top: 16px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  align-items: baseline;
}
.service-card .price b {
  font-family: var(--font-display);
  font-size: 17px;
  color: var(--blue-deep);
}
.service-card .price span {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: var(--mist);
  padding: 4px 10px;
  border-radius: var(--r-pill);
}

.steps { counter-reset: step; }
.step { position: relative; }
.step .num {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--blue-deep);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  margin-bottom: 15px;
}
.step h3 { font-size: 18px; margin-bottom: 6px; }
.step p { color: var(--ink-soft); font-size: 15px; }

/* ---------- 10. Packages ---------- */
.plan {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(24px, 3vw, 34px);
  box-shadow: var(--shadow-sm);
  position: relative;
}
.plan--feature {
  background: linear-gradient(165deg, #37596E, #2E4A5C);
  border-color: transparent;
  color: #EAF5F9;
  box-shadow: var(--shadow-lg);
}

.plan-tag {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: var(--r-pill);
  background: var(--mist);
  color: var(--blue-deep);
  margin-bottom: 16px;
}
.plan--feature .plan-tag { background: rgba(255, 255, 255, .16); color: #fff; }

.plan-price {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(34px, 4.4vw, 46px);
  line-height: 1;
  color: var(--ink);
}
.plan-sub { color: var(--ink-soft); font-size: 15px; margin-top: 6px; }
.plan ul { display: grid; gap: 11px; margin: 22px 0; }
.plan li { display: flex; gap: 10px; font-size: 15px; color: var(--ink-soft); }
.plan li svg { width: 18px; height: 18px; flex: none; margin-top: 4px; }

.plan--feature h3, .plan--feature .plan-price { color: #fff; }
.plan--feature .plan-sub, .plan--feature li { color: rgba(234, 245, 249, .9); }
.plan .btn { margin-top: auto; }
.plan-terms {
  margin: 10px 0 0;
  font-size: 12px;
  line-height: 1.5;
  text-align: center;
  color: var(--ink-faint);
}
.plan--feature .plan-terms { color: rgba(234, 245, 249, .78); }

.addon {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 18px;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.addon:last-child { border-bottom: 0; }
.addon h3 { font-size: 16.5px; }
.addon p { font-size: 14.5px; color: var(--ink-soft); flex: 1 1 320px; }
.addon .cost { font-family: var(--font-display); font-weight: 700; color: var(--blue-deep); white-space: nowrap; }

/* ---------- 11. Reviews ---------- */
.review { display: flex; flex-direction: column; height: 100%; }
.stars { display: flex; gap: 2px; color: var(--peach); margin-bottom: 13px; }
.stars svg { width: 17px; height: 17px; fill: currentColor; }
.review p { font-size: 15.5px; color: var(--ink-soft); }
.review-by { display: flex; align-items: center; gap: 12px; margin-top: auto; padding-top: 20px; }
.review-avatar {
  width: 42px;
  height: 42px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--mist);
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--blue-deep);
}
.review-by b { display: block; font-size: 15px; }
.review-by span { display: block; font-size: 13px; color: var(--ink-faint); }

/* ---------- 12. Forms ---------- */
.form-grid { display: grid; gap: 16px; }
.field { display: grid; gap: 7px; }
.field label { font-size: 14px; font-weight: 700; color: var(--ink); }
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 13px 15px;
  font-size: 16px;                 /* 16px minimum or iOS zooms on focus */
  background: var(--white);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.field textarea { resize: vertical; min-height: 108px; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(95, 169, 196, .16);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.form-note { font-size: 13.5px; color: var(--ink-faint); text-align: center; }

.form-msg {
  display: none;
  padding: 14px 16px;
  border-radius: var(--r-sm);
  font-size: 15px;
  background: var(--green-pale);
  color: #24705A;
}
.form-msg.is-error { background: #FCEDE9; color: #A4462C; }
.form-msg.is-shown { display: block; }

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

/* ---------- 13. Contact ---------- */
.contact-line {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}

/* The icon beside each row already says what it is, so the label is dropped
   from view. It stays in the markup because the icon is aria-hidden — without
   it a screen reader would read a bare number and a bare address with nothing
   saying which is the phone and which is the shop. */
.contact-line .k {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.contact-line:last-child { border-bottom: 0; }
.contact-line svg { width: 22px; height: 22px; flex: none; margin-top: 3px; }
.contact-line .k {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.contact-line .v { font-weight: 600; }

.hours { display: grid; gap: 2px; }
.hours div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px dashed var(--line);
  font-size: 15px;
}
.hours div:last-child { border-bottom: 0; }
.hours span:last-child { font-weight: 700; color: var(--ink); }

.map-frame {
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  background: var(--mist);
  aspect-ratio: 4 / 3;
}
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Closing call-to-action band */
.cta-band {
  background: linear-gradient(160deg, #37596E, #2E4A5C);
  border-radius: var(--r-lg);
  padding: clamp(30px, 5vw, 60px);
  color: #EAF5F9;
  text-align: center;
}
.cta-band h2 { color: #fff; }
.cta-band p { color: rgba(234, 245, 249, .84); margin: 14px auto 0; max-width: 52ch; }
.cta-band .btn-row { justify-content: center; margin-top: 26px; }

/* ---------- 14. Footer ---------- */
.site-footer { background: var(--mist); padding-block: clamp(40px, 6vw, 70px) 28px; margin-top: clamp(40px, 6vw, 80px); }
.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1.3fr;
  gap: clamp(24px, 3.4vw, 46px);
}
.footer-grid h3 {
  font-size: 12.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 16px;
}
.footer-grid ul { display: grid; gap: 11px; }
.footer-grid a, .footer-grid li { color: var(--ink-soft); font-size: 15px; }
.footer-grid a:hover { color: var(--blue-deep); }
.footer-blurb { color: var(--ink-soft); font-size: 15px; margin-top: 16px; max-width: 34ch; }

.socials { display: flex; gap: 10px; margin-top: 20px; }
.socials a {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 13px;
  background: var(--white);
  border: 1px solid var(--line);
  transition: transform .2s var(--ease), background .2s var(--ease);
}
.socials svg { width: 18px; height: 18px; }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px 24px;
  margin-top: clamp(28px, 4vw, 46px);
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--ink-faint);
}

/* Sticky call bar — phones only. A laundry runs on phone calls. */
.callbar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  gap: 10px;
  padding: 10px max(12px, env(safe-area-inset-left)) calc(10px + env(safe-area-inset-bottom));
  background: rgba(253, 248, 243, .94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
}
.callbar .btn { flex: 1; }

/* ---------- 15. Motion ----------
   Reveals are gated on `html.js`, set by an inline script in <head> before
   first paint, so nothing is ever visible and then yanked away, and with
   JavaScript off no content is hidden at all.
   Only opacity and transform animate — both composited, so scrolling stays
   on the GPU and never triggers layout. */
html.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
}
html.js [data-reveal="left"]  { transform: translate3d(-24px, 0, 0); }
html.js [data-reveal="right"] { transform: translate3d(24px, 0, 0); }
html.js [data-reveal="scale"] { transform: scale(.965); }

/* On a narrow screen a sideways start offset pushes the page wider than the
   viewport until the element animates in, so the phone can be dragged a few
   pixels off-centre. Below the tablet breakpoint every reveal rises instead. */
@media (max-width: 760px) {
  html.js [data-reveal="left"],
  html.js [data-reveal="right"] { transform: translate3d(0, 18px, 0); }
}

/* Doubled class so the revealed state outranks every hidden rule above,
   whatever order they end up in. */
/* If main.js never arrived, the head script gives up after 2.5s and everything
   is shown at once. Better a page with no animation than no page. */
html.no-reveal [data-reveal] { opacity: 1 !important; transform: none !important; }

html.js [data-reveal].is-in.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}

/* ---------- Soap bubbles ----------
   Decorative bubbles carried over from the original design: a white highlight
   at 32%/30% of a soft circle, drifting slowly. Purely ornamental, so they are
   hidden from assistive tech and never take a pointer event. Size, position,
   tint and duration vary per bubble and are set where each one is placed.
   The drift animates only transform and opacity, so it stays on the compositor
   and costs nothing on scroll. */
@keyframes bubbleUp {
  0%   { transform: translate3d(0, 10px, 0) scale(.96); opacity: .5; }
  50%  { transform: translate3d(0, -14px, 0) scale(1);  opacity: .9; }
  100% { transform: translate3d(0, 10px, 0) scale(.96); opacity: .5; }
}

.bubble {
  position: absolute;
  --bs: var(--bs-base);
  width: var(--bs);
  height: var(--bs);
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
  will-change: transform;
  background: radial-gradient(circle at 32% 30%, #fff 6%, #DCEEF5 45%, #C7E2EE 100%);
  animation: bubbleUp 10s ease-in-out infinite;
}
.bubble--mint  { background: radial-gradient(circle at 32% 30%, #fff 6%, #E4F2E9 45%, #D2EADF 100%); }
.bubble--peach { background: radial-gradient(circle at 32% 30%, #fff 6%, #FBE7DE 45%, #F6D8CA 100%); }

/* A section holding bubbles has to clip them, or they widen the page. */
.has-bubbles { position: relative; overflow: hidden; }
/* Keep the real content above the decoration. */
.has-bubbles > .wrap, .hero > .wrap { position: relative; z-index: 1; }

/* Bubbles are ornament, not content: on a phone they crowd the text and cost
   paint time on the weakest devices, so the smallest ones step aside. */
/* On a phone the copy runs the full width, so a bubble always ends up behind
   the heading. It stays, but faint enough to read as a wash rather than a
   smudge. The opacity has to come from a second set of keyframes: the running
   animation writes opacity every frame and would overwrite a plain rule. */
@keyframes bubbleUpSoft {
  0%   { transform: translate3d(0, 10px, 0) scale(.96); opacity: .3; }
  50%  { transform: translate3d(0, -14px, 0) scale(1);  opacity: .5; }
  100% { transform: translate3d(0, 10px, 0) scale(.96); opacity: .3; }
}

@media (max-width: 700px) {
  /* Shrink the diameter itself: a transform here would be overwritten by the
     animation, which sets transform on every frame. */
  .bubble { --bs: calc(var(--bs-base) * .7); animation-name: bubbleUpSoft; }
  .bubble--small { display: none; }
}

/* A swaying tag reads as playful on a marketing page and as a distraction to
   anyone who asked for less motion — the reduced-motion block below stops
   both this and the bubbles. */
@keyframes swayTag {
  0%, 100% { transform: rotate(-2.5deg); }
  50%      { transform: rotate(2deg); }
}
.sway { transform-origin: top center; animation: swayTag 6s ease-in-out infinite; }

@keyframes floaty {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-9px); }
}
.floaty { animation: floaty 6s ease-in-out infinite; }

/* ---------- 15b. Rate card ----------
   218 priced items. The job is to make one line findable, so the search sits
   beside the tabs, prices align on a tabular figure, and the rows stay dense
   enough that a column reads as a list rather than a stack of cards. */
.rates { margin-top: clamp(22px, 3vw, 32px); }

.rates-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 18px;
  margin-bottom: clamp(18px, 2.4vw, 26px);
}

.rate-tabs { display: flex; flex-wrap: wrap; gap: 8px; }
.rate-tab {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--white);
  color: var(--ink-soft);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14.5px;
  padding: 10px 16px;
  border-radius: var(--r-pill);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: background .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
.rate-tab .n {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  background: var(--mist);
  border-radius: var(--r-pill);
  padding: 2px 7px;
}
.rate-tab.is-on {
  background: var(--blue-deep);
  border-color: var(--blue-deep);
  color: #fff;
}
.rate-tab.is-on .n { background: rgba(255, 255, 255, .18); color: #fff; }
.rate-tab:focus-visible { outline: 2px solid var(--blue-deep); outline-offset: 2px; }

.rates-search {
  display: flex;
  align-items: center;
  gap: 9px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 0 16px;
  flex: 1 1 210px;
  max-width: 320px;
}
.rates-search svg { width: 18px; height: 18px; flex: none; }
.rates-search input {
  border: 0;
  background: none;
  padding: 12px 0;
  width: 100%;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
}
.rates-search input:focus { outline: none; }
.rates-search:focus-within { border-color: var(--blue-pale); box-shadow: 0 0 0 3px rgba(127, 190, 212, .18); }
.rates-search input::-webkit-search-cancel-button { cursor: pointer; }

/* The category heading only earns its place once results span categories. */
.rate-cat {
  font-size: 15px;
  margin: 0 0 10px;
  color: var(--ink-soft);
  display: none;
}
.rates.is-searching .rate-cat { display: block; }
.rates.is-searching .rate-panel + .rate-panel { margin-top: 26px; }

.rate-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 290px), 1fr));
  gap: 0 clamp(22px, 3.4vw, 52px);
}
.rate-list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 2px;
  border-bottom: 1px dashed var(--line);
  font-size: 15px;
}
.rate-name { flex: 1 1 auto; color: var(--ink-soft); }
.rate-price {
  flex: none;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* Laundry is a two-rate table rather than a single price. */
.rate-weight { max-width: 640px; }
.rate-weight-head,
.rate-list--weight li {
  display: grid;
  grid-template-columns: 1fr 8.5rem 8.5rem;
  gap: 10px;
  align-items: baseline;
}
.rate-weight-head {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 0 2px 8px;
}
.rate-list--weight { grid-template-columns: 1fr; }
.rate-foot,
.rate-note {
  margin: 16px 0 0;
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--ink-soft);
}
.rate-note { margin-top: 22px; }
.rates-empty {
  margin: 22px 0 0;
  font-size: 15px;
  color: var(--ink-soft);
}
.rates-empty a { font-weight: 700; }

@media (max-width: 560px) {
  .rate-weight-head, .rate-list--weight li { grid-template-columns: 1fr 5.6rem 5.6rem; }
  .rate-weight-head { font-size: 11px; }
  .rate-list--weight li { font-size: 14px; }
}

/* ---------- 15c. Rate card filter sheet ----------
   On a phone a row of category pills competes with the search box for a line
   that is not there. The pills give way to a single Filter button that opens a
   sheet from the bottom, the pattern every shopping app uses, and the bar the
   two controls sit in sticks to the top so neither scrolls away in a list of
   two hundred items. */
.rates-filter { display: none; }

@media (max-width: 640px) {
  .rates-bar {
    position: sticky;
    top: var(--header-h, 64px);
    z-index: 5;
    margin-inline: calc(-1 * var(--gutter));
    padding: 10px var(--gutter);
    background: var(--sand);
    border-bottom: 1px solid rgba(46, 74, 92, .08);
    gap: 10px;
    flex-wrap: nowrap;
  }
  .rate-tabs { display: none; }
  .rates-search { max-width: none; flex: 1 1 auto; }
  .rates-search input { padding: 10px 0; }

  .rates-filter {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: none;
    min-height: 44px;
    padding: 0 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--white);
    color: var(--ink);
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
  }
  .rates-filter svg { width: 17px; height: 17px; flex: none; }
  .rates-filter .val { color: var(--blue-deep); }
}

/* The sheet itself */
.sheet[hidden] { display: none; }
.sheet {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: flex-end;
}
.sheet-scrim {
  position: absolute;
  inset: 0;
  background: rgba(29, 48, 60, .45);
  animation: sheetFade .2s var(--ease);
}
.sheet-panel {
  position: relative;
  width: 100%;
  max-height: 80dvh;
  overflow-y: auto;
  background: var(--cream);
  border-radius: 22px 22px 0 0;
  padding: 8px 0 max(14px, env(safe-area-inset-bottom));
  animation: sheetUp .26s var(--ease);
}
@keyframes sheetFade { from { opacity: 0; } }
@keyframes sheetUp { from { transform: translateY(100%); } }

.sheet-grip {
  width: 40px;
  height: 4px;
  border-radius: 4px;
  background: var(--line);
  margin: 6px auto 10px;
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 18px 10px;
}
.sheet-head h3 { margin: 0; font-size: 17px; }
.sheet-close {
  appearance: none;
  border: 0;
  background: none;
  color: var(--ink-soft);
  font-size: 15px;
  font-weight: 700;
  min-height: 44px;
  padding: 0 6px;
  cursor: pointer;
}
.sheet-list { list-style: none; margin: 0; padding: 0 8px; }
.sheet-opt {
  appearance: none;
  width: 100%;
  border: 0;
  background: none;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 0 12px;
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink-soft);
  cursor: pointer;
  text-align: left;
}
.sheet-opt .tick {
  margin-left: auto;
  width: 20px;
  height: 20px;
  flex: none;
  opacity: 0;
}
.sheet-opt .count {
  font-size: 13px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.sheet-opt.is-on { background: var(--mist); color: var(--ink); font-weight: 700; }
.sheet-opt.is-on .tick { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .sheet-scrim, .sheet-panel { animation: none; }
}

/* ---------- 15d. Floating contact buttons ----------
   Bottom right, above everything, on every page. On a phone the call bar
   already carries "Call now", so only WhatsApp floats there, lifted clear of
   the bar rather than sitting on top of it. */
.fab {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 75;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fab-btn {
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(46, 74, 92, .3);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.fab-btn svg { width: 27px; height: 27px; }
.fab-call { background: var(--blue-deep); }
/* WhatsApp's own green, so the button is recognised at a glance */
.fab-wa { background: #25D366; }
.fab-btn:focus-visible { outline: 3px solid var(--blue-deep); outline-offset: 3px; }

@media (hover: hover) {
  .fab-btn:hover { transform: translateY(-2px); box-shadow: 0 12px 26px rgba(46, 74, 92, .34); }
}

@media (max-width: 640px) {
  /* clear of the call bar, which is 74px of fixed furniture */
  .fab { bottom: calc(74px + max(12px, env(safe-area-inset-bottom))); }
  .fab-call { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .fab-btn { transition: none; }
}

/* ---------- 16. Responsive ----------
   Sized fluidly above; these breakpoints only change what the layout *is*,
   not what size things are. */

@media (max-width: 1080px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* .hero--page .hero-grid sets two columns and carries more weight than the
     line above, so the page heroes need collapsing by name or they stay in two
     columns down to the narrowest phone. */
  .hero--page .hero-grid { grid-template-columns: 1fr; }
  .hero-media { order: -1; }
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  .grid--3 { grid-template-columns: repeat(2, 1fr); }
  .split { grid-template-columns: 1fr; }
  .trust { grid-template-columns: repeat(2, 1fr); }

  /* Nav collapses into a panel */
  .nav-links {
    position: fixed;
    inset: 0 0 auto;
    top: var(--header-h, 72px);
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 14px max(var(--gutter), env(safe-area-inset-left)) 22px;
    background: var(--cream);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 26px 40px -30px rgba(46, 74, 92, .6);
    max-height: calc(100dvh - var(--header-h, 72px));
    overflow-y: auto;
  }
  .nav-links.is-open { display: flex; }
  .nav-links a {
    padding: 14px 12px;
    font-size: 17px;
    border-radius: var(--r-sm);
    border-bottom: 1px solid var(--line);
  }
  .nav-links a:last-child { border-bottom: 0; }
  .nav-toggle { display: inline-flex; }
  .nav-cta .btn { display: none; }
}

@media (max-width: 640px) {
  html { scroll-padding-top: 80px; }
  .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr; }
  .trust { grid-template-columns: 1fr; gap: 4px; }
  .trust li { padding: 6px 0; }
  /* Two short link lists sit side by side; the brand block and the contact
     details, which run wider, keep the full width. Four stacked blocks made
     the footer a seventh of the page. */
  /* An inner page opens with a photograph in its hero. A second one directly
     beneath it is a banner on a desktop and a wall of picture on a phone, so
     it steps aside. It is lazy-loaded, so hidden here it is never fetched. */
  .hide-phone { display: none; }

  .footer-grid { grid-template-columns: 1fr 1fr; gap: 24px 18px; }
  .footer-grid > :first-child,
  .footer-grid > :last-child { grid-column: 1 / -1; }
  .footer-grid h3 { font-size: 14px; margin-bottom: 10px; }
  .footer-grid a, .footer-grid li { font-size: 14.5px; }
  .footer-blurb { font-size: 14px; margin-top: 10px; }
  .site-footer { padding-block: 34px 0; }
  .footer-bottom { margin-top: 22px; padding-block: 14px; font-size: 12.5px; }
  .promo .wrap { font-size: 12.5px; gap: 6px 10px; }
  .promo .hide-xs { display: none; }

  .hang { left: 4px; bottom: 12px; }
  .hang-string { height: 16px; }
  .hero-badge--offer { padding: 16px 18px 13px; }
  .hero-badge--offer .big { font-size: 26px; margin-top: 8px; }
  .hero-badge--turn { right: 10px; top: 12px; font-size: 11.5px; }

  .btn { width: 100%; }
  .btn-row .btn { width: auto; flex: 1 1 auto; }

  /* The call bar appears, so the page needs room to scroll past it */
  .callbar { display: flex; }
  body { padding-bottom: 74px; }
  .callbar .btn { width: auto; }
}

/* ---------- 16b. Phone layout ----------
   Collapsing a grid to one column is not a mobile layout: it takes a card
   built to be read across and makes it tall, and a page that was seven
   screens on a desktop becomes thirteen. Below this width the components
   change shape rather than just stack — cards turn into rows, the icon moves
   beside the text instead of above it, and the section rhythm tightens. */
@media (max-width: 640px) {
  :root {
    --section-y: clamp(34px, 7vw, 48px);
    --gutter: clamp(16px, 4.5vw, 22px);
  }

  .section-head { margin-bottom: 18px; }
  .eyebrow { margin-bottom: 10px; font-size: 11.5px; }

  /* A 32px headline runs to three lines on a 375px screen, which reads as
     shouting rather than as a heading. These are the sizes a phone gets;
     the fluid scale above still governs everything wider. */
  .hero h1,
  .hero--page h1 { font-size: 27px; line-height: 1.14; }
  .h-xl { font-size: 26px; line-height: 1.16; }
  .h-lg { font-size: 21px; }
  .h-md { font-size: 18px; }
  h3 { font-size: 16px; }
  .lede { font-size: 15.5px; line-height: 1.65; }
  .h-lg { line-height: 1.15; }

  /* Service cards: icon beside the text, not stacked above it. Roughly
     halves the height of an eight-card list. */
  .service-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "icon title" "icon body" "icon price";
    column-gap: 13px;
    row-gap: 2px;
    align-items: start;
    padding: 15px 16px;
  }
  .service-card .icon-badge {
    grid-area: icon;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    margin: 2px 0 0;
  }
  .service-card .icon-badge svg { width: 21px; height: 21px; }
  .service-card h3 { grid-area: title; font-size: 15.5px; line-height: 1.25; }
  .service-card p { grid-area: body; font-size: 13.8px; line-height: 1.55; margin: 2px 0 0; }
  .service-card .price {
    grid-area: price;
    margin-top: 8px;
    padding-top: 8px;
    gap: 4px 10px;
    font-size: 14px;
  }
  .service-card .price span { font-size: 12px; }

  /* The numbered steps read the same way. */
  .step {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "num title" "num body";
    column-gap: 13px;
    align-items: start;
  }
  .step .num {
    grid-area: num;
    width: 36px;
    height: 36px;
    font-size: 15px;
    margin: 1px 0 0;
  }
  .step h3 { grid-area: title; font-size: 16.5px; }
  .step p { grid-area: body; font-size: 13.8px; line-height: 1.55; margin-top: 3px; }

  /* Reviews lose their padding, not their content. */
  .review { padding: 18px; }
  .review p { font-size: 14.5px; line-height: 1.62; }
  .review-by { padding-top: 14px; }
  .review-avatar { width: 36px; height: 36px; font-size: 14px; }

  .plan { padding: 20px 18px; }
  .plan ul { gap: 8px; margin: 16px 0; }
  .plan li { font-size: 14px; }
  .plan-price { line-height: 1; }

  .addon { padding: 13px 0; }

  /* The hero has the most air to give back. */
  .hero { padding-block: 22px 30px; }
  .hero .lede { margin-top: 12px; }
  .hero .btn-row { margin-top: 18px; }
  .hero-points { margin-top: 16px; gap: 6px 16px; }
  .hero--page .hero-media { margin-top: 20px; }

  .cta-band { padding: 26px 20px; }
  .panel { border-radius: 22px; padding-block: clamp(30px, 7vw, 44px); }

  /* The price wants about 150px of a 326px card, which left "Blankets and
     quilts" 80px and wrapped it. It gets its own line instead, so a title is
     always one line and the card height is predictable. */
  .service-card .price {
    grid-area: price;
    margin: 7px 0 0;
    padding: 0;
    font-size: 13.5px;
    white-space: nowrap;
  }
  /* The delivery note under each price repeats what the page says elsewhere;
     on a phone the room is worth more than the repetition. */
  .service-card .price span { display: none; }
  .service-card p { margin-top: 4px; }
  /* Grid items refuse to shrink past their content unless told to. Without
     this the nowrap price holds the card wider than a 320px screen. */
  .service-card h3,
  .service-card p,
  .service-card .price { min-width: 0; }

  /* Two short fields side by side is what a phone form should do; stacking
     every one of them is what makes the form scroll forever. */
  .field-row { grid-template-columns: 1fr 1fr; gap: 12px; }
  .field label { font-size: 13.5px; }

  /* Cards holding a heading and one line read fine two-up, and four of them
     become two rows instead of four. */
  .duo { grid-template-columns: 1fr 1fr; gap: 12px; }
  .duo .card { padding: 15px 14px; }
  .duo .card h3 { font-size: 15.5px; }
  .duo .card p { font-size: 13.2px; line-height: 1.5; margin-top: 5px; }

  /* Three cards side by side become a swipeable rail rather than three
     full-height cards stacked — the same move a shopping app makes. */
  .rail {
    grid-template-columns: none;
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    padding-bottom: 4px;
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail > * {
    flex: 0 0 84%;
    scroll-snap-align: start;
    min-width: 0;
  }
  .rail > .plan { height: auto; }
}

@media (max-width: 380px) {
  .brand-name { font-size: 18px; }
  .brand-mark { width: 38px; height: 38px; }
}

/* Touch devices: real 44px targets, and no hover effects that stick */
@media (pointer: coarse) {
  .btn, .nav-links a, .socials a, .nav-toggle { min-height: 44px; }
  .nav-links a { display: flex; align-items: center; }
  a, button { -webkit-tap-highlight-color: rgba(95, 169, 196, .14); touch-action: manipulation; }

  /* The brand lockup sits a few pixels under the minimum on its own */
  .brand { min-height: 44px; align-items: center; }
  .link-arrow { min-height: 44px; align-items: center; }
  .rate-tab { min-height: 44px; }

  /* Footer link lists run at a 37px pitch, which is comfortable with a mouse
     and cramped with a thumb. Trading the row gap for padding inside each
     link turns the whole row into the target and costs only 7px a line. */
  .footer-grid ul { row-gap: 0; }
  .footer-grid ul a { display: block; padding-block: 12px; }

  /* Phone numbers in the footer contact list are read as text but tapped as
     buttons, so they get the height outright rather than borrowing it. */
  .contact-line a { display: inline-block; padding-block: 11px; }

  /* The promo strip holds a single link, so its hit area can grow past the
     bar and pull the space back — the strip keeps its height. */
  .promo a { display: inline-block; padding-block: 12px; margin-block: -12px; }
}

@media (hover: hover) {
  .card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
  .service-card:has(.card-link) { cursor: pointer; }
  .service-card:has(.card-link):hover .card-link { color: var(--blue-deep); }
  .card { transition: transform .25s var(--ease), box-shadow .25s var(--ease); }
  .nav-links a:hover { color: var(--ink); background: var(--mist); }
  .socials a:hover { transform: translateY(-2px); background: var(--mist-deep); }
  .link-arrow:hover svg { transform: translateX(4px); }
  .btn--primary:hover { transform: translateY(-1px); }
}

/* Anyone asking for less motion gets a completely static page */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  html.js [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ---------- 17. Utilities ---------- */
.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;
}
.center { text-align: center; }
.mt-0 { margin-top: 0; }
