/* ==========================================================================
   Purple Sky Cleaning — core stylesheet
   Palette: white backgrounds with neutral greys; purple ink, logo and
   accents all drawn from one family. No competing accent hue.
   Deliberately avoids purple gradients / glassmorphism / icon-card grids.
   ========================================================================== */

:root {
  /* Palette is built outward from the logo's periwinkle, so every purple on
     the page belongs to the same family. The old aubergine + terracotta pair
     fought the logo — three unrelated purples and an orange. */

  /* Backgrounds are plain white with neutral greys. The purple lives in the
     logo, the type and the dark sections — not washed across the page. */
  --bone: #ffffff;       /* page background */
  --bone-deep: #f4f4f7;  /* neutral band for alternating sections */
  --paper: #ffffff;      /* cards and panels */

  --ink: #1c1733;        /* near-black carrying a purple cast */
  --ink-2: #56506e;
  --ink-3: #8b85a3;

  --plum: #2f2559;       /* deep brand purple: footer, top bar, dark sections */
  --plum-2: #6247a8;
  --plum-wash: #ede9f9;
  --periwinkle: #8b7bc8; /* the logo itself — too light for text or buttons */

  /* Primary action colour. A deepened periwinkle: same hue as the logo, dark
     enough that white text on it hits 7:1. */
  --accent: #6247a8;
  --accent-dark: #513a8f;
  --accent-wash: #ede9f9;

  --line: rgba(28, 23, 51, 0.12);
  --line-soft: rgba(28, 23, 51, 0.07);
  --line-invert: rgba(255, 255, 255, 0.18);

  --serif: "Fraunces", "Iowan Old Style", Georgia, serif;
  --sans: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  --wrap: 1180px;
  --gutter: 24px;
  --radius: 4px;
  --radius-lg: 10px;
}

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--bone);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img { max-width: 100%; display: block; }

a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  font-variation-settings: "SOFT" 0, "WONK" 0, "opsz" 40;
  letter-spacing: -0.015em;
  line-height: 1.08;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.6rem, 6vw, 4.15rem); }
h2 { font-size: clamp(2rem, 4.2vw, 2.95rem); }
h3 { font-size: clamp(1.25rem, 2.2vw, 1.55rem); line-height: 1.2; }
h4 { font-size: 1.08rem; line-height: 1.3; }

p { margin: 0 0 1.15em; text-wrap: pretty; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap-narrow { max-width: 760px; }

section { position: relative; }

.pad { padding-block: clamp(64px, 9vw, 118px); }
.pad-sm { padding-block: clamp(44px, 6vw, 76px); }

/* Collapse the seam between two sections that share a background.

   Both .pad and .pad-sm set padding on the top AND bottom, so stacking two
   sections put up to 118px + 118px = 236px of empty space between them. Where
   the background changes, that space is doing real work: it is what separates
   a white band from a grey one. Where it does not change, there is no band to
   separate and it just reads as a hole in the page, which is exactly what it
   looked like on /pricing and on every guide.

   So: only zero the top padding when the section above has the same
   background treatment. `.bg-plum` and `.cta-band` are excluded on both sides
   because a dark band always needs its own full breathing room. */
section:not([class*="bg-"]):not(.cta-band) + section:not([class*="bg-"]):not(.cta-band),
section.bg-paper + section.bg-paper {
  padding-top: 0;
}

/* At a background change the incoming band already carries its own generous
   top padding (up to 118px). The outgoing section's full bottom padding sits
   on top of that, which is where the ~194px holes above the grey FAQ bands
   came from. Trim the outgoing side so the boundary reads as one band of
   space rather than two stacked ones, while keeping enough that the content
   above is not resting on the colour edge. */
section:not([class*="bg-"]):not(.cta-band):has(+ section[class*="bg-"]),
section:not([class*="bg-"]):not(.cta-band):has(+ section.cta-band) {
  padding-bottom: clamp(24px, 3vw, 40px);
}

/* And the mirror: a plain section directly BELOW a coloured band. The band's
   own bottom padding already sits above it, so the plain section's full top
   padding lands on top of that as dead white space. Two adjacent coloured
   bands are left alone, because there the padding is inside the colour and
   is doing the work it was designed for. */
section[class*="bg-"] + section:not([class*="bg-"]):not(.cta-band),
section.cta-band + section:not([class*="bg-"]):not(.cta-band) {
  padding-top: clamp(24px, 3vw, 40px);
}

/* Body is white, so .bg-paper needs its own barely-there grey to keep the
   alternating section rhythm. Cards keep pure white via --paper. */
.bg-paper { background: #fafafc; }
.bg-bone-deep { background: var(--bone-deep); }
.bg-plum { background: var(--plum); color: #f6f1f4; }
.bg-plum h2, .bg-plum h3 { color: #fff; }

/* ---------- Type helpers ---------- */

.eyebrow {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 18px;
  display: block;
}

.bg-plum .eyebrow { color: #b9a9e6; }

.lead {
  font-size: clamp(1.08rem, 1.6vw, 1.24rem);
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 60ch;
}

.bg-plum .lead { color: rgba(255, 255, 255, 0.78); }

.serif-em {
  font-style: italic;
  font-variation-settings: "SOFT" 40, "WONK" 1, "opsz" 40;
}

.muted { color: var(--ink-3); }

.small {
  font-size: 0.86rem;
  line-height: 1.55;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  font-family: var(--sans);
  font-size: 0.97rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  padding: 15px 26px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); }

.btn-ink {
  background: var(--plum);
  color: #fff;
  border-color: var(--plum);
}
.btn-ink:hover { background: #241b47; border-color: #241b47; }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.btn-ghost:hover { border-color: var(--ink); background: rgba(28, 23, 51, 0.05); }

/* Ghost buttons sitting on any dark surface. The .cta-band selector is not
   optional — that band is dark but does not carry .bg-plum, and without it
   the button renders near-black on plum. */
.bg-plum .btn-ghost,
.cta-band .btn-ghost,
.calc-out .btn-ghost {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.45);
}
.bg-plum .btn-ghost:hover,
.cta-band .btn-ghost:hover,
.calc-out .btn-ghost:hover { border-color: #fff; background: rgba(255, 255, 255, 0.12); }

/* Now that the accent is a purple, a primary button on a deep-purple panel
   is purple-on-purple and stops reading as the main action. On dark
   surfaces it flips to white with purple text, which is the strongest
   contrast available and keeps the palette to one hue. */
.bg-plum .btn-primary,
.cta-band .btn-primary,
.calc-out .btn-primary {
  background: #fff;
  color: var(--plum);
  border-color: #fff;
}
.bg-plum .btn-primary:hover,
.cta-band .btn-primary:hover,
.calc-out .btn-primary:hover {
  background: var(--plum-wash);
  border-color: var(--plum-wash);
  color: var(--plum);
}

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

.textlink {
  color: var(--plum);
  text-decoration: none;
  border-bottom: 1px solid rgba(98, 71, 168, 0.32);
  padding-bottom: 1px;
  font-weight: 500;
}
.textlink:hover { border-bottom-color: var(--plum); }

/* ---------- Skip link + focus ---------- */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--plum);
  color: #fff;
  padding: 12px 20px;
  z-index: 999;
}
.skip:focus { left: 8px; top: 8px; }

a:focus-visible,
button:focus-visible,
select:focus-visible,
input:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---------- Announcement bar ---------- */

.topbar {
  background: var(--plum);
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.79rem;
  letter-spacing: 0.02em;
}
.topbar .wrap {
  display: flex;
  gap: 10px 26px;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  min-height: 40px;
  padding-block: 8px;
  text-align: center;
}
.topbar b { color: #fff; font-weight: 600; }
.topbar .dot { color: rgba(255, 255, 255, 0.35); }

/* On a phone the three claims wrap to three lines and eat ~100px of the
   first screen. Keep the strongest one (the rating) and drop the rest. */
@media (max-width: 640px) {
  .topbar .wrap > *:nth-child(n + 2) { display: none; }
  .topbar .wrap { min-height: 34px; padding-block: 6px; }
  .topbar { font-size: 0.76rem; }
}

/* ---------- Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(140%) blur(6px);
  border-bottom: 1px solid var(--line-soft);
}

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

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  margin-right: auto;
  flex-shrink: 0;
  color: var(--periwinkle);
}

/* ---------- Logo ----------
   Broom mark + stacked wordmark. Both inherit currentColor from .brand so a
   single inline SVG serves the light header and the plum footer. */
.brand-logo {
  width: 30px;
  height: 43px;
  flex-shrink: 0;
  display: block;
  overflow: visible;
}
.brand-word {
  display: flex;
  flex-direction: column;
  font-family: "Nunito", system-ui, -apple-system, sans-serif;
  font-weight: 800;
  font-size: 1.16rem;
  letter-spacing: -0.025em;
  line-height: 0.95;
  color: inherit;
  white-space: nowrap;
}
.site-footer .brand { color: #fff; }
.site-footer .brand-word { font-size: 1.28rem; }

@media (max-width: 560px) {
  .brand-logo { width: 25px; height: 36px; }
  .brand-word { font-size: 1rem; }
}

.brand-mark {
  font-family: var(--serif);
  font-size: 1.34rem;
  letter-spacing: -0.02em;
  color: var(--plum);
  line-height: 1;
}
.brand-mark i {
  font-style: italic;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
.brand-sub {
  font-size: 0.62rem;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 600;
}

.nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav a {
  text-decoration: none;
  font-size: 0.93rem;
  font-weight: 500;
  color: var(--ink-2);
  padding: 9px 12px;
  border-radius: var(--radius);
  white-space: nowrap;
}
.nav a:hover { color: var(--ink); background: rgba(28, 23, 51, 0.05); }
.nav a[aria-current="page"] { color: var(--plum); font-weight: 600; }

.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.header-phone {
  text-decoration: none;
  font-weight: 600;
  font-size: 0.93rem;
  color: var(--ink);
  white-space: nowrap;
}
.header-phone:hover { color: var(--accent); }
.header-actions .btn { padding: 11px 19px; font-size: 0.9rem; }

.nav-toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  width: 42px;
  height: 40px;
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.nav-toggle span {
  display: block;
  width: 18px;
  height: 1.5px;
  background: var(--ink);
  position: relative;
}
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 1.5px;
  background: var(--ink);
}
.nav-toggle span::before { top: -6px; }
.nav-toggle span::after { top: 6px; }

@media (max-width: 1000px) {
  .nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    background: var(--bone);
    border-bottom: 1px solid var(--line);
    padding: 10px var(--gutter) 20px;
    gap: 0;
  }
  .nav.open { display: flex; }
  .nav a {
    padding: 13px 4px;
    border-bottom: 1px solid var(--line-soft);
    font-size: 1rem;
  }
  .nav-toggle { display: inline-flex; }
  .header-phone { display: none; }
}

/* Below 720px the sticky bottom bar already carries "Get my price" and
   "Call", so the header CTA is a duplicate competing with the logo for a
   narrow row. Drop it and let the header be logo + menu. */
@media (max-width: 720px) {
  .header-actions .btn { display: none; }
  .site-header .wrap { min-height: 64px; gap: 12px; }
}

@media (max-width: 560px) {
  .brand-sub { display: none; }
}

/* Stacked buttons should be the same width, not ragged. */
@media (max-width: 560px) {
  .btn-row { gap: 10px; }
  .btn-row > .btn { flex: 1 1 100%; width: 100%; }
}

/* ---------- Hero ---------- */

.hero { padding-block: clamp(48px, 6.5vw, 92px) clamp(56px, 7vw, 96px); }

.hero-grid {
  display: grid;
  grid-template-columns: 1.08fr 0.92fr;
  gap: clamp(32px, 5vw, 68px);
  align-items: center;
}

.hero h1 { margin-bottom: 22px; }
.hero .lead { margin-bottom: 30px; }

.hero-points {
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  font-size: 0.88rem;
  color: var(--ink-2);
}
.hero-points li {
  display: flex;
  align-items: center;
  gap: 7px;
}
.hero-points li::before {
  content: "";
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E") no-repeat center / contain;
  border-radius: 50%;
  box-shadow: inset 0 0 0 20px var(--accent);
}

.hero-figure { position: relative; }
.hero-figure img {
  width: 100%;
  height: clamp(360px, 46vw, 560px);
  object-fit: cover;
  border-radius: 200px 200px var(--radius-lg) var(--radius-lg);
}

.price-chip {
  position: absolute;
  left: -22px;
  bottom: 34px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 16px 20px;
  box-shadow: 0 14px 34px rgba(28, 23, 51, 0.1);
  max-width: 224px;
}
.price-chip .k {
  font-size: 0.71rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 600;
  display: block;
  margin-bottom: 4px;
}
.price-chip .v {
  font-family: var(--serif);
  font-size: 1.85rem;
  color: var(--plum);
  line-height: 1.05;
  display: block;
}
.price-chip .n {
  font-size: 0.78rem;
  color: var(--ink-3);
  display: block;
  margin-top: 3px;
}

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-figure img { height: clamp(300px, 62vw, 420px); border-radius: 140px 140px var(--radius-lg) var(--radius-lg); }
  .price-chip { left: auto; right: 10px; bottom: -14px; }
}

/* On a phone the floating chip covers half the photo. Drop it below the
   image as its own full-width card instead. */
@media (max-width: 640px) {
  .price-chip {
    position: static;
    max-width: none;
    margin-top: 12px;
    box-shadow: none;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 10px;
    padding: 14px 16px;
  }
  .price-chip .k { margin: 0; }
  .price-chip .v { font-size: 1.55rem; }
  .price-chip .n { margin: 0; flex-basis: 100%; }
}

/* ---------- Stat rule ---------- */

.statrule {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.statrule div {
  padding: 26px 22px;
  border-left: 1px solid var(--line);
}
.statrule div:first-child { border-left: 0; }
.statrule .v {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 2.5vw, 2.05rem);
  color: var(--plum);
  display: block;
  line-height: 1.1;
}
.statrule .k {
  font-size: 0.79rem;
  color: var(--ink-3);
  display: block;
  margin-top: 5px;
  letter-spacing: 0.01em;
}
@media (max-width: 760px) {
  .statrule { grid-template-columns: repeat(2, 1fr); }
  .statrule div:nth-child(3) { border-left: 0; }
  .statrule div:nth-child(n + 3) { border-top: 1px solid var(--line); }
}

/* ---------- Section heading ---------- */

.sec-head {
  max-width: 720px;
  margin-bottom: clamp(34px, 4.5vw, 56px);
}
.sec-head.center { margin-inline: auto; text-align: center; }
.sec-head.center .lead { margin-inline: auto; }

.head-split {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 24px;
  align-items: end;
  margin-bottom: clamp(30px, 4vw, 48px);
}
.head-split .sec-head { margin-bottom: 0; }
@media (max-width: 700px) {
  .head-split { grid-template-columns: 1fr; align-items: start; }
}

/* ---------- Editorial numbered list ---------- */

.index-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}
.index-list > li { border-bottom: 1px solid var(--line); }

.index-list a,
.index-row {
  display: grid;
  grid-template-columns: 62px 1fr 1.15fr auto;
  gap: 22px;
  align-items: baseline;
  padding: 26px 4px;
  text-decoration: none;
  color: inherit;
  transition: background-color 0.18s ease, padding 0.18s ease;
}
.index-list a:hover {
  background: rgba(98, 71, 168, 0.045);
  padding-inline: 12px 4px;
}
.index-num {
  font-family: var(--serif);
  font-size: 0.92rem;
  color: var(--accent);
  letter-spacing: 0.02em;
}
.index-title {
  font-family: var(--serif);
  font-size: clamp(1.25rem, 2vw, 1.6rem);
  line-height: 1.15;
  color: var(--ink);
}
.index-desc {
  font-size: 0.94rem;
  color: var(--ink-2);
  line-height: 1.55;
}
.index-meta {
  font-size: 0.88rem;
  color: var(--plum);
  font-weight: 600;
  white-space: nowrap;
}
@media (max-width: 860px) {
  .index-list a,
  .index-row {
    grid-template-columns: 40px 1fr;
    gap: 6px 16px;
    padding-block: 22px;
  }
  .index-desc { grid-column: 2; }
  .index-meta { grid-column: 2; margin-top: 4px; }
}

/* ---------- Feature split ---------- */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}
.split.reverse .split-media { order: 2; }
/* Top-aligned variant for prose + sidebar card layouts, where centering
   leaves a dead pocket of whitespace beside the first heading. */
.split.top { align-items: start; }
/* Prose + sticky card. The text column carries far more height than the card,
   so an even split leaves a thin column of text beside a mostly empty rail.
   Weighted to keep line length under ~80 characters while shortening the page. */
.split.top { grid-template-columns: 1.35fr 1fr; }
.split.top .split-aside { position: sticky; top: 96px; }
@media (max-width: 880px) {
  /* .split.top out-specifies the plain `.split` single-column rule below, so
     the collapse has to be repeated here or these pages never stack on mobile. */
  .split.top { grid-template-columns: 1fr; }
  .split.top .split-aside { position: static; }
}
.split-media img {
  width: 100%;
  height: clamp(320px, 40vw, 480px);
  object-fit: cover;
  border-radius: var(--radius-lg);
}
@media (max-width: 880px) {
  .split { grid-template-columns: 1fr; }
  .split.reverse .split-media { order: 0; }
  .split-media img { height: clamp(260px, 55vw, 380px); }
}

/* ---------- Checklist columns ---------- */

.check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 2px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
.check-col {
  background: var(--paper);
  padding: 28px 26px 30px;
}
.check-col h3 {
  font-size: 1.12rem;
  margin-bottom: 4px;
}
.check-col .room-count {
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  display: block;
  margin-bottom: 16px;
}
.check-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.91rem;
  color: var(--ink-2);
}
.check-col li {
  padding: 7px 0 7px 21px;
  position: relative;
  line-height: 1.45;
  border-bottom: 1px solid var(--line-soft);
}
.check-col li:last-child { border-bottom: 0; }
.check-col li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 14px;
  width: 9px;
  height: 2px;
  background: var(--plum-2);
}

/* ---------- Calculator ---------- */

.calc {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  /* `clip`, not `hidden`. Both round off the plum panel's corners, but
     `hidden` makes this form a scroll container, and a scroll container is
     the scrollport its own `position: sticky` descendants resolve against.
     That silently disabled the sticky price panel: it tracked the column all
     the way down instead of pinning, so the running total scrolled off screen
     exactly when the form got long enough for it to matter. `clip` does the
     same clipping without creating the scrollport. */
  overflow: clip;
  display: grid;
  grid-template-columns: 1.35fr 0.85fr;
  align-items: stretch;
}
/* Safari below 16 has no `clip`. Give it back `hidden` so the corners still
   round — it loses the sticky panel, which is exactly what it had before. */
@supports not (overflow: clip) {
  .calc { overflow: hidden; }
}
@media (max-width: 940px) {
  .calc { grid-template-columns: 1fr; }
}

.calc-form { padding: clamp(26px, 3.4vw, 42px); }

/* The plum panel fills its column so the card reads as one block, while the
   content inside sticks — the running price stays on screen as you work down
   a long form instead of scrolling away. */
.calc-out {
  background: var(--plum);
  color: #fff;
}
.calc-out-inner {
  position: sticky;
  top: 88px;
  padding: clamp(26px, 3.4vw, 42px);
  display: flex;
  flex-direction: column;
  /* With every add-on ticked the summary reaches ~740px. Pinned 88px down
     that overruns a laptop viewport and takes the button with it, so cap the
     panel and let it scroll inside itself rather than off the screen. */
  max-height: calc(100vh - 112px);
  overflow-y: auto;
}
@media (max-width: 940px) {
  /* Price first, then the fields, then the button. Otherwise the summary
     lands below the button the customer has already pressed. */
  .calc { display: flex; flex-direction: column; }
  .calc-out { order: -1; }
  /* Nothing is pinned here, so the viewport cap would just clip the summary
     and bury the total behind an inner scrollbar. Let it run its full height. */
  .calc-out-inner {
    position: static;
    max-height: none;
    overflow-y: visible;
  }
}

/* ---------- Mobile: collapsed price summary ----------

   Expanded, this panel is 507px of a 812px phone screen, so the first control
   a customer can actually touch sits a full screen down the page. Collapsed it
   is a two-line receipt header that opens on demand, and the form starts
   roughly 390px sooner. Desktop is untouched. */

@media (max-width: 940px) {
  .calc-out.is-collapsed .calc-out-inner {
    /* relative only so the toggle can anchor to it. `top` must be cleared:
       it is still 88px from the sticky base rule, and on a relative box that
       is an offset, which shunts the whole summary out of its own panel. */
    position: relative;
    top: auto;
    padding: 15px 18px;
    gap: 1px;
  }
  /* The breakdown, the label and the small print collapse. The button never
     does: it is the only way out of the calculator, and hiding it behind a
     "Details" toggle means nobody finds it. */
  .calc-out.is-collapsed .out-label,
  .calc-out.is-collapsed .out-lines,
  .calc-out.is-collapsed .out-note { display: none; }
  /* padding-right clears the absolutely-positioned Details toggle (79px wide,
     14px from the edge). Without it the save badge slides straight underneath
     the word "Details". */
  .calc-out.is-collapsed .out-sub {
    order: 1;
    margin: 0;
    padding-right: 92px;
    font-size: 0.83rem;
  }
  .calc-out.is-collapsed .out-price { order: 2; font-size: 1.75rem; line-height: 1.15; }
  .calc-out.is-collapsed .btn { order: 3; margin-top: 13px; }
  .calc-out.is-collapsed .btn + .btn { margin-top: 9px; }
  /* The strikethrough IS breakdown detail. The saving is not: it is the reason
     to pick every-2-weeks over one-time, and hiding it meant only the people
     who opened Details ever saw it — the ones who needed convincing least. */
  .calc-out.is-collapsed .out-strike { display: none; }

  /* Anchored to the price row, not the panel's middle: once the button is in
     there, centring would float it over the button instead. */
  .calc-toggle {
    position: absolute;
    top: 17px;
    right: 14px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 10px;
    background: none;
    border: 0;
    color: rgba(255, 255, 255, 0.82);
    font: inherit;
    font-size: 0.82rem;
    cursor: pointer;
  }
  .calc-toggle svg { transition: transform 0.2s ease; }
  .calc-out:not(.is-collapsed) .calc-toggle {
    position: static;
    transform: none;
    align-self: flex-start;
    margin: 16px 0 0;
    padding-inline: 0;
  }
  .calc-out:not(.is-collapsed) .calc-toggle svg { transform: rotate(180deg); }
}
@media (min-width: 941px) {
  /* Desktop keeps the full panel. Nothing to collapse, nothing to toggle. */
  .calc-toggle { display: none; }
}


.field { margin-bottom: 24px; }
.field > label,
.field > .flabel {
  display: block;
  font-size: 0.79rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

.seg {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg label {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 9px 15px;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  color: var(--ink-2);
  background: var(--paper);
  transition: all 0.15s ease;
  user-select: none;
}
.seg label:hover { border-color: var(--plum-2); color: var(--ink); }
.seg input:checked + label {
  background: var(--plum);
  border-color: var(--plum);
  color: #fff;
}
.seg input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

.calc select,
.calc input[type="text"],
.calc input[type="email"],
.calc input[type="tel"],
.calc input[type="date"],
.calc input[type="number"],
.calc textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.97rem;
  color: var(--ink);
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

/* Date inputs keep their intrinsic UA sizing unless appearance is cleared —
   width:100% alone leaves them at ~131px, so "Preferred date" on /book was
   rendering as a squat monospace box a third the width of every field around
   it, with a 21px tap target. iOS additionally centres the value and shrinks
   the field to its content, hence the min-height and the value alignment. */
.calc input[type="date"] {
  -webkit-appearance: none;
  appearance: none;
  /* The value box is intrinsically 21px against 19px for a text input, so the
     shared 13px padding would leave the date field 2px taller than the select
     sitting right beside it. 12px lands both on exactly 47px. */
  padding-block: 12px;
  min-height: 47px;
  text-align: left;
}
.calc input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}
.calc input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0.55;
  cursor: pointer;
}
.calc input[type="date"]:focus::-webkit-calendar-picker-indicator { opacity: 1; }

/* iOS Safari zooms the whole page in whenever a focused field's font-size is
   below 16px, and 0.97rem is 15.52px. On the booking form that meant every
   tap re-scaled the page, and it never scaled back — except on the Stripe
   card field, which already sets 16px for this exact reason (js/book.js).
   Match it so the form does not zoom on some fields and not others. */
@media (max-width: 720px) {
  .calc select,
  .calc input[type="text"],
  .calc input[type="email"],
  .calc input[type="tel"],
  .calc input[type="date"],
  .calc input[type="number"],
  .calc textarea {
    font-size: 16px;
  }
}
.calc select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2356506e' stroke-width='1.6' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 38px;
}

.addons {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  gap: 8px;
}
.addon {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 13px;
  font-size: 0.89rem;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.addon:hover { border-color: var(--plum-2); }
.addon input { accent-color: var(--plum); width: 16px; height: 16px; flex-shrink: 0; }
.addon .a-price { margin-left: auto; color: var(--ink-3); font-size: 0.84rem; white-space: nowrap; }
.addon:has(input:checked) { border-color: var(--plum); background: var(--plum-wash); }

.calc-out .out-label {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
  font-weight: 600;
  margin-bottom: 8px;
}
.calc-out .out-price {
  font-family: var(--serif);
  font-size: clamp(3rem, 6vw, 4rem);
  line-height: 1;
  color: #fff;
  margin-bottom: 4px;
}
.calc-out .out-price .cur { font-size: 0.44em; vertical-align: 0.55em; margin-right: 2px; }
.calc-out .out-sub {
  font-size: 0.87rem;
  color: rgba(255, 255, 255, 0.68);
  margin-bottom: 20px;
}
.calc-out .out-strike {
  text-decoration: line-through;
  color: rgba(255, 255, 255, 0.45);
  margin-right: 8px;
}
.calc-out .out-save {
  display: inline-block;
  /* Light periwinkle on the deep panel — the accent purple would sit too
     close to the background to register as a badge. */
  background: #b9a9e6;
  color: var(--plum);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: 3px;
}

.out-lines {
  list-style: none;
  margin: 20px 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--line-invert);
  font-size: 0.89rem;
}
.out-lines li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 5px 0;
  color: rgba(255, 255, 255, 0.76);
}
.out-lines li span:last-child { color: #fff; white-space: nowrap; }

/* Add-ons are itemised one per line. Indent and lighten them so they read as
   items under the base clean rather than as separate charges of equal weight,
   and so a home with six add-ons still scans as one block. */
.out-lines li.addon-line {
  padding-left: 14px;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.62);
}
.out-lines li.addon-line span:last-child { color: rgba(255, 255, 255, 0.86); }
.out-lines li.addon-line + li:not(.addon-line) { margin-top: 6px; }
.out-lines li.total {
  border-top: 1px solid var(--line-invert);
  margin-top: 10px;
  padding-top: 12px;
  font-weight: 600;
  color: #fff;
}

.calc-out .btn { width: 100%; }
.calc-out .out-note {
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.55);
  margin: 14px 0 0;
  line-height: 1.5;
}

/* ---------- Quote form ---------- */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }

.form-note {
  font-size: 0.82rem;
  color: var(--ink-3);
  margin-top: 14px;
}

.form-status {
  margin-top: 16px;
  padding: 14px 16px;
  border-radius: var(--radius);
  font-size: 0.92rem;
  display: none;
}
.form-status.show { display: block; }
.form-status.ok { background: var(--plum-wash); color: var(--plum); }
.form-status.err { background: var(--accent-wash); color: var(--accent-dark); }

/* ---------- Quote / testimonial ---------- */

.pullquote {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 3.1vw, 2.25rem);
  line-height: 1.28;
  color: var(--ink);
  margin: 0 0 24px;
  text-wrap: balance;
}
.bg-plum .pullquote { color: #fff; }

.attrib {
  font-size: 0.87rem;
  color: var(--ink-3);
  letter-spacing: 0.02em;
}
.bg-plum .attrib { color: rgba(255, 255, 255, 0.6); }

.review-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border-block: 1px solid var(--line-soft);
}
.review {
  background: var(--bone);
  padding: 30px 28px;
}
.review .stars {
  color: var(--accent);
  font-size: 0.9rem;
  letter-spacing: 0.14em;
  margin-bottom: 12px;
}
.review p {
  font-size: 0.96rem;
  color: var(--ink-2);
  line-height: 1.6;
}
.review .attrib { margin-top: 14px; display: block; }

/* ---------- Areas index ---------- */

.area-index {
  columns: 4;
  column-gap: 32px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.area-index li {
  break-inside: avoid;
  border-bottom: 1px solid var(--line-soft);
}
.area-index a {
  display: block;
  padding: 10px 0;
  text-decoration: none;
  font-size: 0.94rem;
  color: var(--ink-2);
}
.area-index a:hover { color: var(--accent); }
@media (max-width: 900px) { .area-index { columns: 2; } }
@media (max-width: 460px) { .area-index { columns: 1; } }

/* ---------- Steps ---------- */

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
}
.step {
  background: var(--paper);
  padding: 32px 28px 34px;
}
.step .n {
  font-family: var(--serif);
  font-size: 2.4rem;
  color: var(--plum-wash);
  line-height: 1;
  display: block;
  margin-bottom: 14px;
}
.step h3 { font-size: 1.16rem; margin-bottom: 8px; }
.step p { font-size: 0.93rem; color: var(--ink-2); margin: 0; }
@media (max-width: 780px) { .steps { grid-template-columns: 1fr; } }

/* ---------- Pricing table ---------- */

.ptable {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
/* Table caption lives outside the table. Inside, it inherits the table's
   wider-than-screen width on mobile and gets clipped by the scroll box. */
.table-note {
  font-size: 0.79rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 600;
  margin: 0 0 12px;
}
.ptable th,
.ptable td {
  text-align: left;
  padding: 15px 16px;
  border-bottom: 1px solid var(--line-soft);
}
.ptable thead th {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  border-bottom: 1px solid var(--line);
}
.ptable tbody th { font-family: var(--sans); font-weight: 600; font-size: 0.95rem; }
.ptable td.price { font-family: var(--serif); font-size: 1.2rem; color: var(--plum); white-space: nowrap; }
.ptable tbody tr:hover { background: rgba(98, 71, 168, 0.04); }

.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* On a phone the price table is wider than the screen. It scrolled fine but
   gave no sign it could, so the extra columns were effectively invisible.
   Pin the row label and say so. */
@media (max-width: 720px) {
  .table-scroll { position: relative; }
  .table-scroll::after {
    content: "Swipe the table for more columns →";
    display: block;
    font-size: 0.76rem;
    color: var(--ink-3);
    padding: 10px 2px 0;
  }
  .ptable tbody th,
  .ptable thead th:first-child {
    position: sticky;
    left: 0;
    background: var(--bone);
    z-index: 1;
  }
  .ptable tbody tr:hover th { background: var(--bone); }
}

/* ---------- FAQ ---------- */

.faq { border-top: 1px solid var(--line); }
.faq details {
  border-bottom: 1px solid var(--line);
}
.faq summary {
  cursor: pointer;
  list-style: none;
  padding: 24px 46px 24px 0;
  position: relative;
  font-family: var(--serif);
  font-size: clamp(1.08rem, 1.7vw, 1.28rem);
  line-height: 1.3;
  color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 50%;
  width: 13px;
  height: 13px;
  margin-top: -6px;
  background: var(--plum);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 5v14M5 12h14' stroke='%23000' stroke-width='2.4'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 5v14M5 12h14' stroke='%23000' stroke-width='2.4'/%3E%3C/svg%3E") no-repeat center / contain;
  transition: transform 0.2s ease;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq .faq-body {
  padding: 0 60px 26px 0;
  color: var(--ink-2);
  font-size: 0.98rem;
  max-width: 70ch;
}
.faq .faq-body p:last-child { margin-bottom: 0; }

/* ---------- CTA band ---------- */

.cta-band {
  position: relative;
  overflow: hidden;
  background: var(--plum);
  color: #fff;
}
.cta-band img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.26;
}
/* Scrim so the headline and buttons keep contrast over the photo. */
.cta-band::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(30, 24, 62, 0.4), rgba(30, 24, 62, 0.66));
}
.cta-band .wrap { position: relative; z-index: 1; }
.cta-band h2 { color: #fff; }
.cta-band .lead { color: rgba(255, 255, 255, 0.82); }

/* ---------- Prose ---------- */

.prose { max-width: 72ch; }
.prose h2 { margin-top: 1.8em; }
.prose h3 { margin-top: 1.6em; font-size: 1.24rem; }
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose ul, .prose ol { padding-left: 1.15em; margin-bottom: 1.2em; color: var(--ink-2); }
.prose li { margin-bottom: 0.5em; }
.prose a { color: var(--plum); }

/* ---------- Breadcrumbs ---------- */

.crumbs {
  font-size: 0.83rem;
  color: var(--ink-3);
  padding-block: 18px 0;
}
.crumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
}
.crumbs li::after { content: "/"; margin-left: 8px; color: var(--line); }
.crumbs li:last-child::after { content: ""; }
.crumbs a { text-decoration: none; color: var(--ink-3); }
.crumbs a:hover { color: var(--accent); }

/* ---------- Footer ---------- */

.site-footer {
  background: var(--plum);
  color: rgba(255, 255, 255, 0.72);
  padding-block: clamp(52px, 6vw, 78px) 32px;
  font-size: 0.91rem;
}
.footer-top {
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: 40px 32px;
  padding-bottom: 44px;
  border-bottom: 1px solid var(--line-invert);
}
.site-footer h4 {
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: 16px;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 9px; }
.site-footer a { text-decoration: none; color: rgba(255, 255, 255, 0.72); }
.site-footer a:hover { color: #fff; }
.footer-brand .brand { display: inline-flex; }
.footer-brand p { margin: 16px 0 0; max-width: 34ch; color: rgba(255, 255, 255, 0.6); font-size: 0.9rem; }
.footer-contact {
  margin-top: 18px;
  font-size: 0.95rem;
  color: #fff;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.footer-contact a { color: #fff; font-weight: 500; }
.footer-bottom {
  padding-top: 26px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  justify-content: space-between;
  font-size: 0.83rem;
  color: rgba(255, 255, 255, 0.5);
}
@media (max-width: 860px) {
  .footer-top { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
  .footer-top { grid-template-columns: 1fr; gap: 32px; }
}

/* ---------- Sticky mobile CTA ---------- */

/* Parked off-screen by default. site.js slides it up only once the hero CTA
   has scrolled away, and drops it again whenever the calculator or enquiry
   form is on screen — sitting there permanently made it a duplicate of the
   button directly above it, then a lid over the thing it linked to. */
.mobile-cta {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  background: var(--paper);
  border-top: 1px solid var(--line);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  gap: 10px;
  visibility: hidden;
  transform: translateY(115%);
  transition: transform 0.24s ease, visibility 0.24s;
}
.mobile-cta.is-visible {
  visibility: visible;
  transform: none;
}
.mobile-cta .btn { flex: 1; padding: 13px 12px; font-size: 0.92rem; }
@media (max-width: 720px) {
  .mobile-cta { display: flex; }
  body { padding-bottom: 74px; }
  /* /book, /booking-confirmed and the admin calendar ship no bar at all, so
     they must not reserve the room for one. */
  body.no-mobile-cta { padding-bottom: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .mobile-cta { transition: none; }
}

/* ---------- Utilities ---------- */

.hr { border: 0; border-top: 1px solid var(--line); margin: 0; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.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;
}

/* ---------- Guides ---------- */

/* Long-form prose gets a narrower measure than the marketing pages. Anything
   past roughly 70 characters a line costs comprehension, and these pages are
   meant to actually be read rather than skimmed. */
.guide-body { max-width: 68ch; }
.guide-body h2 {
  margin: 2.4rem 0 0.9rem;
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  line-height: 1.25;
}
.guide-body h2:first-child { margin-top: 0; }
.guide-body p { margin: 0 0 1.05rem; }
.guide-body > ol,
.guide-body > ul { margin: 0 0 1.5rem; }
.guide-body .table-scroll { margin: 1.4rem 0 0.4rem; }
.guide-body .table-note { margin-bottom: 1.8rem; }

/* The title block and the prose now share one section, so the prose needs
   its own top spacing rather than inheriting a section break. */
.guide-body { margin-top: clamp(28px, 4vw, 48px); }

.guide-meta {
  margin-top: 1rem;
  font-size: 0.85rem;
  color: var(--ink-3);
  letter-spacing: 0.01em;
}

/* Article images.

   Deliberately NOT `.hero-figure`. That one is the service-page hero: a tall
   arched crop (200px top radius) built to sit beside a column of text. In an
   article it would read as a decorative header rather than a picture in the
   flow of the piece. */
.guide-figure {
  margin: 2rem 0 2.2rem;
}
.guide-figure img {
  display: block;
  width: 100%;
  height: clamp(220px, 34vw, 380px);
  object-fit: cover;
  border-radius: var(--radius-lg);
}
.guide-figure figcaption {
  margin-top: 0.7rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--ink-3);
  padding-left: 0.9rem;
  border-left: 2px solid var(--line-soft);
}
/* Inline SVG diagrams. Unlike .guide-figure these must NOT be cropped: a
   diagram with its edges cut off is worse than no diagram, so there is no
   fixed height and no object-fit here. The viewBox sets the aspect ratio and
   the box simply takes the width it is given. */
.diagram-figure {
  margin: 2rem 0 2.2rem;
  /* A diagram squeezed into a phone-width column renders its labels at about
     7px, which is unreadable. Below the min-width on the svg the box scrolls
     sideways inside itself rather than shrinking the type into nothing.
     `min-width: 0` is load-bearing: without it this figure's min-content width
     is the svg's 500px, that propagates up through the grid item, and the
     whole PAGE scrolls sideways on a phone instead of just this figure. */
  min-width: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Same reason: a grid item defaults to `min-width: auto` and refuses to shrink
   below its content, so the text column has to be told it may. */
.split.top .prose { min-width: 0; }
.diagram-figure svg {
  display: block;
  width: 100%;
  min-width: 500px;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--paper);
}
.diagram-figure figcaption {
  margin-top: 0.7rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--ink-3);
  padding-left: 0.9rem;
  border-left: 2px solid var(--line-soft);
}

/* The one above the fold sits tighter to the title and stands a little taller,
   since it is carrying the top of the page. */
.guide-figure-lead {
  margin-top: 0;
  margin-bottom: 2.4rem;
}
.guide-figure-lead img { height: clamp(240px, 38vw, 440px); }

@media (max-width: 640px) {
  .guide-figure img,
  .guide-figure-lead img { height: clamp(200px, 56vw, 280px); }
}

/* Guide lists.

   Deliberately NOT `.steps` or `.ticks`. `.steps` is the homepage three-column
   grid and `.ticks` never existed, so both broke the guide pages. These are
   single-column reading lists, sized to the prose around them. */
.guide-steps,
.guide-ticks {
  margin: 0 0 1.5rem;
  padding: 0;
  list-style: none;
  counter-reset: guidestep;
}
.guide-steps > li,
.guide-ticks > li {
  position: relative;
  padding-left: 2.1rem;
  margin-bottom: 0.85rem;
  line-height: 1.62;
}
.guide-steps > li:last-child,
.guide-ticks > li:last-child { margin-bottom: 0; }

.guide-steps > li { counter-increment: guidestep; }
.guide-steps > li::before {
  /* With the number alone this read as "1 Bathrooms." rather than a list. */
  content: counter(guidestep) ".";
  position: absolute;
  left: 0;
  top: 0.1em;
  width: 1.5rem;
  font-family: var(--serif);
  font-size: 1.02rem;
  color: var(--accent);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.guide-ticks > li::before {
  content: "";
  position: absolute;
  left: 0.45rem;
  top: 0.72em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.guide-steps > li strong { font-weight: 650; }

/* Included / not-included columns on the deep-vs-standard comparison. Colour
   alone would not survive a colourblind reader or a greyscale print, so the
   cells carry the words Yes and No and the glyph is decorative. */
.compare-table td.yes,
.compare-table td.no {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.9rem;
  white-space: nowrap;
}
.compare-table td.yes { color: var(--accent); }
.compare-table td.no { color: var(--ink-3); }
.compare-table td.yes::before { content: "✓ "; }
.compare-table td.no::before { content: "· "; }

/* ---------- Inline card entry on /book ---------- */

/* Stripe's Payment Element renders inside its own iframe here. We style the
   container; the fields themselves are themed through the `appearance` object
   in js/book.js, because CSS cannot cross into Stripe's frame. */
/* The Card Element renders one compact row. It needs a real bordered box
   around it, because the iframe itself draws no chrome. */
.card-element {
  margin-top: 14px;
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.card-element.StripeElement--focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(98, 71, 168, 0.14);
}
.card-element.StripeElement--invalid { border-color: #b3261e; }

/* The submit button sits at the END of the form, under the card field, where
   a form's action belongs. It used to live in the sticky price panel on the
   right, which read as unrelated to the fields being filled in. */
.calc-submit {
  margin-top: 26px;
  padding-top: 24px;
  border-top: 1px solid var(--line-soft);
}
.calc-submit .btn {
  width: 100%;
  padding-block: 15px;
  font-size: 1rem;
}
.calc-submit .form-status { margin-top: 12px; }

.pay-lede {
  margin: 6px 0 0;
  font-size: 0.95rem;
  color: var(--ink-2);
  line-height: 1.55;
}

.pay-trust {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 14px 0 0;
  font-size: 0.85rem;
  color: var(--ink-3);
}
.pay-trust .lock { font-size: 0.95rem; }

/* ---------- Booking date picker ----------

   Replaces the native <input type="date"> on /book. A native date field can
   only grey out a contiguous range via min/max, so individual days off looked
   bookable until the moment they were refused. Every class here is prefixed dp-
   and none existed before, so nothing else in this stylesheet can reach in. */

.dp { position: relative; }

.dp-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--paper); color: var(--ink);
  font: inherit; font-size: 16px; /* under 16px and iOS zooms on focus */
  text-align: left; cursor: pointer;
}
.dp-trigger:hover { border-color: var(--accent); }
.dp-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dp-icon { flex: none; font-size: 0.95rem; opacity: .75; }

.dp-panel {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  width: min(320px, calc(100vw - 32px));
  padding: 14px;
  background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: 0 18px 40px rgba(28, 23, 51, 0.18);
}

.dp-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px;
}
.dp-month { font-weight: 600; font-size: 0.95rem; color: var(--ink); }
.dp-nav {
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--paper); color: var(--ink);
  width: 30px; height: 30px; line-height: 1; cursor: pointer;
}
.dp-nav:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.dp-nav:disabled { opacity: .3; cursor: default; }

.dp-dow, .dp-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.dp-dow span {
  text-align: center; font-size: 0.7rem; color: var(--ink-3);
  padding-bottom: 4px;
}

.dp-day {
  aspect-ratio: 1; min-height: 38px;
  border: 0; border-radius: 6px;
  background: transparent; color: var(--ink);
  font: inherit; font-size: 0.9rem; cursor: pointer;
}
.dp-day:hover:not(:disabled) { background: var(--accent-wash); }
.dp-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.dp-day.on { background: var(--accent); color: #fff; font-weight: 600; }

/* Unavailable. Struck through as well as dimmed: dimming alone reads as a
   rendering quirk, and a line through the number says "not this day". */
.dp-day:disabled { color: var(--ink-3); opacity: .45; cursor: not-allowed; }
.dp-day.off:disabled { text-decoration: line-through; }

.dp-note {
  margin: 10px 0 0; font-size: 0.78rem; line-height: 1.45; color: var(--ink-3);
}

@media (max-width: 480px) {
  /* Full width and in the flow, so the panel cannot hang off the edge of a
     phone or cover the fields under it. */
  .dp-panel { position: static; width: 100%; box-shadow: none; margin-top: 6px; }
}

/* Inline "that date is closed" note under the date field. Sits tight to the
   input so it reads as being about that field and not the form as a whole.

   NOT .field-note: that name is already taken further down for the privacy note
   above the details fields, which carries a left border and a different colour.
   Reusing it meant this rule was silently shadowed. */
.date-closed-note {
  margin: 6px 0 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--accent-dark);
}

.card-fallback {
  margin: 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bone-deep);
  font-size: 0.92rem;
  color: var(--ink-2);
}

/* ---------- Address autocomplete on /book ---------- */

.addr-field { position: relative; }

.addr-results {
  position: absolute;
  z-index: 30;
  left: 0;
  right: 0;
  margin: 4px 0 0;
  padding: 4px;
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 12px 28px rgba(28, 23, 51, 0.14);
  max-height: 280px;
  overflow-y: auto;
}
.addr-results[hidden] { display: none; }

.addr-results li {
  padding: 10px 12px;
  border-radius: var(--radius);
  cursor: pointer;
  line-height: 1.35;
}
/* Hover and keyboard selection look identical on purpose, so arrowing down
   and mousing over do not fight each other visually. */
.addr-results li:hover,
.addr-results li.active { background: rgba(98, 71, 168, 0.09); }

.addr-line { display: block; font-size: 0.95rem; color: var(--ink); }
.addr-rest { display: block; font-size: 0.82rem; color: var(--ink-3); margin-top: 1px; }

.field-hint {
  margin: 6px 0 0;
  font-size: 0.82rem;
  color: var(--ink-3);
}

/* ---------- Booking confirmed page ---------- */

/* The "your email is coming" note is the single most reassuring thing on this
   page, so it gets its own tinted panel rather than being another paragraph
   people skim past. */
.confirm-mail {
  max-width: 560px;
  margin: 22px auto 0;
  padding: 16px 20px;
  background: #f7f6fb;
  border-radius: var(--radius-lg);
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-2);
}
.confirm-mail strong { color: var(--ink); }

/* The card-hold notice. Deliberately louder than .confirm-mail: an unexpected
   pending amount on a statement is the single most likely reason someone calls
   confused, so the one place they are guaranteed to look gets an accent edge. */
.confirm-hold {
  max-width: 560px;
  margin: 18px auto 0;
  padding: 16px 20px;
  background: var(--accent-wash);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-lg);
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-2);
  text-align: left;
}
.confirm-hold strong { color: var(--ink); }

.confirm-steps { margin-top: 30px; }
.confirm-steps .eyebrow { display: block; margin-bottom: 14px; }
.confirm-steps .guide-steps > li { margin-bottom: 1rem; }

/* ---------- Internal booking calendar ---------- */

.cal-gate { max-width: 420px; margin: 40px auto; text-align: center; }
.cal-gate-form { display: flex; gap: 10px; margin-top: 20px; }
.cal-gate-form input {
  flex: 1; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 16px; /* under 16px iOS zooms on focus */
}

.cal-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 22px;
}
.cal-head h1 { margin: 4px 0 0; font-size: clamp(1.6rem, 3vw, 2.2rem); }
.cal-nav { display: flex; gap: 8px; }
.cal-nav .btn { padding: 9px 16px; }

.cal-stats { display: flex; gap: 12px; margin-bottom: 22px; flex-wrap: wrap; }
.cal-stat {
  background: var(--bone-deep); border-radius: var(--radius-lg);
  padding: 14px 20px; min-width: 150px;
}
.cal-stat .n { display: block; font-family: var(--serif); font-size: 1.7rem; color: var(--plum); line-height: 1.1; }
.cal-stat .l { display: block; font-size: 0.82rem; color: var(--ink-3); margin-top: 3px; }

/* minmax(0,1fr) not 1fr: a grid column's default min-width is auto, so one
   long customer name would stretch its column and squash the rest of the week. */
.cal-grid-head, .cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px;
}
.cal-grid-head {
  margin-bottom: 6px;
  font-size: 0.74rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
}
.cal-grid-head span { padding: 0 6px; }

.cal-grid { background: var(--line-soft); border: 1px solid var(--line-soft); border-radius: var(--radius-lg); overflow: hidden; }
.cal-day {
  background: var(--paper); min-height: 104px; padding: 6px; min-width: 0;
  display: flex; flex-direction: column; gap: 4px;
}
.cal-day.out { background: #fbfbfd; }
.cal-day.out .cal-date { color: var(--ink-3); opacity: .5; }
.cal-day.today { box-shadow: inset 0 0 0 2px var(--accent); }

/* A day you are not working. Diagonal hatching rather than a flat tint, because
   a flat tint is what "not this month" already uses and the two would read as
   the same thing. Sits under the date and any booking, so a day closed AFTER
   something was booked still shows both. */
.cal-day.closed {
  background-color: #f6f4fb;
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(98, 71, 168, 0.10) 0 6px,
    transparent 6px 12px
  );
}
.cal-off {
  align-self: flex-start;
  font-size: 0.66rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent-dark);
  background: var(--paper);
  border-radius: 3px;
  padding: 1px 5px;
}
.cal-date { font-size: 0.78rem; color: var(--ink-3); }
.cal-day.has .cal-date { color: var(--ink); font-weight: 600; }

.cal-chip {
  display: block; width: 100%; max-width: 100%; min-width: 0; text-align: left; cursor: grab;
  border: 0; border-radius: 5px; padding: 4px 6px;
  background: rgba(98, 71, 168, 0.12); color: var(--ink);
  font: inherit; font-size: 0.74rem; line-height: 1.25;
  /* Without this a drag off a chip on a phone scrolls the page instead of
     picking the booking up. Everywhere that is not a chip still scrolls. */
  touch-action: none;
}
.cal-chip:hover { background: rgba(98, 71, 168, 0.2); }
/* Finished jobs read as background, not as things still to do. */
.cal-chip.done { background: var(--bone-deep); color: var(--ink-3); cursor: default; }
.cal-chip-time { display: block; font-size: 0.68rem; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-chip-name { display: block; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cal-upcoming { margin-top: 44px; }
.cal-upcoming h2 { margin-bottom: 16px; }
.cal-empty { color: var(--ink-3); }

.cal-card {
  display: flex; gap: 18px; padding: 18px 0;
  border-top: 1px solid var(--line-soft);
}
.cal-card.flash { background: rgba(98, 71, 168, 0.07); }
.cal-card-when { text-align: center; min-width: 52px; }
.cal-card-when .d { display: block; font-family: var(--serif); font-size: 1.7rem; color: var(--plum); line-height: 1; }
.cal-card-when .mo { display: block; font-size: 0.74rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.cal-card-body { flex: 1; min-width: 0; }
.cal-card-body h3 { margin: 0 0 4px; font-size: 1.05rem; display: flex; justify-content: space-between; gap: 14px; }
.cal-card-body h3 span { font-family: var(--serif); color: var(--plum); white-space: nowrap; }
.cal-meta { margin: 0 0 6px; font-size: 0.88rem; color: var(--ink-2); }
.cal-addr { margin: 0 0 6px; font-size: 0.9rem; }
.cal-addons { margin: 0 0 6px; font-size: 0.85rem; color: var(--ink-2); }
/* Access notes are the thing you actually need on the doorstep. */
/* Where a booking came from, on the rota row. Quiet by default; an AI-assistant
   booking gets the accent treatment because that is the channel worth noticing.
   New name, checked against the stylesheet first: .cal-addons and .cal-notes
   already exist and mean other things. */
.cal-source {
  margin: 6px 0 0;
  font-size: 0.8rem;
  color: var(--ink-3);
}
.cal-source.ai {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--accent-dark);
  font-weight: 600;
}

.cal-notes {
  margin: 0 0 8px; font-size: 0.85rem; color: var(--ink-2);
  padding-left: 10px; border-left: 2px solid var(--accent);
}
/* Reason picker that replaces "Mark as dealt with" once clicked. */
.cal-close-ask { display: inline-flex; align-items: center; gap: 8px; }
.cal-close-ask select {
  padding: 4px 6px; font: inherit; font-size: 0.82rem;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--paper); color: var(--ink);
}

.cal-contact { margin: 0; font-size: 0.85rem; display: flex; gap: 14px; flex-wrap: wrap; }

@media (max-width: 720px) {
  .cal-day { min-height: 78px; padding: 4px; }
  .cal-chip-time { display: none; }
  /* Taller than the text needs: on a phone a chip is a drag handle, and a
     14px-high target is not one. */
  .cal-chip { font-size: 0.68rem; padding: 6px 5px; min-height: 28px; }
  .cal-grid-head { font-size: 0.66rem; }
}

/* ---------- Calendar: recurrence, dragging, editing ---------- */

/* Projected repeats. Faded and outlined rather than filled, so at a glance
   you can tell a real booking from one the schedule implies. */
.cal-chip.ghost {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(98, 71, 168, 0.4);
  color: var(--ink-2);
}
.cal-chip.ghost:hover { background: rgba(98, 71, 168, 0.1); }
.cal-chip.lifted { opacity: 0.35; }

.cal-drag-ghost {
  position: fixed; z-index: 90; pointer-events: none;
  box-shadow: 0 8px 20px rgba(28, 23, 51, 0.25);
  background: var(--plum); color: #fff; opacity: 0.95;
  cursor: grabbing;
}
.cal-drag-ghost .cal-chip-time { color: rgba(255, 255, 255, 0.75); }

.cal-day.drop { background: rgba(98, 71, 168, 0.14); box-shadow: inset 0 0 0 2px var(--plum); }
body.cal-dragging { cursor: grabbing; user-select: none; -webkit-user-select: none; }

.cal-tag {
  display: inline-block; font-size: 0.7rem; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-3);
  border: 1px solid var(--line-soft); border-radius: 20px; padding: 1px 8px;
}

.cal-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--plum); text-decoration: underline;
}

.cal-help { margin: 0 0 18px; font-size: 0.86rem; color: var(--ink-3); }

/* ---------- Calendar modal ---------- */

.cal-modal { position: fixed; inset: 0; z-index: 100; display: flex; }
.cal-modal[hidden] { display: none; }
.cal-modal-back { position: absolute; inset: 0; background: rgba(28, 23, 51, 0.45); }
.cal-modal-box {
  position: relative; margin: auto; width: min(560px, calc(100% - 24px));
  max-height: calc(100vh - 40px); overflow-y: auto;
  background: var(--paper); border-radius: var(--radius-lg);
  padding: 28px; box-shadow: 0 24px 60px rgba(28, 23, 51, 0.3);
}
body.cal-modal-open { overflow: hidden; }
.cal-modal-box h2 { margin: 0 0 8px; font-size: 1.3rem; }
.cal-lead { margin: 0 0 16px; color: var(--ink-2); font-size: 0.95rem; }
.cal-lead a { margin-right: 12px; }
.cal-edit-head { margin-bottom: 4px; }

.cal-warn, .cal-note {
  margin: 0 0 16px; padding: 11px 14px; border-radius: 8px;
  font-size: 0.86rem; line-height: 1.55;
  background: var(--bone-deep); color: var(--ink-2);
}
/* Reserved for the one that changes more than the VA asked it to. */
.cal-warn.strong { background: var(--accent-wash); color: var(--accent-dark); }

.cal-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
/* Closed-date list in the Availability panel. Chips rather than rows: the list
   is short, each entry is only a date plus a way to undo it, and rows would
   make ten holidays look like a table that needs reading. */
.cal-chips {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.cal-chips li {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 6px 5px 12px;
  background: var(--accent-wash);
  border-radius: 999px;
  font-size: 0.88rem;
  color: var(--ink);
}
.cal-chip-x {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 4px;
  color: var(--accent-dark);
  border-radius: 999px;
}
.cal-chip-x:hover { background: rgba(28, 23, 51, 0.08); }

/* Add-on on/off list in the Availability panel. Two columns so eight add-ons
   fit without the panel growing tall enough to push Save out of view, which is
   a mistake this panel has already made once. */
.cal-toggles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 14px;
  margin: 0 0 16px;
}
.cal-toggle {
  display: flex; align-items: center; gap: 7px;
  padding: 5px 0;
  font-size: 0.88rem; color: var(--ink);
  cursor: pointer;
}
.cal-toggle input { flex: none; margin: 0; }
.cal-toggle span:first-of-type { flex: 1; min-width: 0; }
.cal-toggle-price { flex: none; font-size: 0.8rem; color: var(--ink-3); }
/* Unticked reads as "not offered", so grey the whole row to match. */
.cal-toggle:has(input:not(:checked)) { color: var(--ink-3); }
.cal-toggle:has(input:not(:checked)) span:first-of-type { text-decoration: line-through; }

@media (max-width: 520px) {
  .cal-toggles { grid-template-columns: 1fr; }
}

/* Date field plus its "Close it" button on one line.

   Scoped as `.cal-field > .cal-inline` rather than `.cal-inline`, because
   `.cal-field > span` (one class + one element) out-specifies a bare class and
   would otherwise force this wrapper back to display:block and give it the
   small grey type meant for field LABELS. The margin and font are reset for the
   same reason: this span is a row of controls, not a caption. */
.cal-field > .cal-inline {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin-bottom: 0;
  font-size: 1rem;
  color: var(--ink);
}
.cal-field > .cal-inline input { flex: 1; width: auto; }
.cal-field > .cal-inline .btn { white-space: nowrap; }

.cal-field { display: block; margin-bottom: 14px; }
.cal-grid2 .cal-field { margin-bottom: 0; }
.cal-field > span {
  display: block; margin-bottom: 5px;
  font-size: 0.78rem; color: var(--ink-3); letter-spacing: .02em;
}
.cal-field input, .cal-field select, .cal-field textarea {
  width: 100%; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 16px; /* under 16px and iOS zooms on focus */
  background: var(--paper); color: var(--ink);
}
.cal-field textarea { resize: vertical; }

.cal-addons {
  border: 1px solid var(--line-soft); border-radius: 10px;
  padding: 12px 14px 4px; margin: 0 0 16px;
}
.cal-addons legend { padding: 0 6px; font-size: 0.78rem; color: var(--ink-3); }
.cal-addons label {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 0; font-size: 0.9rem;
}
.cal-addons .p { margin-left: auto; color: var(--ink-3); font-size: 0.85rem; }

.cal-price {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  background: var(--bone-deep); border-radius: 10px;
  padding: 14px 18px; margin-bottom: 16px;
}
.cal-price .was { font-family: var(--serif); font-size: 1.2rem; color: var(--ink-3); text-decoration: line-through; }
.cal-price .now { font-family: var(--serif); font-size: 1.7rem; color: var(--plum); line-height: 1; }
.cal-price .l { font-size: 0.82rem; color: var(--ink-3); }

.cal-notify {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 0.92rem; margin-bottom: 12px; line-height: 1.45;
}
.cal-notify input { margin-top: 3px; }

.cal-status { min-height: 20px; font-size: 0.88rem; color: var(--ink-3); margin-bottom: 8px; }
.cal-status.err { color: var(--accent-dark); }

/* Stuck to the bottom of the scrolling modal, so Save is reachable no matter how
   tall the contents get. Learned twice on the Availability panel: adding a couple
   of explanatory lines, and later eight add-on toggles, each pushed Save and the
   error line out of sight, which made the panel look broken rather than tall.

   The negative bottom margin cancels .cal-modal-box's 28px bottom padding so the
   bar sits flush against the bottom edge, and the padding puts that space back
   INSIDE the bar. Inert on short modals: sticky only engages once there is
   something to scroll. */
.cal-actions {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  position: sticky; bottom: -28px; z-index: 2;
  margin-bottom: -28px; padding: 12px 0 28px;
  background: var(--paper);
}
/* The status line rides just above it, for the same reason: an error nobody can
   see reads as nothing happening. */
.cal-status {
  position: sticky; bottom: calc(50px + 12px); z-index: 2;
  background: var(--paper);
}
@media (max-width: 640px) {
  /* .cal-modal-box drops to 20px padding at this width. */
  .cal-actions { bottom: -20px; margin-bottom: -20px; padding-bottom: 20px; }
}
.cal-actions .btn { padding: 11px 20px; }
.cal-danger {
  margin-left: auto; border: 0; background: none; padding: 8px 4px;
  font: inherit; font-size: 0.88rem; color: #a3324b; cursor: pointer; text-decoration: underline;
}
.cal-danger.solid {
  margin-left: 0; text-decoration: none; color: #fff; background: #a3324b;
  border-radius: var(--radius); padding: 11px 20px;
}
.cal-actions button[disabled] { opacity: .55; cursor: default; }

.cal-toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 110;
  background: var(--ink); color: #fff; border-radius: 999px;
  padding: 11px 22px; font-size: 0.9rem;
  box-shadow: 0 10px 30px rgba(28, 23, 51, 0.3);
}
.cal-toast[hidden] { display: none; }

@media (max-width: 560px) {
  .cal-modal-box { padding: 20px; width: calc(100% - 16px); }
  .cal-grid2 { grid-template-columns: 1fr; }
  .cal-danger { margin-left: 0; }
}

/* ---------- Calendar: this clean, or every one after it ---------- */

.cal-scope {
  border: 1px solid var(--line-soft); border-radius: 10px;
  padding: 12px 14px 4px; margin: 0 0 16px;
}
.cal-scope legend { padding: 0 6px; font-size: 0.78rem; color: var(--ink-3); }
.cal-scope label {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 0; cursor: pointer;
}
.cal-scope input { margin-top: 3px; flex: none; }
.cal-scope strong { display: block; font-size: 0.95rem; font-weight: 600; }
.cal-scope em {
  display: block; font-style: normal;
  font-size: 0.83rem; color: var(--ink-3); margin-top: 2px; line-height: 1.45;
}
/* Splitting a schedule leaves individually-changed visits behind. Saying so
   before the VA commits is cheaper than explaining it to a customer after. */
.cal-scope-note {
  margin: 4px 0 10px; padding-left: 10px;
  border-left: 2px solid var(--accent);
  font-size: 0.82rem; color: var(--ink-2); line-height: 1.45;
}

/* ---------- Calendar: a finished clean at a glance ---------- */

.cal-facts { width: 100%; border-collapse: collapse; margin: 0 0 16px; }
.cal-facts th {
  text-align: left; font-weight: 400; vertical-align: top;
  padding: 9px 18px 9px 0; white-space: nowrap;
  font-size: 0.78rem; color: var(--ink-3); letter-spacing: .02em;
}
.cal-facts td {
  padding: 9px 0; vertical-align: top;
  font-size: 0.93rem; color: var(--ink); line-height: 1.5;
}
.cal-facts tr + tr th, .cal-facts tr + tr td { border-top: 1px solid var(--line-soft); }

/* ---------- Calendar: unfinished bookings ---------- */

.cal-leads { margin: 0 0 16px; }
.cal-lead-row { padding: 16px 0; border-top: 1px solid var(--line-soft); }
.cal-lead-row.closed { opacity: 0.45; }
.cal-lead-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 14px; margin-bottom: 4px;
}
.cal-lead-head strong { font-size: 1.02rem; }
.cal-lead-head span { font-family: var(--serif); color: var(--plum); white-space: nowrap; }
.cal-lead-meta { margin: 0 0 4px; font-size: 0.86rem; color: var(--ink-2); line-height: 1.45; }

/* ---------- Booking: terms consent ---------- */

.calc-terms {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 16px; font-size: 0.92rem; line-height: 1.5; color: var(--ink-2);
  cursor: pointer;
}
/* Big enough to hit on a phone, and never allowed to shrink when the label
   wraps to three lines next to it. */
.calc-terms input {
  flex: none; width: 18px; height: 18px; margin-top: 2px; accent-color: var(--plum);
}
.calc-terms a { text-decoration: underline; }

/* ---------- Booking: what happens to your details ---------- */

/* Sits between the "Your details" label and the fields, so it is read before
   anything is typed rather than after. Quiet, but not hidden: greying it out
   any further would defeat the point of saying it. */
.field-note {
  margin: 0 0 14px;
  font-size: 0.85rem; line-height: 1.55; color: var(--ink-2);
  padding-left: 11px; border-left: 2px solid var(--line);
}

/* ---------- Guides: where the facts came from ---------- */

/* Set apart from the argument above it. A reader should be able to tell at a
   glance that this is provenance, not more prose. */
.guide-sources {
  margin: 40px 0 0;
  padding: 22px 24px;
  background: var(--bone-deep);
  border-radius: var(--radius-lg);
}
.guide-sources h2 { margin: 0 0 10px; font-size: 1.05rem; }
.guide-sources p { margin: 0 0 12px; font-size: 0.9rem; color: var(--ink-2); line-height: 1.6; }
.guide-sources ul { margin: 0; padding-left: 18px; }
.guide-sources li {
  margin-bottom: 8px; font-size: 0.9rem; line-height: 1.6; color: var(--ink-2);
}
.guide-sources a { color: var(--plum); }
