/*
  OnTime Beyond Clean, "Signed Off" theme on the prototype's navy/lime palette.

  One rule worth knowing before editing: --accent-text flips per surface. On a
  light background it resolves to the darkened lime (readable); inside a navy
  section it resolves to the bright lime. Use var(--accent-text) for any text or
  icon that should be "the accent colour", and never raw --color-lime on light.
*/

/* ---------- Base ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  --accent-text: var(--color-lime-ink);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-navy);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; text-decoration-color: color-mix(in srgb, currentColor 35%, transparent); }
a:hover { text-decoration-color: currentColor; }

:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 0 0 0.6em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.3rem, 5vw, 3.7rem); }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.8rem); }
h3 { font-size: clamp(1.2rem, 2vw, 1.45rem); }

p { margin: 0 0 1.1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-navy);
  color: var(--color-paper);
  padding: 0.75rem 1.25rem;
  z-index: 100;
}
.skip-link:focus { left: 0; }

/* Material Symbols, drawn from the sprite inlined at the top of the body.
   `currentColor` is the whole trick: an icon takes the colour of the text it
   sits with, so it never needs a colour rule of its own. */
.icon {
  width: 1.15rem; height: 1.15rem; flex: none;
  fill: currentColor; vertical-align: -0.19em;
}
.icon--sm { width: 1rem; height: 1rem; }
.icon--lg { width: 1.4rem; height: 1.4rem; }

/* ---------- Layout ---------- */

.wrap {
  width: min(1180px, 100% - 2.5rem);
  margin-inline: auto;
}

.section { padding: clamp(3.5rem, 8vw, 6.5rem) 0; }
.section--tight { padding: clamp(2.5rem, 5vw, 4rem) 0; }
.section--alt { background: var(--color-paper-alt); }

.section--ink {
  background: var(--color-navy);
  color: var(--color-paper);
  --accent-text: var(--color-lime);
}
.section--ink a { color: var(--color-paper); }

.section--pine {
  background: var(--color-navy-soft);
  color: var(--color-paper);
  --accent-text: var(--color-lime);
}

.section-head { max-width: 46ch; margin-bottom: 2.75rem; }
.section-head--wide { max-width: 60ch; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin: 0 0 0.9rem;
}

.lede {
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  max-width: 58ch;
}

.rule {
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--color-navy-soft) 25%, transparent);
  margin: 0;
}

/* Two-column "text beside a photo" block. */
/* Top aligned, not centred. Centring looks right when the two columns are a
   similar height and wrong the moment they are not: beside a tall card the
   photo floats in the middle of its own column with a band of empty page
   above and below it, which reads as a layout mistake rather than a choice.
   The guarantee section is the one that showed it worst. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.split--wide-text { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
.split--photo-first > .split__media { order: -1; }

@media (max-width: 900px) {
  .split,
  .split--wide-text { grid-template-columns: 1fr; }
}

/* ---------- Photos ---------- */

.figure {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-paper-alt);
  box-shadow: 0 24px 60px -34px rgba(21, 34, 58, 0.45);
}

.figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 3 / 2;
}

/* Every image gets the same curve, including ones not wrapped in a figure. */
.card__media img,
.track__media img,
.brand__logo,
.footer-logo { border-radius: 0; }

/* The card is rounded and clips its contents, so the media inside must not
   round again or the image corners would pull away from the card corners.
   `overflow: hidden` is the half that was missing: without it the radius was
   there but nothing honoured it, and a photo sitting flush to the top of a
   card covered the two corners with its own square ones. */
.card,
.track {
  border-radius: var(--radius);
  overflow: hidden;
}

/* Parallax: the image is oversized and shifted by JS, so the frame never
   shows an empty edge as it moves. */
.js-animate [data-parallax] img {
  will-change: transform;
  transform: scale(1.12);
}

.figure--band img { aspect-ratio: 21 / 8; }
.figure--tall img { aspect-ratio: 4 / 5; }

/* A photo used as a section background, with a scrim so text stays readable.
   The colour flip matters as much as the scrim: without it the section would
   inherit navy body text and sit invisibly on its own dark background. */
.section--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--color-navy);
  color: var(--color-paper);
  --accent-text: var(--color-lime);
}
.section--photo a { color: var(--color-paper); }
.section--photo .btn--primary { color: var(--color-navy); }
.section--photo > .section-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.section--photo > .section-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The lightest stop is 72% and not lower on purpose: over a bright photo, a
   55% scrim drops paper-on-navy text to about 3:1, which fails. At 72% it
   holds around 5:1 even against white. */
.section--photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    100deg,
    color-mix(in srgb, var(--color-navy) 95%, transparent) 0%,
    color-mix(in srgb, var(--color-navy) 86%, transparent) 55%,
    color-mix(in srgb, var(--color-navy) 72%, transparent) 100%
  );
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  padding: 0.9rem 1.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.18s var(--ease-signature), background-color 0.18s ease, color 0.18s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--color-lime);
  color: var(--color-navy);
}
.btn--primary:hover { background: color-mix(in srgb, var(--color-lime) 84%, #fff); }

.btn--secondary {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}
.btn--secondary:hover { background: color-mix(in srgb, currentColor 10%, transparent); }

.btn--ink { background: var(--color-navy); color: var(--color-paper); }
.btn--ink:hover { background: var(--color-navy-soft); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  align-items: center;
}

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--color-paper) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0.9rem 0;
}

.brand {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  flex: none;
}

/* The logo is a wide lockup, so it is sized by height and lets width follow. */
.brand__logo {
  height: 3.1rem;
  width: auto;
  max-width: min(58vw, 22rem);
  object-fit: contain;
}

.footer-logo {
  height: 2.6rem;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin-bottom: 1.25rem;
}

@media (max-width: 480px) {
  .brand__logo { height: 2.5rem; }
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

/* :not(.btn), because `.site-nav a` beats `.btn` on specificity and was
   flattening the button to no horizontal padding at all: 38px tall next to the
   same button's 56px everywhere else on the site. A nav link and a nav button
   are different things and only one of them wants an underline. */
.site-nav a:not(.btn) {
  font-size: 0.95rem;
  text-decoration: none;
  padding: 0.35rem 0;
  border-bottom: 1px solid transparent;
}
.site-nav a:not(.btn):hover,
.site-nav a:not(.btn)[aria-current="page"] { border-bottom-color: var(--color-lime-ink); }

.site-nav__phone {
  /* The icon and the number as one thing, so the number never wraps away from
     what identifies it. */
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid color-mix(in srgb, var(--color-navy) 25%, transparent);
  border-radius: var(--radius-pill);
  padding: 0.5rem 1rem;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}

/* The standalone primary button. Hidden by default because above the
   fold-away width the nav row already carries one; the media query below turns
   it on. Declared before that query, not after: equal specificity means the
   last rule wins, and putting this last silently cancelled it. */
.site-header__cta { display: none; }

/* Between a phone and a laptop the nav is too wide to sit in a row and the
   screen is too big to justify hiding everything. At 1024 the links fitted but
   the button was pressed against the edge of the window; at 768 the whole nav
   folded away and took the one button the site exists to be clicked with it.
   Tightened here, and the button pulled out of the menu below. */
@media (max-width: 1180px) and (min-width: 961px) {
  .site-header__inner { gap: 1rem; }
  .site-nav { gap: 1rem; }
  .site-nav a:not(.btn) { font-size: 0.9rem; }
  .site-nav .btn { padding: 0.75rem 1.2rem; }
  /* The phone is in the hero and the footer. It is the first thing to go when
     the row is tight, because the button is not. */
  .site-nav__phone { display: none; }
}

@media (max-width: 960px) {
  .nav-toggle { display: block; }

  /* The primary button stays out of the menu.
     A customer on a tablet should not have to open a hamburger to find the
     thing the whole page is asking them to do. */
  .site-header__inner { gap: 0.75rem; }
  /* Pushed right so it sits with the menu button rather than floating in the
     middle of the bar, which is what space-between does with three children. */
  .site-header__cta { display: inline-flex; margin-left: auto; }

  .site-nav {
    display: none;
    position: absolute;
    inset: 100% 0 auto;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--color-paper);
    border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
    padding: 0.5rem 1.25rem 1.25rem;
    z-index: 41;
  }
  .site-nav[data-open="true"] { display: flex; }
  .site-nav a {
    padding: 0.75rem 0;
    border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  }
  /* Already outside the menu, so not repeated inside it. */
  .site-nav .btn { display: none; }
}

/* ---------- The header on a phone ----------
   Three things fought over one row: a wide logo lockup, the button the site
   exists to have pressed, and the menu. Something always lost, and on the
   narrowest phones the logo was squeezed to a smudge.

   Two rows instead. The logo gets the first to itself and is centred, which
   is what a logo that wide wants. Underneath, the button on the left where a
   thumb reaches it and the menu on the right where a thumb expects it.

        +---------------------------+
        |         [ logo ]          |
        | [Instant Pricing]  [Menu] |
        +---------------------------+
*/
@media (max-width: 640px) {
  .site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "brand brand"
      "cta   toggle";
    gap: 0.75rem 1rem;
    padding: 0.75rem 0 0.85rem;
  }

  .brand {
    grid-area: brand;
    justify-content: center;
  }
  .brand__logo {
    height: 2.9rem;
    /* Room to be legible now it has a row of its own. */
    max-width: min(80vw, 17rem);
  }

  /* `margin-left: auto` from the tablet rules would push it off its own
     column, so it is put back. */
  .site-header__cta {
    grid-area: cta;
    margin-left: 0;
    justify-self: start;
  }

  .nav-toggle {
    grid-area: toggle;
    justify-self: end;
  }

  /* The dropdown hangs from the bottom of a header that is now taller, and
     `inset: 100% 0 auto` already follows it. */
}

@media (max-width: 380px) {
  /* Only at the very narrowest does the label need to give ground. */
  .site-header__cta { padding: 0.7rem 1rem; font-size: 0.85rem; }
  .brand__logo { height: 2.6rem; }
}

/* ---------- Home page slider ---------- */

.slider {
  position: relative;
  isolation: isolate;
  background: var(--color-navy);
  color: var(--color-paper);
  --accent-text: var(--color-lime);
  overflow: hidden;
}

.slider__media { position: absolute; inset: 0; z-index: -2; }

.slider__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-slide) var(--ease-signature);
}
.slider__img.is-active { opacity: 1; }

.slider::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    100deg,
    color-mix(in srgb, var(--color-navy) 96%, transparent) 0%,
    color-mix(in srgb, var(--color-navy) 88%, transparent) 50%,
    color-mix(in srgb, var(--color-navy) 62%, transparent) 100%
  );
}

/* All slides occupy the same grid cell, so the block is as tall as the tallest
   and nothing jumps when they change. */
.slider__slides {
  display: grid;
  padding: clamp(3.5rem, 8vw, 6rem) 0 clamp(5rem, 10vw, 8rem);
}

.slide {
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--duration-slide) var(--ease-signature),
    visibility 0s linear var(--duration-slide);
}
.slide.is-active {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.slide__copy { max-width: 40rem; }

/* No frame and no border: the photo is just a rounded rectangle sitting on
   the background image. */
.slide__media {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
}
.slide__media img {
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

/* The size set here rather than inherited: only the first slide's title is an
   H1 now, and the rest are H2s, which would otherwise come out smaller. */
.slide__title { margin-bottom: 0.5em; color: var(--color-paper); font-size: clamp(2.3rem, 5vw, 3.7rem); }
.slide__text {
  font-size: clamp(1.05rem, 1.7vw, 1.25rem);
  max-width: 46ch;
  margin-bottom: 1.75rem;
  color: color-mix(in srgb, var(--color-paper) 88%, transparent);
}

@media (max-width: 900px) {
  .slide { grid-template-columns: 1fr; }
  .slide__media { display: none; }
}

.slider__controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(1.25rem, 3vw, 2rem);
  display: flex;
  align-items: center;
  gap: 1rem;
  pointer-events: none;
}
.slider__controls > * { pointer-events: auto; }

.slider__dots { display: flex; gap: 0.5rem; align-items: center; }

.slider__dot {
  width: 2.25rem;
  height: 3px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: color-mix(in srgb, var(--color-paper) 35%, transparent);
  cursor: pointer;
  transition: background-color 0.25s ease;
}
.slider__dot[aria-current="true"] { background: var(--color-lime); }

.slider__arrows { display: flex; gap: 0.5rem; margin-left: auto; }

.slider__arrow {
  width: 2.5rem;
  height: 2.5rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--color-paper) 40%, transparent);
  background: color-mix(in srgb, var(--color-navy) 55%, transparent);
  color: var(--color-paper);
  font-size: 1rem;
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}
.slider__arrow:hover {
  background: var(--color-lime);
  border-color: var(--color-lime);
  color: var(--color-navy);
}

.slider__img { transform: scale(1.08); }

/* ---------- The quote page: the hero, with the questions in the box ---------- */

/* The slides take the left column and the box the right, where the home
   page's photo sits. The slides still stack in one cell, so the copy can
   change without the box moving. */
.quote-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  padding: clamp(2.5rem, 6vw, 4.5rem) 0 clamp(4.5rem, 8vw, 6rem);
}
.slider--quote .slider__slides { padding: 0; }
.slider--quote .slide { grid-template-columns: 1fr; }

.qbox {
  position: relative;
  background: var(--color-surface);
  color: var(--color-navy);
  --accent-text: var(--color-lime-ink);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px -24px rgb(0 0 0 / 0.55);
  padding: 1.35rem 1.5rem 1.25rem;
}
/* One height for every step on a desk-sized screen, so the box does not
   jump as they move through it, and tall enough that none of them scrolls. */
@media (min-width: 901px) {
  .qbox { min-height: 33rem; display: flex; flex-direction: column; }
  .qbox__form, .qbox__done { flex: 1; display: flex; flex-direction: column; }
  .qbox__form[hidden], .qbox__done[hidden] { display: none; }
}
@media (max-width: 900px) {
  .quote-hero { grid-template-columns: 1fr; }
}

/* Hidden means hidden in here, whatever display a class gives the element:
   the premises box is a grid, and was showing on a home clean. */
.qbox [hidden] { display: none !important; }

.qbox__head { margin-bottom: 1rem; }
.qbox__eyebrow {
  display: flex; justify-content: space-between; gap: 1rem;
  font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--color-muted); margin: 0 0 0.55rem;
}
.qbox__count { letter-spacing: 0.04em; }
.qbox__bar {
  height: 4px; border-radius: 2px; overflow: hidden;
  background: color-mix(in srgb, var(--color-navy) 10%, transparent);
}
.qbox__bar span { display: block; height: 100%; width: 0; background: var(--color-lime-ink); transition: width 0.3s ease; }

.qbox__step { border: 0; margin: 0 0 1rem; padding: 0; min-width: 0; }
.qbox__form.is-stepped .qbox__step[hidden] { display: none; }
.qbox__title,
.qbox__step > legend {
  display: block; padding: 0; margin: 0 0 0.9rem;
  font-weight: 600; font-size: clamp(1.25rem, 2vw, 1.5rem); line-height: 1.2;
  color: var(--color-navy);
}

.qbox__cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.65rem; }
.qbox__card {
  position: relative; display: flex; flex-direction: column; gap: 0.25rem;
  margin: 0; padding: 0.85rem 0.95rem; cursor: pointer; font-weight: 400;
  border: 1.5px solid color-mix(in srgb, var(--color-navy) 15%, transparent);
  border-radius: var(--radius-sm);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.qbox__card:hover { border-color: color-mix(in srgb, var(--color-lime-ink) 50%, transparent); }
.qbox__card.is-chosen, .qbox__card:has(input:checked) {
  border-color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 14%, var(--color-surface));
}
.qbox__card:has(input:focus-visible) { outline: 2px solid var(--color-lime-ink); outline-offset: 2px; }
.qbox__card input { position: absolute; opacity: 0; pointer-events: none; }
.qbox__card-name { font-weight: 600; font-size: 1rem; }
.qbox__card-blurb { font-size: 0.82rem; line-height: 1.4; color: var(--color-muted); }
.qbox__card--slim { align-items: center; text-align: center; padding: 0.9rem 0.6rem; }

.qbox__fields { display: grid; gap: 0.75rem; }
.qbox__field { display: grid; gap: 0.3rem; min-width: 0; }
.qbox__field label, .qbox__label { font-weight: 600; font-size: 0.88rem; margin: 0; }
.qbox .field-input { padding: 0.6rem 0.8rem; font-size: 0.98rem; }
.qbox textarea.field-input { resize: vertical; }
.qbox__optional { font-weight: 400; color: var(--color-muted); }
.qbox__pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 0.75rem; }

.qbox__counters {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem;
}
.qbox__counter {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.4rem 0.5rem 0.4rem 0.75rem; min-width: 0;
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  border-radius: var(--radius-sm);
}
.qbox__counter-name { font-size: 0.9rem; min-width: 0; overflow-wrap: anywhere; }
.qbox__counter-controls { display: flex; align-items: center; gap: 0.15rem; flex: none; }
.qbox__btn {
  width: 1.9rem; height: 1.9rem; flex: none; padding: 0;
  border: 1px solid color-mix(in srgb, var(--color-navy) 20%, transparent);
  border-radius: 50%; background: var(--color-surface); color: var(--color-navy);
  font-size: 1rem; line-height: 1; cursor: pointer;
}
.qbox__btn:hover { border-color: var(--color-lime-ink); background: color-mix(in srgb, var(--color-lime) 18%, var(--color-surface)); }
.qbox__counter-controls input[type="number"] {
  width: 2rem; text-align: center; font-family: var(--font-mono); font-size: 0.95rem;
  border: 0; background: none; padding: 0.2rem 0; color: inherit;
  -moz-appearance: textfield;
}
.qbox__counter-controls input::-webkit-outer-spin-button,
.qbox__counter-controls input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qbox__counters--tight { margin-bottom: 0.8rem; gap: 0.4rem; }
.qbox__counters--tight .qbox__counter { padding-block: 0.15rem; }
.qbox__counters--tight .qbox__btn { width: 1.7rem; height: 1.7rem; }

.qbox__tick { display: flex; align-items: center; gap: 0.5rem; margin: 0; font-weight: 400; font-size: 0.9rem; cursor: pointer; }
.qbox__tick input, .qbox__area input { width: 1.05rem; height: 1.05rem; flex: none; accent-color: var(--color-lime-ink); }
.qbox__tick--plain { font-size: 0.88rem; }

.qbox__more {
  margin-top: 0.7rem; padding: 0.5rem 0.9rem;
  border: 1px dashed color-mix(in srgb, var(--color-navy) 30%, transparent);
  border-radius: var(--radius-pill); background: none; color: var(--color-navy);
  font: inherit; font-size: 0.88rem; font-weight: 600; cursor: pointer;
}
.qbox__more:hover { border-color: var(--color-lime-ink); }
.qbox__area-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.3rem 1rem; }
.qbox__area { display: flex; align-items: center; gap: 0.5rem; margin: 0; font-weight: 400; font-size: 0.86rem; cursor: pointer; }
.qbox__hint { font-size: 0.85rem; color: var(--color-muted); margin: 0 0 0.6rem; }
.qbox__hint--after { margin: 0.8rem 0 0; font-size: 0.8rem; }

.qbox__slots { display: flex; gap: 0.35rem; }
.qbox__slot { position: relative; flex: 1; margin: 0; cursor: pointer; font-weight: 400; }
.qbox__slot input { position: absolute; opacity: 0; pointer-events: none; }
.qbox__slot span {
  display: block; text-align: center; padding: 0.6rem 0.2rem; font-size: 0.84rem;
  border: 1px solid color-mix(in srgb, var(--color-navy) 22%, transparent);
  border-radius: var(--radius-sm);
}
.qbox__slot input:checked + span {
  border-color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 14%, var(--color-surface));
  font-weight: 600;
}
.qbox__slot input:focus-visible + span { outline: 2px solid var(--color-lime-ink); outline-offset: 2px; }

.qbox__errors { list-style: none; margin: 0.6rem 0 0; padding: 0; color: var(--color-alert); font-size: 0.86rem; }
.qbox__problem { color: var(--color-alert); font-size: 0.86rem; margin: 0 0 0.6rem; }

.qbox__nav { display: flex; gap: 0.6rem; justify-content: flex-end; margin-top: auto; padding-top: 0.5rem; }
.qbox__nav .btn { padding: 0.75rem 1.4rem; }
.qbox__back { margin-right: auto; background: none; border-color: color-mix(in srgb, var(--color-navy) 25%, transparent); color: var(--color-navy); }
.qbox__nav [hidden] { display: none; }

/* Without the script, every step shows and only the send button is useful. */
.qbox__form:not(.is-stepped) [data-qnext],
.qbox__form:not(.is-stepped) [data-qback],
.qbox__form:not(.is-stepped) .qbox__bar { display: none; }

/* A phone: one counter to a row, so a name like "Separate toilets" is not
   squeezed to a letter a line beside its buttons. */
@media (max-width: 560px) {
  .qbox { padding: 1.1rem 1rem 1rem; }
  .qbox__counters { grid-template-columns: 1fr; gap: 0.4rem; }
  .qbox__counter { padding-block: 0.2rem; }
  .qbox__pair { grid-template-columns: 1fr; }
}

.qbox__done { gap: 0.4rem; outline: none; }
.qbox__done p { margin: 0 0 0.6rem; }

/* ---------- Plain hero (inner pages) ---------- */

.hero { padding: clamp(3rem, 7vw, 5rem) 0 clamp(3rem, 7vw, 5rem); position: relative; }

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.hero__title { margin-bottom: 0.5em; }

.hero__promise {
  font-size: clamp(1.05rem, 1.7vw, 1.25rem);
  max-width: 44ch;
  margin-bottom: 2rem;
}

.hero__markers {
  list-style: none;
  margin: 2.25rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.75rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: color-mix(in srgb, currentColor 72%, transparent);
}
.hero__markers li { display: flex; align-items: center; gap: 0.5rem; }
.hero__markers li::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--accent-text);
  flex: none;
}

@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; }
}

/* ---------- Cards ---------- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 1.25rem;
}

.card {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  /* var(--radius), not a number. This rule comes after the shared one above,
     so a hardcoded value here silently won and cards sat at 4px while every
     other panel on the site was at 16px. The same mistake was in `.track`. */
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.2s ease, transform 0.2s var(--ease-signature);
}
.card:hover { border-color: color-mix(in srgb, var(--color-lime-ink) 45%, transparent); }

.card__media { margin: 0; background: var(--color-paper-alt); }
.card__media img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }

.card__body-wrap {
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  flex: 1;
}

.section--ink .card,
.section--pine .card {
  background: color-mix(in srgb, #fff 6%, transparent);
  border-color: color-mix(in srgb, #fff 16%, transparent);
}

.card__title { margin: 0; font-size: 1.2rem; }
.card__price {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--accent-text);
  letter-spacing: 0.02em;
}
.card__body { margin: 0; font-size: 0.98rem; }
.card__link {
  margin-top: auto;
  padding-top: 0.75rem;
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
  color: var(--color-lime-ink);
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
}
.section--ink .card__link { color: var(--color-lime); }
.card__link:hover { gap: 0.65rem; }

/* Two-track split */
.tracks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: 1.5rem;
}

.track {
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  /* Matches every other card and the images inside it. This said 4px, which
     overrode the shared rule further up the file and left these two the only
     near-square corners on the page. */
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-surface);
  display: flex;
  flex-direction: column;
}

.track__media { margin: 0; }
.track__media img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.track__inner { padding: clamp(1.5rem, 3vw, 2.25rem); display: flex; flex-direction: column; flex: 1; }

.track__price {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  color: var(--color-lime-ink);
  margin: 0.25rem 0 1rem;
}

.track__list { list-style: none; margin: 0 0 1.75rem; padding: 0; display: grid; gap: 0.55rem; }
/* Not a flex row.
   The tick, the service name and its summary are one sentence, and a flex
   container makes each of them an item that sizes itself. The moment the name
   became a link it got its own narrow column and wrapped down the page while
   the summary sat beside it in a second column. The tick hangs in the margin
   instead, and the text flows as text. */
.track__list li {
  position: relative;
  padding-left: 1.5rem;
  font-size: 0.98rem;
}
.track__list li::before {
  content: "\2713";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--color-lime-ink);
  font-family: var(--font-mono);
  font-size: 0.85rem;
}
/* The service names are links now. They were plain text, and the only
   clickable route to a service page was the footer -- so the pages that rank
   for "airbnb cleaning pakenham" had nothing pointing at them from the page
   most people land on. Underlined on hover rather than always, because a
   checklist of underlines reads as a list of warnings. */
.track__list a { color: inherit; text-decoration: none; font-weight: 600; }
.track__list a:hover,
.track__list a:focus-visible { text-decoration: underline; }

.track .btn-row { margin-top: auto; }

/* ---------- Pain points ---------- */

.pains {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 2.5rem;
}

.pain-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.4rem; }

.pain-list li {
  padding-left: 1.25rem;
  border-left: 2px solid color-mix(in srgb, var(--accent-text) 70%, transparent);
}

.pain-list h4 {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1.02rem;
  margin: 0 0 0.35rem;
  line-height: 1.4;
}

.pain-list p { font-size: 0.97rem; margin: 0; opacity: 0.88; }

/* ---------- Process ---------- */

/* Four steps that need to read as four distinct stages, not one repeated
   block: each is a card, each carries its own big numeral, and the set
   staircases downward so the eye follows the sequence. */
.process {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1.75rem 1.25rem;
  align-items: start;
}

.step {
  position: relative;
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  border-radius: var(--radius);
  padding: 3.25rem 1.5rem 1.6rem;
  box-shadow: 0 18px 40px -34px rgba(21, 34, 58, 0.5);
}

/* The staircase. Each card sits a little lower than the one before it. */
.step:nth-child(2) { margin-top: 1.25rem; }
.step:nth-child(3) { margin-top: 2.5rem; }
.step:nth-child(4) { margin-top: 3.75rem; }

/* The numeral, as a lime chip overlapping the top edge of its card. */
.step__number {
  position: absolute;
  top: -1.15rem;
  left: 1.5rem;
  min-width: 2.9rem;
  height: 2.3rem;
  padding: 0 0.7rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-lime);
  color: var(--color-navy);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.08em;
}

/* Later steps get progressively more solid, so "further along" is visible at
   a glance rather than only readable. */
.step:nth-child(2) .step__number { background: color-mix(in srgb, var(--color-lime) 78%, var(--color-navy-soft)); }
.step:nth-child(3) .step__number { background: var(--color-navy-soft); color: var(--color-lime); }
.step:nth-child(4) .step__number { background: var(--color-navy); color: var(--color-lime); }
.step:nth-child(4) { border-color: color-mix(in srgb, var(--color-navy) 30%, transparent); }

.step__title { font-size: 1.12rem; margin-bottom: 0.4rem; }
.step p { font-size: 0.96rem; margin: 0; color: var(--color-muted); }

/* The connector between cards, drawn only where they sit side by side. */
.step__link {
  position: absolute;
  top: -0.05rem;
  right: -1.25rem;
  width: 1.25rem;
  height: 1px;
  background: color-mix(in srgb, var(--color-navy) 22%, transparent);
}

@media (max-width: 900px) {
  .step:nth-child(n) { margin-top: 0; }
  .step__link { display: none; }
}

.section--ink .step,
.section--photo .step {
  background: color-mix(in srgb, #fff 7%, transparent);
  border-color: color-mix(in srgb, #fff 16%, transparent);
}
.section--ink .step p,
.section--photo .step p { color: color-mix(in srgb, var(--color-paper) 78%, transparent); }

/* ---------- Docket ----------
   The shared device for anything the customer is being held to: the
   guarantee, the cancellation terms, and any notice on a form.

   These were four separate pastel-tinted cards with a coloured bar down the
   left, which is the house style of every generated landing page on the
   internet and looked nothing like the rest of this site. This is built from
   what the site already uses instead: paper-white, a hairline rule, the mono
   label with its leading dash, and marks drawn rather than typed. It reads as
   a printed docket, which is the thing the brand is actually about.

   The alert red survives as a label colour only. It marks the section as
   binding without washing the whole panel pink. */

.guarantee,
.policy,
.form-notice {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 14%, transparent);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 3vw, 2rem);
}

/* The mono label with a rule running into it. Same gesture as the eyebrow
   used across the rest of the site, so this belongs to the same family. */
.guarantee__label,
.policy__title,
.form-notice__label {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-alert);
  margin: 0 0 1rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
}
.guarantee__label::before,
.policy__title::before,
.form-notice__label::before {
  content: "";
  flex: none;
  width: 1.5rem;
  height: 2px;
  background: currentColor;
}

.guarantee h2 { margin-bottom: 0.6rem; }
.guarantee__promise { margin: 0 0 1.25rem; max-width: 62ch; }

/* The deadline decides whether the promise applies, so it is set as a figure
   rather than a sentence: mono, on its own line, underscored in lime. */
.guarantee__window {
  margin: 0 0 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  display: inline-block;
  padding-bottom: 0.3rem;
  border-bottom: 2px solid var(--color-lime);
}
.guarantee__window strong { font-weight: 500; }

.guarantee__terms,
.policy__terms {
  display: grid;
  gap: 1.5rem 2.5rem;
  grid-template-columns: 1fr;
  margin: 0 0 1.25rem;
}

/* Covered against not covered is genuinely two-sided, so it splits. The
   cancellation terms are a sequence in time and stay in one column, read top
   to bottom, rather than wrapping and leaving the last one stranded. */
@media (min-width: 640px) {
  .guarantee__terms { grid-template-columns: 1fr 1fr; }
}
.policy__terms { gap: 0.9rem; }

.guarantee__terms h3 {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin: 0 0 0.7rem;
}
.guarantee__terms ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
  font-size: 0.93rem;
}
.guarantee__terms li {
  padding-left: 1.5rem;
  position: relative;
  line-height: 1.5;
}

/* Drawn, not typed. A "✓" glyph varies by font and reads as an emoji at small
   sizes; two borders rotated are crisp at any size and are ours. */
.guarantee__terms li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 0.5rem;
  height: 0.28rem;
  border-left: 2px solid var(--color-lime-ink);
  border-bottom: 2px solid var(--color-lime-ink);
  transform: rotate(-45deg);
}
.guarantee__not li { color: var(--color-muted); }
.guarantee__not li::before {
  top: 0.68em;
  width: 0.7rem;
  height: 0;
  border-left: 0;
  border-bottom: 2px solid color-mix(in srgb, var(--color-navy) 30%, transparent);
  transform: none;
}

.guarantee__note,
.policy__note {
  margin: 0;
  font-size: 0.87rem;
  color: var(--color-muted);
  padding-top: 1rem;
  border-top: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
}

/* The cancellation terms: same docket, laid out as a definition list. */
.policy__terms > div { display: grid; gap: 0.2rem; align-content: start; }
.policy__terms dt {
  font-weight: 600;
  font-size: 0.93rem;
}
.policy__terms dd {
  margin: 0;
  font-size: 0.9rem;
  color: var(--color-muted);
  line-height: 1.5;
}
/* ---------- Suburb list ---------- */

.suburbs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 2rem;
  font-family: var(--font-mono);
  font-size: 0.95rem;
}
.suburbs a {
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.suburbs a:hover { border-bottom-color: var(--accent-text); }

/* ---------- Checklist inclusions (location pages) ---------- */

.inclusions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1.5rem;
}

.inclusion-group {
  border-top: 2px solid var(--accent-text);
  padding-top: 1rem;
}

.inclusion-group h3 {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 0.9rem;
}

.inclusion-list { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.5rem; }
.inclusion-list li { display: flex; gap: 0.55rem; font-size: 0.95rem; align-items: baseline; }
.inclusion-list li::before {
  content: "\2713";
  color: var(--accent-text);
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.inclusion-list--excluded li::before {
  content: "\00D7";
  color: color-mix(in srgb, currentColor 55%, transparent);
}

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

.faq { max-width: 52rem; display: grid; gap: 0.25rem; }

.faq details {
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 14%, transparent);
  padding: 0.35rem 0;
}

.faq summary {
  cursor: pointer;
  padding: 0.9rem 2rem 0.9rem 0;
  font-weight: 600;
  font-size: 1.02rem;
  list-style: none;
  position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+";
  position: absolute;
  right: 0.25rem;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  color: var(--accent-text);
  font-size: 1.2rem;
}
.faq details[open] summary::after { content: "\2212"; }
.faq details p { padding: 0 2rem 1rem 0; margin: 0; opacity: 0.9; }

/* ---------- Forms ---------- */

.form { display: grid; gap: 1.25rem; max-width: 38rem; }

.form label { font-weight: 600; font-size: 0.92rem; display: block; margin-bottom: 0.35rem; }

/* A Yes / No question: the legend reads like a label, the two answers sit on
   one line. `.form .choice-row__option`, two classes, because `.form label`
   is 0,1,1 and would otherwise make each option a block of its own. */
.choice-row { border: 0; padding: 0; margin: 0; min-width: 0; }
.choice-row legend { font-weight: 600; font-size: 0.92rem; padding: 0; margin-bottom: 0.45rem; }
.choice-row__options { display: flex; gap: 1.5rem; flex-wrap: wrap; }
.form .choice-row__option {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-weight: 400; font-size: 1rem; margin: 0; cursor: pointer;
}
.choice-row__option input { width: 1.1rem; height: 1.1rem; accent-color: var(--color-navy); }

.field-input {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  padding: 0.8rem 1rem;
  border: 1px solid color-mix(in srgb, var(--color-navy) 25%, transparent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: inherit;
}
.field-input:focus-visible { border-color: var(--color-lime-ink); }

.field-help { font-size: 0.85rem; color: var(--color-muted); margin: 0.35rem 0 0; }

.errorlist {
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
  color: var(--color-alert);
  font-size: 0.9rem;
}

.slots { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); gap: 0.75rem; }
.slots ul { list-style: none; margin: 0; padding: 0; display: contents; }

.slots label {
  display: block;
  border: 1px solid color-mix(in srgb, var(--color-navy) 22%, transparent);
  border-radius: var(--radius-sm);
  padding: 1rem 1.1rem;
  cursor: pointer;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.95rem;
  margin: 0;
  background: var(--color-surface);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.slots label:hover { border-color: var(--color-lime-ink); }
.slots input { accent-color: var(--color-lime-ink); margin-right: 0.5rem; }
.slots label:has(input:checked) {
  border-color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 28%, var(--color-surface));
}

.form-steps { display: grid; gap: 2.5rem; }

.form-step__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-lime-ink);
  margin: 0 0 0.75rem;
}

/* Required marker. Uses the alert red rather than the accent, so it reads as
   "you must" rather than as decoration. */
.req {
  color: var(--color-alert);
  font-weight: 700;
  margin-left: 0.15rem;
}

.form-required-note {
  font-size: 0.88rem;
  color: var(--color-muted);
  margin: 0;
}

.form label .muted { font-weight: 400; font-size: 0.85rem; }

/* Shape only. The docket block above owns how it looks. */
.form-notice {
  margin: 0 0 1.5rem;
  max-width: 42rem;
  font-size: 0.93rem;
}

/* The spam trap. Off-screen rather than display:none, because some bots skip
   fields that are outright hidden. */
.form-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- Address lookup ---------- */

.address-field { position: relative; }

.address-suggestions {
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0.25rem;
  border: 1px solid color-mix(in srgb, var(--color-navy) 18%, transparent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  box-shadow: 0 18px 40px -28px rgba(21, 34, 58, 0.5);
  max-height: 16rem;
  overflow-y: auto;
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
}
.address-suggestions[hidden] { display: none; }

.address-suggestions li { margin: 0; }

.address-suggestions button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.6rem 0.75rem;
  border-radius: calc(var(--radius-sm) - 2px);
  cursor: pointer;
}
.address-suggestions button strong { display: block; font-weight: 600; }
.address-suggestions button span { display: block; font-size: 0.82rem; color: var(--color-muted); }
.address-suggestions button:hover,
.address-suggestions button[aria-selected="true"] {
  background: color-mix(in srgb, var(--color-lime) 25%, var(--color-surface));
}

.address-status {
  font-size: 0.85rem;
  color: var(--color-muted);
  margin: 0.35rem 0 0;
}

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

.site-footer {
  background: var(--color-navy);
  color: color-mix(in srgb, var(--color-paper) 88%, transparent);
  padding: clamp(3rem, 6vw, 4.5rem) 0 2rem;
  font-size: 0.95rem;
  --accent-text: var(--color-lime);
}

.footer-grid {
  display: grid;
  /* The brand column first and wider, then the link columns sharing what is
     left evenly.

     Five equal tracks made the brand column exactly as wide as a list of
     links, so the logo and the blurb under it were cramped into 11rem while
     "Areas we service" carried nine region names in the same width. Giving
     the first track its own minmax fixes that without pinning the count:
     three of the four link columns are conditional in the template, so a
     hard-coded `repeat(4, 1fr)` would leave a gap the day a section is
     switched off.

     The widths still fit on one row, which was the point of the previous
     note: 13rem + four at 10rem + four 2rem gaps is 61rem inside a 1180px
     wrap. `min(100%, ...)` on both keeps them from overflowing a phone. */
  grid-template-columns:
    minmax(min(100%, 13rem), 1.6fr)
    repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
  gap: 2.5rem 2rem;
  align-items: start;
  margin-bottom: 2.5rem;
}

/* One column on a phone.

   Giving the brand track a 13rem floor took away auto-fit's ability to drop
   to a single column: at 390px it laid out 208px + 160px + a 32px gap = 400px
   and the page scrolled sideways. That is the same horizontal overflow this
   footer already had once, reintroduced by the fix for the column widths.
   Measured with tools/three-fixes-shot.js, which reports scrollWidth against
   clientWidth at three viewports for exactly this reason. */
@media (max-width: 559px) {
  .footer-grid { grid-template-columns: 1fr; }
}

.site-footer h4 {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-lime);
  margin-bottom: 1rem;
}

.footer-links { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.footer-links a { text-decoration: none; opacity: 0.9; }
.footer-links a:hover { opacity: 1; text-decoration: underline; }

.footer-contact {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  display: grid;
  gap: 0.55rem;
  /* The column must be allowed to shrink. Left implicit it is sized to the
     longest line - the email address - and a grid column wider than its own
     grid simply overflows, which put the whole footer past the right edge of
     every page between roughly 1000px and 1300px and gave the site a
     horizontal scrollbar on any laptop. */
  grid-template-columns: minmax(0, 1fr);
}

/* Icon and value on one line, aligned to the first line of text so a wrapping
   email address does not leave its icon floating in the middle.

   Direct children only. Written as `.footer-contact span` this also caught
   the inner span that holds the text, turning every value into a flex
   container of its own, and a flex container does not wrap its contents the
   way a line of text does. That is the other half of why the email never
   broke and always overflowed. */
.footer-contact > a,
.footer-contact > span {
  display: inline-flex;
  align-items: start;
  gap: 0.5rem;
  /* Flex items refuse to shrink below their content unless told they may. */
  min-width: 0;
}

/* The email has no spaces in it, so there is nowhere for ordinary wrapping to
   break and `anywhere` is the only thing that will. */
.footer-contact > a > span,
.footer-contact > span > span {
  min-width: 0;
  overflow-wrap: anywhere;
}
.footer-contact .icon {
  margin-top: 0.15em;
  opacity: 0.75;
}

.footer-blurb {
  max-width: 26ch;
  font-size: 0.92rem;
  color: color-mix(in srgb, var(--color-paper) 72%, transparent);
}

.footer-bottom {
  border-top: 1px solid color-mix(in srgb, var(--color-paper) 18%, transparent);
  padding-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  font-size: 0.85rem;
  opacity: 0.75;
}

/* The build credit sits opposite the copyright, and wraps under it on a
   phone because .footer-bottom already wraps. Underlined rather than
   coloured: the row is at 0.75 opacity, and lime on navy at this size reads
   as a call to action, which this is not. */
.footer-bottom__by a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.footer-bottom__by a:hover,
.footer-bottom__by a:focus-visible {
  color: var(--color-lime);
}

/* ---------- Scroll and text animation ---------- */

/* Start states are only applied once JS has confirmed it will animate, so a
   failed CDN or reduced-motion leaves a complete, readable page. */
.js-animate [data-reveal],
.js-animate [data-process] .step {
  opacity: 0;
  transform: translateY(30px);
}

/* With GSAP driving each slide's contents, the container must not also slide
   or the two movements read as one mushy one. */
.js-animate .slide { transform: none; }

/* Headings split into words by JS, then rise into place a word at a time. */
.split-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
/* Start state lives here, not in a gsap.set(), so the tween simply animates
   from whatever the stylesheet already put on screen. */
.split-word > span {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .js-animate [data-reveal],
  .js-animate [data-process] .step { opacity: 1; transform: none; }
  .js-animate [data-parallax] img { transform: none; }
  .split-word > span { transform: none; opacity: 1; }
  .btn:hover { transform: none; }
  .slide { transform: none; }
  .slider__img { transform: none; }
}

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

.mono { font-family: var(--font-mono); }
.muted { color: var(--color-muted); }
.section--ink .muted,
.slider .muted,
.site-footer .muted { color: color-mix(in srgb, var(--color-paper) 75%, transparent); }
.stack > * + * { margin-top: 1.5rem; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- Cleaner portal ----------
   A work tool, not a page to browse. Used one-handed on a phone in a
   driveway, so: no marketing chrome, the next action always reachable with a
   thumb, and the two facts that decide anything (pay, address) largest. */

body.portal {
  background: var(--color-paper-alt);
  padding-bottom: 5.5rem; /* clear of the tab bar */
}

.portal-bar {
  position: sticky; top: 0; z-index: 30;
  background: var(--color-navy); color: var(--color-paper);
}
.portal-bar__inner {
  width: min(1180px, 100% - 2rem); margin-inline: auto;
  display: flex; align-items: center; gap: 1rem;
  padding: 0.6rem 0; font-size: 0.9rem;
}
.portal-bar__brand {
  display: flex; align-items: center; gap: 0.5rem;
  font-weight: 700; text-decoration: none; color: inherit; letter-spacing: -0.01em;
}
/* Height-led, and no plate. The plate kept a small dark favicon legible on
   the navy bar; the mark here is the footer logo now, which is already drawn
   for a dark ground. */
.portal-bar__mark {
  height: 1.6rem;
  width: auto;
  max-width: 9rem;
  object-fit: contain;
  flex-shrink: 0;
}
.portal-bar__who { margin-left: auto; opacity: 0.75; font-size: 0.85rem; }
.portal-bar__out {
  background: none; border: 0; color: inherit; font: inherit; font-size: 0.85rem;
  opacity: 0.75; cursor: pointer; padding: 0.3rem 0;
}
.portal-bar__out:hover { opacity: 1; }

.portal-main { width: min(1180px, 100% - 2rem); margin-inline: auto; padding: 1.5rem 0 2rem; }

.portal-head { margin-bottom: 1.25rem; }
.portal-head h1 {
  font-family: var(--font-body); font-weight: 700; letter-spacing: -0.02em;
  font-size: clamp(1.5rem, 4vw, 2rem); margin: 0 0 0.2rem; text-wrap: balance;
}
.portal-head__sub { margin: 0; color: var(--color-muted); font-size: 0.95rem; }
.portal-head--job h1 { font-size: clamp(1.35rem, 3.6vw, 1.75rem); line-height: 1.25; }
.job-ref { font-family: var(--font-mono); font-size: 0.78rem; color: var(--color-muted); margin: 0 0 0.35rem; }
.portal-subhead {
  font-family: var(--font-body); font-weight: 700; font-size: 1.1rem;
  margin: 2.25rem 0 1rem;
}

/* Thumb bar. Fixed, because on a phone the alternative is scrolling back up. */
.portal-tabs {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: flex; background: var(--color-surface);
  border-top: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  padding-bottom: env(safe-area-inset-bottom);
}
.portal-tabs a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  padding: 0.65rem 0.5rem; text-decoration: none; font-size: 0.76rem;
  color: var(--color-muted); position: relative;
}
.portal-tabs a[aria-current="page"] { color: var(--color-navy); font-weight: 600; }
.portal-tabs a[aria-current="page"]::before {
  content: ""; position: absolute; top: 0; left: 22%; right: 22%; height: 2px;
  background: var(--color-lime-ink);
}
.portal-tabs__icon { display: inline-flex; line-height: 1; }
.portal-tabs__icon .icon { width: 1.15rem; height: 1.15rem; vertical-align: 0; }
.portal-tabs__count {
  position: absolute; top: 0.35rem; right: 50%; transform: translateX(1.6rem);
  font-style: normal; font-family: var(--font-mono); font-size: 0.62rem;
  background: var(--color-lime); color: var(--color-navy);
  border-radius: var(--radius-pill); padding: 0 0.35rem;
}
/* On a desktop there is no thumb to reach with, and a bar pinned to the bottom
   of a wide screen reads as leftover furniture. Ordered back up under the
   header instead, as the tab strip it actually is. */
@media (min-width: 800px) {
  body.portal { display: flex; flex-direction: column; min-height: 100vh; padding-bottom: 2rem; }
  .portal-bar { order: 0; }
  .portal-tabs { order: 1; }
  .portal-main { order: 2; }

  .portal-tabs {
    position: static; background: none; border-top: 0; padding-bottom: 0;
    width: min(1180px, 100% - 2rem); margin: 1.25rem auto 0; gap: 0.25rem;
    border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  }
  .portal-tabs a {
    flex: 0 0 auto; flex-direction: row; gap: 0.5rem;
    padding: 0.6rem 1.15rem; font-size: 0.92rem;
  }
  .portal-tabs a[aria-current="page"]::before { top: auto; bottom: -1px; left: 0; right: 0; }
  .portal-tabs__count { position: static; transform: none; }
}

/* Flashes are transient, so they stay small, but they lose the pastel fill for
   the same reason the dockets did. The status is carried by a drawn mark on
   the left rather than by tinting the whole strip. */
.flash { list-style: none; margin: 0 0 1.25rem; padding: 0; display: grid; gap: 0.5rem; }
.flash__item {
  position: relative;
  padding: 0.75rem 1rem 0.75rem 2.4rem;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--color-navy) 14%, transparent);
  background: var(--color-surface);
  font-size: 0.93rem;
}
.flash__item::before {
  content: "";
  position: absolute;
  left: 1rem;
  top: 1.15em;
  width: 0.7rem;
  height: 2px;
  background: var(--color-navy-soft);
}
/* A drawn tick, matching the docket lists. */
.flash__item--success::before {
  top: 1em;
  width: 0.5rem;
  height: 0.28rem;
  background: none;
  border-left: 2px solid var(--color-lime-ink);
  border-bottom: 2px solid var(--color-lime-ink);
  transform: rotate(-45deg);
}
.flash__item--error::before { background: var(--color-alert); height: 3px; }

/* auto-fill, not auto-fit: with one job left, auto-fit stretches that single
   card across the whole desktop width and it stops reading as a card. */
.job-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); gap: 1rem; }

.job-card {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  border-radius: var(--radius);
  padding: 1.1rem 1.15rem 1.15rem;
  display: flex; flex-direction: column; gap: 0.7rem;
}
.job-card--link { text-decoration: none; color: inherit; }
.job-card--link:hover { border-color: var(--color-lime-ink); }
.job-card--quiet { opacity: 0.68; }

/* Pay is the number a cleaner is choosing on, so it is the biggest thing. */
.job-card__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.job-card__pay {
  font-family: var(--font-mono); font-size: 1.6rem; font-weight: 500;
  color: var(--color-navy); line-height: 1;
}
.job-card__when { text-align: right; font-size: 0.85rem; color: var(--color-muted); line-height: 1.35; }
.job-card__when em { font-style: normal; color: var(--color-navy); font-weight: 600; }

.job-card__where { font-size: 1.02rem; font-family: var(--font-body); font-weight: 600; margin: 0; line-height: 1.35; }
.job-card__what { margin: 0; font-size: 0.92rem; color: var(--color-muted); }

.job-card__tags { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.job-card__tags li {
  font-size: 0.75rem; color: var(--color-navy);
  background: color-mix(in srgb, var(--color-navy) 7%, transparent);
  border-radius: var(--radius-pill); padding: 0.15rem 0.6rem;
}

.job-card__notes {
  margin: 0; font-size: 0.9rem; font-style: italic; color: var(--color-muted);
  border-left: 2px solid color-mix(in srgb, var(--color-navy) 16%, transparent);
  padding-left: 0.75rem;
}
.job-card__next { margin: 0; font-size: 0.88rem; display: flex; align-items: center; gap: 0.45rem; }
.dot { width: 0.55rem; height: 0.55rem; border-radius: 50%; flex: none; }
.dot--now { background: var(--color-lime); }
.dot--go { background: var(--color-navy-soft); }
.dot--done { background: var(--color-lime-ink); }
.dot--bad { background: var(--color-alert); }

/* ---------- Cleaner home ---------- */

.portal-bar__who { display: inline-flex; align-items: center; gap: 0.35rem; color: inherit; text-decoration: none; }
.portal-bar__who:hover, .portal-bar__who[aria-current="page"] { opacity: 1; }
.portal-bar__who .icon { width: 1rem; height: 1rem; }

.dash-alerts { list-style: none; margin: 0 0 1.25rem; padding: 0; display: grid; gap: 0.6rem; }
.dash-alert {
  display: flex; gap: 0.75rem; align-items: flex-start;
  background: var(--color-surface); border-radius: var(--radius-sm);
  padding: 0.85rem 1rem; border-left: 4px solid var(--color-lime-ink);
}
.dash-alert--warn { border-left-color: #C98A1B; }
.dash-alert--bad { border-left-color: var(--color-alert); }
.dash-alert__icon .icon { width: 1.2rem; height: 1.2rem; }
.dash-alert--warn .dash-alert__icon { color: #9A6710; }
.dash-alert--bad .dash-alert__icon { color: var(--color-alert); }
.dash-alert p { margin: 0.15rem 0 0; font-size: 0.9rem; color: var(--color-muted); }

.dash { display: grid; gap: 1.5rem; }
@media (min-width: 900px) {
  .dash { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); align-items: start; }
}
.dash__label {
  font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--color-muted); font-weight: 500;
  margin: 0 0 0.6rem;
}
.dash__main .dash__label:not(:first-child) { margin-top: 1.75rem; }
.dash__label--bad { color: var(--color-alert); }
.dash__none { margin: 0; font-size: 0.92rem; }
.dash__more { margin: 0.6rem 0 0; font-size: 0.9rem; }

.next-job {
  background: var(--color-surface); border-radius: var(--radius);
  border: 1.5px solid var(--color-lime-ink);
  padding: 1.25rem 1.25rem 1.1rem; display: grid; gap: 0.6rem;
}
.next-job__when { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.75rem; }
.next-job__day { font-weight: 700; font-size: 1.3rem; letter-spacing: -0.01em; }
.next-job__time { font-family: var(--font-mono); font-size: 1.05rem; }
.next-job__in { font-size: 0.85rem; color: var(--color-muted); }
.next-job__where { margin: 0; font-family: var(--font-body); font-size: 1.15rem; line-height: 1.3; }
.next-job__notes { margin: 0; display: grid; gap: 0.35rem; font-size: 0.9rem; }
.next-job__notes div { display: grid; grid-template-columns: 6.5rem 1fr; gap: 0.5rem; }
.next-job__notes dt { color: var(--color-muted); }
.next-job__notes dd { margin: 0; }
.next-job__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.3rem; }
.next-job__actions .btn { flex: 1 1 10rem; justify-content: center; }
.next-job__actions .icon { width: 1rem; height: 1rem; }

.dash-list { list-style: none; margin: 0; padding: 0; background: var(--color-surface); border-radius: var(--radius-sm); overflow: hidden; }
.dash-list li + li { border-top: 1px solid color-mix(in srgb, var(--color-navy) 8%, transparent); }
.dash-list a {
  display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) auto; gap: 0.75rem; align-items: center;
  padding: 0.75rem 1rem; text-decoration: none; color: inherit;
}
.dash-list a:hover { background: color-mix(in srgb, var(--color-lime) 12%, var(--color-surface)); }
.dash-list__when { font-family: var(--font-mono); font-size: 0.8rem; color: var(--color-muted); }
.dash-list__what { font-weight: 600; font-size: 0.95rem; min-width: 0; }
.dash-list__what small { display: block; font-weight: 400; color: var(--color-muted); font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-list__pay { font-family: var(--font-mono); font-weight: 600; }
.dash-list .dash-list__day {
  padding: 0.5rem 1rem; font-size: 0.78rem; font-weight: 600;
  background: color-mix(in srgb, var(--color-navy) 4%, var(--color-surface));
}
.pill--bad { background: color-mix(in srgb, var(--color-alert) 14%, var(--color-surface)); color: var(--color-alert); border-radius: var(--radius-pill); padding: 0.1rem 0.55rem; font-size: 0.75rem; font-weight: 600; }

.dash__side { display: grid; gap: 1.25rem; }
.dash-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
.dash-stat {
  display: grid; gap: 0.2rem; padding: 0.9rem 1rem; text-decoration: none; color: inherit;
  background: var(--color-surface); border-radius: var(--radius-sm);
}
a.dash-stat:hover { outline: 1.5px solid var(--color-lime-ink); }
.dash-stat__label { font-size: 0.78rem; color: var(--color-muted); }
.dash-stat__value { font-family: var(--font-mono); font-size: 1.35rem; font-weight: 600; }
.dash-stat__value small { font-family: var(--font-body); font-size: 0.78rem; font-weight: 400; color: var(--color-muted); }

.dash-quote {
  margin: 0; padding: 1rem 1.1rem; background: var(--color-surface);
  border-radius: var(--radius-sm); border-left: 4px solid var(--color-lime);
}
.dash-quote p { margin: 0 0 0.4rem; font-size: 0.95rem; }
.dash-quote footer { font-size: 0.8rem; color: var(--color-muted); }

.dash-board__head { display: flex; justify-content: space-between; align-items: baseline; }
.dash-board__head a { font-size: 0.85rem; }

.board-day {
  display: flex; align-items: baseline; gap: 0.6rem;
  font-family: var(--font-body); font-size: 1.05rem; font-weight: 700; margin: 1.75rem 0 0.75rem;
}
.board-day:first-of-type { margin-top: 0.5rem; }
.board-day span { font-weight: 400; font-size: 0.85rem; color: var(--color-muted); }
.job-card--overdue { border-color: var(--color-alert); }

/* ---------- Earnings ---------- */
/* Cards inside the portal. The public .card is an image tile with its body in
   a wrapper; here a card is a plain white panel with a heading row, and the
   Earnings page drew flush to its edges without this. */
.portal-main .card {
  display: block; background: var(--color-surface); border: 0;
  border-radius: var(--radius-sm); padding: 1.1rem 1.25rem; overflow: visible;
}
.portal-main .card:hover { border-color: transparent; }
.portal-main .card__head {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  margin: 0 0 0.75rem;
}
.portal-main .card__title { font-family: var(--font-body); font-size: 1.05rem; font-weight: 700; }
.portal-main .summary__lines { list-style: none; margin: 0; padding: 0; }
.portal-main .summary__lines li {
  display: flex; justify-content: space-between; gap: 1rem; padding: 0.55rem 0;
  border-top: 1px solid color-mix(in srgb, var(--color-navy) 8%, transparent); font-size: 0.93rem;
}
.portal-main .summary__lines li:first-child { border-top: 0; }
.portal-main .summary__lines li span:last-child { font-family: var(--font-mono); }
.portal-main .payment + .payment { border-top: 1px solid color-mix(in srgb, var(--color-navy) 8%, transparent); }
.portal-main .payment summary { cursor: pointer; padding: 0.6rem 0; }
.dash-stats--four { margin-bottom: 1rem; }
@media (min-width: 800px) { .dash-stats--four { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.dash-stat--lead { border: 1.5px solid var(--color-lime-ink); }
.dash-stat__note { font-size: 0.78rem; color: var(--color-muted); }
.earnings__hold { margin: 0 0 1rem; }
.earnings__chart {
  list-style: none; margin: 1rem 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 0.4rem;
  height: 11rem; align-items: end;
}
.earnings__chart li { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; gap: 0.3rem; min-width: 0; }
.earnings__track { flex: 1; width: 100%; display: flex; align-items: flex-end; justify-content: center; }
.earnings__bar {
  width: 100%; max-width: 3rem; min-height: 2px; border-radius: 4px 4px 0 0;
  background: color-mix(in srgb, var(--color-navy) 22%, var(--color-surface));
}
.earnings__bar.is-now { background: var(--color-lime-ink); }
.earnings__amount { font-family: var(--font-mono); font-size: 0.68rem; color: var(--color-muted); white-space: nowrap; }
.earnings__week { font-size: 0.68rem; color: var(--color-muted); white-space: nowrap; }

.btn--block { width: 100%; justify-content: center; }
.btn--quiet {
  background: none; border-color: color-mix(in srgb, var(--color-navy) 20%, transparent);
  color: var(--color-muted); font-size: 0.88rem; padding: 0.6rem 1.1rem;
}

/* Directions and Call, side by side and large. */
.job-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; margin-bottom: 1.25rem; }
.job-actions .btn { justify-content: center; padding: 0.95rem 1rem; }

.panel {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  border-radius: var(--radius); padding: 1.15rem; margin-bottom: 1rem;
}
.panel h2 {
  font-family: var(--font-body); font-weight: 700; font-size: 0.95rem;
  letter-spacing: 0.01em; margin: 0 0 0.85rem;
  display: flex; align-items: center; gap: 0.5rem;
}
.panel--access { border-left: 4px solid var(--color-lime-ink); }

/* The checklist on the job sheet: folded, with the heading as the handle. */
.checklist summary { cursor: pointer; list-style: none; }
.checklist summary::-webkit-details-marker { display: none; }
.checklist summary h2 { margin: 0; }
.checklist summary h2::after { content: "+"; margin-left: auto; font-weight: 400; color: var(--color-muted); }
.checklist[open] summary h2::after { content: "\2212"; }
.checklist[open] summary { margin-bottom: 0.85rem; }
.checklist__area { font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-lime-ink); margin: 0.9rem 0 0.35rem; }
.checklist__items { margin: 0; padding-left: 1.1rem; font-size: 0.9rem; display: grid; gap: 0.25rem; }

.facts { display: grid; gap: 0.7rem; margin: 0; }
.facts > div { display: grid; grid-template-columns: 7.5rem 1fr; gap: 0.75rem; align-items: baseline; }
.facts dt {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--color-muted);
}
.facts dd { margin: 0; font-size: 0.97rem; }
@media (max-width: 460px) { .facts > div { grid-template-columns: 1fr; gap: 0.1rem; } }

.steps { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin: 0 0 1.25rem; padding: 0; }
.steps__item { display: flex; align-items: center; gap: 0.45rem; font-size: 0.86rem; color: var(--color-muted); }
.steps__item span {
  width: 1.5rem; height: 1.5rem; flex: none; display: grid; place-items: center;
  border-radius: 50%; font-family: var(--font-mono); font-size: 0.74rem;
  background: color-mix(in srgb, var(--color-navy) 9%, transparent);
}
.steps__item.is-now { color: var(--color-navy); font-weight: 600; }
.steps__item.is-now span { background: var(--color-lime); color: var(--color-navy); }
.steps__item.is-done span { background: var(--color-navy); color: var(--color-paper); }

.photo-block__count {
  font-family: var(--font-mono); font-size: 0.72rem; color: var(--color-muted);
  background: color-mix(in srgb, var(--color-navy) 8%, transparent);
  border-radius: var(--radius-pill); padding: 0.05rem 0.5rem;
}
.photo-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr)); gap: 0.55rem; margin-bottom: 1rem; }
.photo-strip figure { margin: 0; }
.photo-strip img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); background: var(--color-paper-alt); }
.photo-strip figcaption { font-size: 0.74rem; color: var(--color-muted); margin-top: 0.25rem; }

.upload { display: grid; gap: 0.65rem; }

/* The file input is the whole tile. On a phone this opens the camera. */
.camera {
  position: relative; display: grid; place-items: center; gap: 0.2rem;
  border: 2px dashed color-mix(in srgb, var(--color-navy) 22%, transparent);
  border-radius: var(--radius); padding: 1.5rem 1rem; cursor: pointer;
  background: color-mix(in srgb, var(--color-lime) 6%, var(--color-surface));
  text-align: center; margin: 0;
}
.camera:hover { border-color: var(--color-lime-ink); }
.camera input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.camera__icon { display: inline-flex; line-height: 1; color: var(--color-lime-ink); }
.camera__icon .icon { width: 1.9rem; height: 1.9rem; vertical-align: 0; }
.camera__label { font-weight: 600; font-size: 1rem; }
.camera__hint { font-size: 0.8rem; color: var(--color-muted); }

.finish { margin-top: 1.5rem; }
.btn--finish { padding: 1.05rem 1.5rem; font-size: 1rem; }
.job-release { margin-top: 2rem; text-align: center; }
.stars { color: var(--color-lime-ink); letter-spacing: 0.15em; margin: 0; }

.empty {
  border: 1px dashed color-mix(in srgb, var(--color-navy) 20%, transparent);
  border-radius: var(--radius); padding: 2.5rem 1.5rem; text-align: center;
  background: var(--color-surface);
}
.empty p { margin: 0 0 0.35rem; }

/* Login is the only thing between a cleaner and the board, so it is one card. */
body.portal--login { display: grid; place-items: center; min-height: 100vh; padding: 1.5rem; }
.login-card {
  width: min(24rem, 100%); background: var(--color-surface);
  border-radius: var(--radius); padding: 2rem 1.75rem;
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
}
/* Centred, and big enough to read.

   It was 52px square with the size written into the tag, which was fine while
   the file was a square favicon shipped with the site. The mark is uploadable
   now, and a real logo is wider than it is tall - squeezed into a 52px box it
   came out as a smudge in the top-left corner of the card. Height-led with a
   free width, so a wide wordmark and a square badge both land the same size
   to the eye. */
.login-card__logo {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 4.5rem;
  object-fit: contain;
  margin: 0 auto 1.5rem;
}
.login-card h1 { font-family: var(--font-body); font-weight: 700; font-size: 1.5rem; margin: 0 0 0.2rem; }
.login-card .form { margin-top: 1.5rem; gap: 1rem; }

/* ---------- Paying ----------
   The last page before money moves. The amount and the two buttons sit
   together in one column so the decision is never split across a scroll, and
   the cancellation terms sit beside them rather than under a "terms" link,
   because a customer agreeing to them should have read them. */

.pay {
  display: grid; gap: 1.5rem; align-items: start;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) { .pay { grid-template-columns: minmax(0, 1.35fr) minmax(20rem, 1fr); gap: 2rem; } }

.pay__main { display: grid; gap: 1rem; }
.pay__side {
  display: grid; gap: 0.75rem;
  position: sticky; top: 1.5rem;
}
@media (max-width: 899px) { .pay__side { position: static; } }

.pay__total {
  background: var(--color-navy); color: var(--color-paper);
  border-radius: var(--radius); padding: 1.4rem 1.5rem;
}
.pay__label {
  margin: 0 0 0.2rem; font-family: var(--font-mono);
  font-size: 0.68rem; letter-spacing: 0.09em; text-transform: uppercase;
  opacity: 0.75;
}
.pay__amount {
  margin: 0; font-family: var(--font-mono); font-weight: 500;
  font-size: clamp(2.2rem, 6vw, 2.8rem); line-height: 1;
}
.pay__rest { margin: 0.6rem 0 0; font-size: 0.85rem; opacity: 0.8; }

.pay__talk {
  border: 1px dashed color-mix(in srgb, var(--color-navy) 25%, transparent);
  border-radius: var(--radius); padding: 1.1rem 1.2rem;
  background: color-mix(in srgb, var(--color-lime) 7%, var(--color-surface));
  margin-top: 0.5rem;
}
.pay__talk h2 {
  font-family: var(--font-body); font-weight: 700; font-size: 0.98rem;
  margin: 0 0 0.4rem; text-wrap: balance;
}
.pay__talk p { margin: 0 0 0.75rem; font-size: 0.9rem; color: var(--color-muted); }
.pay__talk-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; }
.pay__talk-actions .btn { flex: 1 1 auto; justify-content: center; }

/* The cancellation terms are a docket too, styled with the guarantee. */

.numbered { margin: 0; padding-left: 1.2rem; display: grid; gap: 0.5rem; }
.numbered li { font-size: 0.95rem; }

.mono { font-family: var(--font-mono); font-size: 1.02rem; letter-spacing: 0.02em; }
/* ---------- Quote calculator ---------- */

.quote {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.6fr);
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
}
@media (max-width: 900px) { .quote { grid-template-columns: 1fr; } }

.quote__build { display: grid; gap: 2.25rem; }

.quote__step { border: 0; padding: 0; margin: 0; }
.quote__step legend {
  display: flex; align-items: center; gap: 0.7rem;
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 600;
  margin-bottom: 1rem; padding: 0;
}
.quote__num {
  width: 1.9rem; height: 1.9rem; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--color-lime); color: var(--color-navy);
  font-family: var(--font-mono); font-size: 0.85rem;
}

.choice-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: 0.85rem;
}
.choice-cards--row { grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr)); }

.choice-card {
  position: relative;
  display: flex; flex-direction: column; gap: 0.35rem;
  border: 1.5px solid color-mix(in srgb, var(--color-navy) 15%, transparent);
  border-radius: var(--radius);
  background: var(--color-surface);
  padding: 1.1rem 1.1rem 1rem;
  cursor: pointer; margin: 0; font-weight: 400;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.choice-card:hover { border-color: color-mix(in srgb, var(--color-lime-ink) 50%, transparent); }
.choice-card.is-chosen {
  border-color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 12%, var(--color-surface));
}
.choice-card input { position: absolute; opacity: 0; pointer-events: none; }
.choice-card--slim { align-items: center; text-align: center; padding: 0.9rem 0.75rem; }

.choice-card__badge {
  align-self: flex-start;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--color-navy);
  background: var(--color-lime); border-radius: var(--radius-pill);
  padding: 0.15rem 0.55rem; margin-bottom: 0.2rem;
}
.choice-card__name { font-weight: 600; font-size: 1.02rem; }
.choice-card__blurb { font-size: 0.9rem; color: var(--color-muted); line-height: 1.45; }
.choice-card__uplift {
  font-style: normal; font-family: var(--font-mono); font-size: 0.75rem;
  color: var(--color-alert); margin-left: 0.35rem;
}

.counters, .addons { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }

.counter, .addon {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  border-radius: var(--radius-sm);
  padding: 0.7rem 0.9rem;
}
.counter__name, .addon__name { font-size: 0.98rem; }
.addon__name em { font-style: normal; font-family: var(--font-mono); font-size: 0.82rem; color: var(--color-lime-ink); }
.addon label { display: flex; align-items: center; gap: 0.6rem; margin: 0; font-weight: 400; width: 100%; }
.addon input[type="checkbox"] { width: 1.15rem; height: 1.15rem; accent-color: var(--color-lime-ink); }

.counter__controls { display: flex; align-items: center; gap: 0.4rem; flex: none; }
.counter__btn {
  width: 2.3rem; height: 2.3rem; flex: none;
  border: 1px solid color-mix(in srgb, var(--color-navy) 20%, transparent);
  border-radius: 50%; background: var(--color-surface);
  font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.counter__btn:hover { border-color: var(--color-lime-ink); background: color-mix(in srgb, var(--color-lime) 18%, var(--color-surface)); }
.counter__controls input[type="number"] {
  width: 3rem; text-align: center; font-family: var(--font-mono); font-size: 1rem;
  border: 0; background: none; padding: 0.35rem 0;
  -moz-appearance: textfield;
}
.counter__controls input::-webkit-outer-spin-button,
.counter__controls input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* "Other", as a tick-list of areas rather than a count and a box to type in.
   A column rather than the counter's row, because the list is the control. */
.counter--areas { display: block; }
.area-picker { border: 0; margin: 0; padding: 0; min-width: 0; }
/* Three classes deep, because `.quote__step legend` (0,1,1) is the big
   numbered step heading and would otherwise turn this into another one. */
.quote__step .area-picker legend {
  display: block; font-family: inherit; font-size: 0.98rem; font-weight: 400;
  margin-bottom: 0.6rem;
}
.area-picker__grid {
  display: grid; gap: 0.45rem 1rem;
  grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
}
.area-picker__option {
  display: flex; align-items: center; gap: 0.55rem;
  margin: 0; font-weight: 400; font-size: 0.93rem; cursor: pointer;
}
.area-picker__option input { width: 1.1rem; height: 1.1rem; flex: none; accent-color: var(--color-lime-ink); }

/* Pets: two tickboxes on a row, then a box for anything else. */
.pets__label { display: block; font-weight: 600; margin-bottom: 0.35rem; }
.pets__options { margin-bottom: 0.6rem; }
.pets__options > div { display: flex; flex-wrap: wrap; gap: 1.1rem; }
.pets__options label { display: flex; align-items: center; gap: 0.5rem; margin: 0; font-weight: 400; }
.pets input[type="checkbox"] { width: 1.15rem; height: 1.15rem; accent-color: var(--color-lime-ink); }
.pets__other { display: flex; align-items: center; gap: 0.75rem; margin: 0; font-weight: 400; }
.pets__other > span { flex: none; color: var(--color-muted); font-size: 0.9rem; }
.pets__other .field-input { flex: 1; min-width: 0; }

/* The one fact somebody opens the order page for: has the money arrived. */
.order-status {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem;
  margin: 0 0 1rem; padding: 0.7rem 0.9rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-navy) 5%, transparent);
}
.order-status .icon { width: 1.4rem; height: 1.4rem; flex: none; }
.order-status strong { font-size: 1.05rem; }
.order-status span { color: var(--color-muted); font-size: 0.92rem; }
.order-status.is-paid {
  background: color-mix(in srgb, var(--color-lime) 22%, var(--color-surface));
  color: var(--color-lime-ink);
}
.order-status.is-paid span { color: inherit; }

/* Stated, never added: the total already includes it. */
.summary__gst, .pay__gst {
  margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--color-muted);
}

/* The provider's payment form, embedded. The frame is theirs; the box around
   it is ours, which is the whole point of doing it this way. */
.pay-embed {
  max-width: 34rem; margin: 0 auto;
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent);
  border-radius: var(--radius); overflow: hidden;
  padding: 0.9rem;
}
.pay-embed__status { margin: 0; padding: 1.4rem 0; text-align: center; color: var(--color-muted); }
.pay-embed__status:empty { display: none; padding: 0; }
.pay-embed__frame {
  display: block; width: 100%; border: 0;
  /* Their library sets the height itself from the size its page reports, so
     there is no fixed height here to fight it with. Small until it does, so
     an unloaded form is a modest box rather than a screen of nothing. */
  height: 4rem;
  /* Hidden only until the browser says their page has loaded. Deliberately
     not until their own initialised handshake: that message is dropped on an
     exact URL comparison, and gating on it showed a blank box forever. */
  visibility: hidden;
}
.pay-embed.is-ready .pay-embed__frame { visibility: visible; min-height: 24rem; }
.pay-embed.is-expired .pay-embed__frame { display: none; }

/* Waiting for the answer, after their form says it has finished.
   The page no longer navigates on that event -- it asks our server and waits
   for "paid" or "failed" -- so this is the state a customer is looking at
   while the money moves. Without something to show, "Confirming your
   payment…" in muted grey reads as a page that has stopped. Their frame stays
   where it is underneath, because their own receipt is reassuring. */
.pay-embed.is-waiting .pay-embed__status {
  color: var(--color-navy);
  font-weight: 600;
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
}
.pay-embed.is-waiting .pay-embed__status::before {
  content: ""; width: 1rem; height: 1rem; flex: none;
  border: 2px solid color-mix(in srgb, var(--color-navy) 25%, transparent);
  border-top-color: var(--color-navy);
  border-radius: 50%;
  animation: pay-embed-spin 0.7s linear infinite;
}
/* Somebody who has asked not to see motion gets a still ring rather than
   nothing at all: the point is that the page is working, not the spin. */
@media (prefers-reduced-motion: reduce) {
  .pay-embed.is-waiting .pay-embed__status::before { animation: none; }
}
@keyframes pay-embed-spin { to { transform: rotate(360deg); } }

.pay-embed.is-done .pay-embed__status { color: var(--color-navy); }
.pay-embed.is-done .pay-embed__status::before { display: none; }

/* A bounced payment. The page stays here rather than sending somebody who has
   just been declined somewhere else to press another button, so this is both
   the bad news and the way out of it. */
.pay-embed.is-failed .pay-embed__status { color: var(--color-alert); font-weight: 600; }
.pay-embed__retry {
  display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center;
  margin: 0 0 0.4rem;
}
/* `.pay-embed__retry` sets `display`, which beats the browser's own [hidden]
   rule, so without this the retry buttons show on every payment page before
   anybody has tried to pay. The same trap put an unasked-for surcharge in
   front of every customer once already. */
.pay-embed__retry[hidden] { display: none; }

/* The map beside the address. Appears only once an address has been chosen,
   so it is never an empty grey box asking what it is for. */
.address-map {
  height: 14rem;
  margin-top: 0.7rem;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  /* Leaflet's own panes sit at z-index 400 and up, which would otherwise put
     the map over the address suggestions dropdown above it. `position` is
     what makes the z-index apply at all, and what boxes Leaflet's 400 into a
     stacking context of its own so it cannot escape past the dropdown's 20. */
  position: relative;
  z-index: 0;
}
/* `.address-map` has no `display` of its own, but Leaflet sets one on the
   element as soon as it initialises, and that would beat the browser's
   [hidden] rule. Same trap that once put an unasked-for surcharge in front of
   every customer. */
.address-map[hidden] { display: none; }
.address-map .leaflet-container { border-radius: var(--radius); }

@media (min-width: 60rem) {
  .address-map { height: 16rem; }
}

/* The promo code, on the booking page directly under the address, because the
   address is what decides whether an area-restricted code applies. */
.promo-field { margin-top: 0.9rem; }
.promo-field__row { display: flex; gap: 0.6rem; max-width: 26rem; }
.promo-field__row .btn { flex: none; }
.promo-field input { text-transform: uppercase; }
.promo-field .field-help.is-error { color: var(--color-alert); }

/* The running total. Sticky on a wide screen so it is always in view. */
.quote__summary { position: sticky; top: 6rem; display: grid; gap: 1rem; }
@media (max-width: 900px) { .quote__summary { position: static; } }

.summary {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  border-radius: var(--radius);
  padding: 1.4rem;
  transition: opacity 0.15s ease;
}
.summary.is-stale { opacity: 0.5; }

.summary__label {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--color-muted); margin: 0 0 0.9rem;
}
.summary__meta { display: grid; gap: 0.45rem; margin: 0 0 1rem; padding-bottom: 1rem;
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 10%, transparent); }
.summary__meta > div { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.9rem; }
.summary__meta dt { color: var(--color-muted); }
.summary__meta dd { margin: 0; font-weight: 600; }

.summary__lines { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.4rem; }
.summary__lines li { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.92rem; }
/* Any rule that sets `display` beats the browser's own `[hidden]` rule, so a
   line marked hidden shows anyway. That put "Urgent booking (+5%)" on every
   booking page whether or not anybody had ticked the box -- a surcharge the
   customer could see and had not agreed to. */
.summary__lines li[hidden] { display: none; }
.summary__lines .is-discount { color: var(--color-lime-ink); }
.summary__empty { color: var(--color-muted); font-style: italic; }

.summary__total {
  display: flex; align-items: baseline; justify-content: flex-end; gap: 0.7rem;
  padding-top: 0.9rem;
  border-top: 1px solid color-mix(in srgb, var(--color-navy) 14%, transparent);
}
.summary__total strong { font-family: var(--font-display); font-size: 2rem; line-height: 1; }
.summary__was { font-size: 1rem; color: var(--color-muted); text-decoration: line-through; }

.summary__time { font-family: var(--font-mono); font-size: 0.8rem; color: var(--color-muted); margin: 0.5rem 0 0; text-align: right; }
.summary__error { font-size: 0.85rem; color: var(--color-alert); margin: 0.6rem 0 0; }
.summary__note { font-size: 0.82rem; color: var(--color-muted); margin: 1rem 0 0; }

/* ---------- Accepted payment methods ----------
   Renders uploaded artwork where it exists and a text chip where it does not,
   so the strip looks deliberate before any logo has been supplied rather than
   like something half-built. */

.accepted { margin-top: 1.25rem; }
.accepted__label {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin: 0 0 0.7rem;
}
.accepted__row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
}
.accepted__item {
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  border-radius: var(--radius-sm);
  /* Fixed box, mark centred inside it. The logos are wildly different shapes,
     so sizing the chip to its contents gave a ragged row of different heights
     and the marks themselves came out too small to read. */
  width: 4.5rem;
  height: 3rem;
  padding: 0.4rem 0.55rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* height, not max-height.
   max-height only shrinks something already too big, so a logo whose file is
   intrinsically small just stayed small: Google Pay and PayID sat noticeably
   tinier than Visa and Amex in identical boxes. Setting the height normalises
   every mark to the same optical size whatever it arrived as, and max-width
   keeps a very wide one from bursting the box. */
.accepted__item img {
  /* Unitless, divided here. The template used to emit "100%" straight into
     this multiplication, which calc() rejects - a length times a percentage
     is not a thing - so the declaration was dropped and every logo rendered
     at its natural size, spilling its baked-in white background out past the
     chip's rounded corners. */
  height: calc(1.85rem * var(--logo-scale, 100) / 100);
  width: auto;
  max-width: 100%;
  display: block;
  object-fit: contain;
}
/* Reversed artwork gets a dark chip, so a white-on-dark mark sits as one of
   the row rather than reading as a black hole punched in it. The light variant
   is tidier where the brand publishes one. */
.accepted__item--dark {
  background: var(--color-navy);
  border-color: var(--color-navy);
}

/* The text fallback. Reads as a considered chip, not a missing image, and is
   allowed to size to its own words rather than being squeezed into the box a
   logo wants. */
.accepted__item--text {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  color: var(--color-navy);
  width: auto;
  min-width: 4.5rem;
  padding-inline: 0.75rem;
  text-align: center;
}

/* Centred where the strip is a section of its own, as on the home page. Beside
   the pay buttons it stays left, lined up with everything else in that column. */
.accepted--centred { text-align: center; }
.accepted--centred .accepted__row { justify-content: center; }

/* A small qualifier beside a heading, e.g. how fast PayID clears. */
.badge {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 22%, transparent);
  border-radius: var(--radius-pill);
  padding: 0.15rem 0.6rem;
  white-space: nowrap;
}

/* Staff, not customers. Present but not competing with the customer links. */
.footer-links__staff { opacity: 0.6; }
.footer-links__staff:hover { opacity: 1; }

/* The password form on the staff login, folded away under the emailed link. */
.login-alt { margin-top: 1.5rem; }
.login-alt summary {
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--color-muted);
  padding: 0.4rem 0;
}
.login-alt summary:hover { color: var(--color-navy); }
.login-alt[open] summary { margin-bottom: 0.5rem; }
.login-card__foot { margin-top: 1.5rem; }

/* ---------- The office ----------
   Its own screen, not the marketing site and not the Django admin. The admin
   lists tables; this lists work. Everything on it is something a person does,
   and the first section is blank on a good day, which is what makes it worth
   opening at all. */

body.office { background: var(--color-paper-alt); }

.office-bar { background: var(--color-navy); color: var(--color-paper); }
.office-bar__inner {
  width: min(1240px, 100% - 2.5rem); margin-inline: auto;
  display: flex; align-items: center; gap: 1.5rem; padding: 0.75rem 0;
  flex-wrap: wrap;
}
.office-bar__brand {
  display: flex; align-items: center; gap: 0.55rem;
  font-weight: 700; text-decoration: none; color: inherit; letter-spacing: -0.01em;
}
.office-bar__brand img { border-radius: 4px; background: var(--color-paper); padding: 2px; }
.office-bar__brand em {
  font-style: normal; font-family: var(--font-mono); font-size: 0.58rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  background: var(--color-lime); color: var(--color-navy);
  border-radius: var(--radius-pill); padding: 0.1rem 0.5rem;
}
.office-bar__nav { display: flex; gap: 1.1rem; margin-left: auto; flex-wrap: wrap; }
.office-bar__nav a {
  color: var(--color-paper); text-decoration: none; font-size: 0.88rem; opacity: 0.8;
}
.office-bar__nav a:hover { opacity: 1; text-decoration: underline; }
.office-bar__out {
  background: none; border: 0; color: inherit; font: inherit; font-size: 0.88rem;
  opacity: 0.7; cursor: pointer;
}
.office-bar__out:hover { opacity: 1; }

.office-main { width: min(1240px, 100% - 2.5rem); margin-inline: auto; padding: 2rem 0 3rem; }

.office-head { margin-bottom: 1.75rem; }
.office-head h1 {
  font-family: var(--font-display); font-size: clamp(2rem, 4vw, 2.6rem); margin: 0 0 0.1rem;
}
.office-head__sub { margin: 0; color: var(--color-muted); font-family: var(--font-mono); font-size: 0.85rem; }

.office-block { margin-bottom: 2rem; }
.office-block__title {
  font-family: var(--font-mono); font-weight: 500; font-size: 0.7rem;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--color-muted);
  margin: 0 0 0.9rem; display: flex; align-items: center; gap: 0.6rem;
}

.office-cols { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 900px) { .office-cols { grid-template-columns: 1fr 1fr; } }

.office-clear {
  background: var(--color-surface); border-radius: var(--radius);
  border: 1px dashed color-mix(in srgb, var(--color-navy) 18%, transparent);
  padding: 1.5rem; margin: 0; color: var(--color-muted); text-align: center;
}

/* The queue. Each row is one thing to do, with the doing of it on the right. */
.queue { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.queue__item {
  background: var(--color-surface); border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  border-left: 4px solid var(--color-navy-soft);
  padding: 1.1rem 1.25rem;
  display: flex; gap: 1.25rem; align-items: flex-start; flex-wrap: wrap;
}
.queue__item--now { border-left-color: var(--color-alert); }
.queue__item--soon { border-left-color: var(--color-lime-ink); }
.queue__body { flex: 1 1 20rem; }
.queue__body h3 {
  font-family: var(--font-body); font-weight: 700; font-size: 1.02rem; margin: 0 0 0.25rem;
}
.queue__detail { margin: 0 0 0.35rem; font-size: 0.92rem; }
.queue__note { margin: 0; font-size: 0.86rem; color: var(--color-muted); font-style: italic; }
.queue__side {
  display: flex; flex-direction: column; align-items: flex-end; gap: 0.5rem;
  margin-left: auto;
}
.queue__age { font-family: var(--font-mono); font-size: 0.72rem; color: var(--color-muted); }

/* The day's run sheet. */
.runsheet {
  list-style: none; margin: 0; padding: 0;
  background: var(--color-surface); border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  overflow: hidden;
}
.runsheet__item {
  display: flex; align-items: center; gap: 1rem; padding: 0.85rem 1.15rem;
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 8%, transparent);
}
.runsheet__item:last-child { border-bottom: 0; }
.runsheet__time {
  font-family: var(--font-mono); font-size: 0.85rem; flex: none; width: 5rem;
}
.runsheet__where { flex: 1; display: grid; gap: 0.1rem; min-width: 0; }
.runsheet__where strong { font-size: 0.95rem; font-weight: 600; }
.runsheet__where em { font-style: normal; font-size: 0.82rem; color: var(--color-muted); }
.runsheet__state {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.06em;
  text-transform: uppercase; border-radius: var(--radius-pill);
  padding: 0.2rem 0.65rem; flex: none; white-space: nowrap;
  background: color-mix(in srgb, var(--color-navy) 8%, transparent);
}
.runsheet__state--available { background: color-mix(in srgb, var(--color-alert) 12%, transparent); color: var(--color-alert); }
.runsheet__state--in_progress { background: color-mix(in srgb, var(--color-lime) 30%, transparent); color: var(--color-lime-ink); }
.runsheet__state--completed { background: color-mix(in srgb, var(--color-navy) 82%, transparent); color: var(--color-paper); }
.runsheet__state--disputed { background: var(--color-alert); color: var(--color-paper); }

.tally {
  margin: 0; background: var(--color-surface); border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  overflow: hidden;
}
.tally > div {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  padding: 0.7rem 1.15rem;
  border-bottom: 1px solid color-mix(in srgb, var(--color-navy) 8%, transparent);
}
.tally > div:last-child { border-bottom: 0; }
.tally dt { font-size: 0.9rem; color: var(--color-muted); }
.tally dd { margin: 0; font-family: var(--font-mono); font-size: 1.05rem; }

/* The money plumbing. Reports proof, not configuration. */
.systems {
  background: var(--color-surface); border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  padding: 1.25rem 1.35rem;
}
.systems__name {
  font-family: var(--font-body); font-weight: 700; font-size: 1rem;
  margin: 0 0 1rem; display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
}
.systems__verdict {
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.1em;
  text-transform: uppercase; border-radius: var(--radius-pill); padding: 0.15rem 0.6rem;
}
.systems__verdict--ok { background: var(--color-lime); color: var(--color-navy); }
.systems__verdict--bad { background: var(--color-alert); color: var(--color-paper); }
.systems__verdict--manual { background: color-mix(in srgb, var(--color-navy) 10%, transparent); color: var(--color-muted); }

.systems__list { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.7rem; }
.systems__list li {
  display: grid; gap: 0.1rem; padding-left: 1.5rem; position: relative; font-size: 0.9rem;
}
.systems__list li::before {
  content: ""; position: absolute; left: 0; top: 0.42em;
  width: 0.5rem; height: 0.28rem;
  border-left: 2px solid var(--color-lime-ink); border-bottom: 2px solid var(--color-lime-ink);
  transform: rotate(-45deg);
}
.systems__list li.is-bad::before {
  top: 0.55em; width: 0.65rem; height: 0.65rem;
  border: 0; border-radius: 50%; background: var(--color-alert); transform: none;
}
.systems__label { font-weight: 600; }
.systems__detail { color: var(--color-muted); font-size: 0.86rem; }
.systems__fix {
  font-size: 0.84rem; color: var(--color-alert);
  border-left: 2px solid color-mix(in srgb, var(--color-alert) 40%, transparent);
  padding-left: 0.6rem; margin-top: 0.25rem;
}
.systems__note { margin: 0; font-size: 0.85rem; color: var(--color-muted); }

.badge--warn { background: color-mix(in srgb, #A9812E 22%, transparent); color: #7a5c1f; }

/* The banner shown when the calculator restored an earlier selection. */
.resumed {
  background: color-mix(in srgb, var(--color-lime) 16%, var(--color-surface));
  border: 1px solid color-mix(in srgb, var(--color-lime-ink) 30%, transparent);
  border-radius: var(--radius-sm);
  padding: 0.7rem 1rem;
  margin: 0 0 1.25rem;
  font-size: 0.9rem;
}


/* The Urgent job tickbox, directly under the date field.

   Given a border and a background so it reads as an offer rather than a
   footnote: it is the answer to the dead end a customer has just hit, and a
   plain checkbox under a greyed-out calendar gets missed. */
.urgent {
  display: flex;
  align-items: start;
  gap: 0.7rem;
  margin-top: 0.9rem;
  padding: 0.85rem 1rem;
  border: 1px solid color-mix(in srgb, var(--color-navy) 16%, transparent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font-size: 0.88rem;
  line-height: 1.5;
  cursor: pointer;
}
.urgent:hover { border-color: var(--color-lime-ink); }
.urgent input { margin-top: 0.2rem; accent-color: var(--color-lime-ink); flex: none; }
.urgent:has(input:checked) {
  border-color: var(--color-lime-ink);
  background: color-mix(in srgb, var(--color-lime) 12%, transparent);
}
.urgent strong { display: block; }

/* "or send it yourself", between the hosted payment page and the manual PayID.
   A rule with a word in it, so the two are read as alternatives rather than
   as steps one after the other. */
.or-line {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 1.25rem 0;
  font-size: 0.8rem;
  color: var(--color-muted);
  text-transform: lowercase;
}
.or-line::before,
.or-line::after {
  content: "";
  flex: 1;
  height: 1px;
  background: color-mix(in srgb, var(--color-navy) 14%, transparent);
}
.btn--block { display: block; width: 100%; text-align: center; }


/* The payment methods under the Pay Now button.

   Bright blue and bold on purpose: it is the line that answers "can I pay the
   way I want to", and a customer who cannot see the answer does not press the
   button to find out. Blue rather than the site's navy or lime, because those
   read as furniture here and this has to read as information. */
.pay__methods {
  margin: 0.6rem 0 0.5rem;
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1.5;
  text-align: center;
  color: #0B63E5;
  letter-spacing: 0.005em;
}
/* Dark mode is not a thing this site has, but a bright blue on a dark surface
   is unreadable if it ever becomes one, so the colour is stated against the
   surface it sits on rather than assumed. */
@media (prefers-color-scheme: dark) {
  .pay__methods { color: #6FA8FF; }
}


/* ---------- A thumb, not a mouse pointer ----------

   Measured rather than guessed at: on a 390px screen the counter buttons were
   37px, the footer links 18px and the slider dots 3px tall. Apple and Google
   both put the floor around 44px, and 18px is a link you hit by luck.

   Nothing here changes how any of it looks on a laptop. The dots stay 3px --
   they grow an invisible pad around them, because a bigger dot is a worse dot
   and a bigger target is a better target.
   Placed at the end of this file on purpose. Every rule it overrides is
   defined further down than where this block started out, and at equal
   specificity the last one wins -- so the first version of this changed
   nothing at all on the two pages that needed it most.
*/
@media (max-width: 780px) {
  /* The calculator's main interaction. Missing one of these adds a bedroom. */
  .counter__btn { width: 2.9rem; height: 2.9rem; font-size: 1.25rem; }

  /* Footer columns: one tap each, comfortably apart. */
  .footer-links a {
    display: inline-block;
    padding: 0.55rem 0;
    min-height: 44px;
    line-height: 1.6;
  }

  /* The same tap target, but no `display` of its own. These rows lay their
     icon and value out with `gap`, and `gap` does nothing on an inline-block:
     setting one here left every icon jammed against its text on a phone while
     the location line, which is not a link and so was never matched, kept its
     spacing and looked different from the two above it. */
  .footer-contact a {
    padding: 0.55rem 0;
    min-height: 44px;
    line-height: 1.6;
  }

  /* "Cleaning in Officer →", "What's involved →" -- the link that is the
     whole point of a card. */
  .card__link {
    display: inline-block;
    padding: 0.5rem 0;
    min-height: 44px;
  }

  /* Links in a list are still links. These are the service names on the home
     page and the suburbs beside them -- 19px tall, hit by luck. */
  .track__list a,
  .suburbs a {
    display: inline-block;
    padding: 0.6rem 0;
    min-height: 44px;
    line-height: 1.6;
  }

  /* The dot stays a dot; the hit area does not. */
  .slider__dot {
    padding: 1.35rem 0;
    background-clip: content-box;
  }
  .slider__dots { gap: 0.35rem; }

  /* The smallest text on the site was 9.6px. Body copy gets a floor of
     12.8px; the uppercase mono labels get 11.5px, which is legible at that
     weight and spacing and is the voice the brand is written in -- making
     every eyebrow the size of a paragraph would say something different. */
  .field-help,
  .summary__time,
  .summary__note,
  .footer-blurb,
  .footer-legal { font-size: 0.8rem; }

  .choice-card__badge,
  .summary__label,
  .accepted__label,
  .guarantee__label { font-size: 0.72rem; }
}


/* ---------------------------------------------------------------------------
   Help centre.

   Appended after the site-wide mobile block deliberately. Every selector here
   is a .help class that exists on one page, so there is nothing above for it
   to fight with, and its own narrow-screen rules sit at the bottom where the
   note further up this file says they have to be.

   The contents rail is sticky on a wide screen and a plain list on a narrow
   one. A sticky sidebar shorter than the viewport is useful; the same thing
   on a phone is a lump of links between the reader and the answer.
--------------------------------------------------------------------------- */
.help {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.help__contents {
  font-size: 0.9rem;
}

.help__contents-title {
  font-family: var(--font-mono, monospace);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.72rem;
  opacity: 0.7;
  margin-bottom: 0.75rem;
}

.help__contents ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.help__contents a {
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.help__contents a:hover,
.help__contents a:focus-visible {
  border-bottom-color: currentColor;
}

.help__section + .help__section {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}

/* Room for the fixed header, so clicking a contents link does not park the
   heading underneath it. */
.help__section {
  scroll-margin-top: 6rem;
}

.help__section h2 {
  margin-bottom: 1rem;
}

.help__section h3 {
  margin-top: 2rem;
  margin-bottom: 0.6rem;
}

.help__section p + p,
.help__section ul + p,
.help__section ol + p,
.help__section p + ul,
.help__section p + ol {
  margin-top: 1rem;
}

.help__list,
.help__steps {
  display: grid;
  gap: 0.75rem;
  padding-left: 1.2rem;
}

.help__list { list-style: disc; }
.help__steps { list-style: decimal; }

.help__list li,
.help__steps li { line-height: 1.6; }

.help__table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
}

.help__table th,
.help__table td {
  text-align: left;
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}

.help__table th {
  font-family: var(--font-mono, monospace);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  opacity: 0.7;
}

/* The bank-details warning. Loud on purpose: invoice fraud works by looking
   ordinary, so the one paragraph telling people we never change our account
   details by email cannot read like the paragraphs around it. */
.help__warn {
  margin-top: 1rem;
  padding: 1rem 1.15rem;
  border-left: 3px solid var(--color-accent, currentColor);
  background: color-mix(in srgb, currentColor 5%, transparent);
  border-radius: 0 var(--radius-sm, 6px) var(--radius-sm, 6px) 0;
}

@media (min-width: 900px) {
  .help {
    grid-template-columns: minmax(12rem, 15rem) minmax(0, 1fr);
  }

  .help__contents {
    position: sticky;
    top: 6rem;
    max-height: calc(100vh - 8rem);
    overflow-y: auto;
  }
}

@media (max-width: 899px) {
  .help__contents {
    padding-bottom: 1.5rem;
    border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
  }

  .help__contents ol {
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  }
}


/* ---------------------------------------------------------------------------
   Booking form: two steps, and the map popup.

   Appended at the end for the same reason the help centre block is: the note
   further up this file about equal specificity and last-rule-wins.

   Everything here is inert until site.js adds `is-stepped`. That is the whole
   no-JavaScript story - without it the steps bar is not drawn, both panels
   stay open, and the form is the single column it has always been.
--------------------------------------------------------------------------- */
.steps-bar { display: none; }

.form.is-stepped .steps-bar {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  margin: 0 0 1.75rem;
  padding: 0;
}

.steps-bar li {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm, 6px);
  font-size: 0.85rem;
  background: color-mix(in srgb, currentColor 6%, transparent);
  opacity: 0.55;
}

.steps-bar li span {
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  font-size: 0.75rem;
  font-weight: 600;
  background: color-mix(in srgb, currentColor 15%, transparent);
}

.steps-bar li.is-current,
.steps-bar li.is-done { opacity: 1; }

.steps-bar li.is-current span {
  background: var(--color-accent, currentColor);
  color: var(--color-ink, #fff);
}

/* The nav rows are swapped by the stepper, not by CSS, so that a form with no
   JavaScript shows the submit button and nothing else odd. */
.step-nav { margin-top: 1.5rem; }


@media (max-width: 600px) {
  .steps-bar li {
    font-size: 0.78rem;
    padding: 0.5rem 0.55rem;
  }
}


/* ---------------------------------------------------------------------------
   A section heading whose two lines are each meant to stay on one line.

   The default .section-head caps at 46ch and .lede at 58ch. That is the right
   measure for reading a paragraph, and it is what broke the instant quote
   heading into three lines. Here both caps come off and the type is sized in
   vw so that nowrap can never push the page sideways.

   Above 1024px only. "Price it yourself, in under a minute." is 37 characters
   and a phone cannot hold that on one line at any size that still looks like
   a heading, so below the breakpoint it wraps as before. A heading that fits
   by being unreadable is not a heading.
--------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .section-head--oneline { max-width: none; }

  .section-head--oneline h1,
  .section-head--oneline .lede {
    max-width: none;
    white-space: nowrap;
    /* text-wrap: balance on headings does nothing once nothing wraps, but it
       is inherited, so it is cleared rather than left to look meaningful. */
    text-wrap: nowrap;
  }

  /* Sized off the viewport rather than fixed, so the line shrinks to fit
     between 1024px and the 1180px the wrap tops out at instead of spilling.
     The min() keeps it from growing past the normal h1 size on a wide screen. */
  .section-head--oneline h1 { font-size: min(3.7rem, 4.35vw); }
  .section-head--oneline .lede { font-size: min(1.2rem, 1.42vw); }
}

/* --- the map under the address ---
   Shown by the lookup the moment an address is chosen, and never before: the
   iframe has no src until then, so a page nobody books from costs no map
   load. There used to be a button in front of it, which nobody would have
   pressed. */
.address-map {
  margin: 0.9rem 0 0;
  border-radius: var(--radius-sm, 6px);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}

/* A shape rather than a height, so it is the same map on a phone as on a
   desktop instead of turning into a letterbox. 16/10 is enough sky to see the
   house and its neighbours without pushing the rest of the form off screen. */
.address-map iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border: 0;
}

.address-map figcaption {
  margin: 0;
  padding: 0.55rem 0.7rem;
  background: color-mix(in srgb, currentColor 4%, transparent);
}

@media (max-width: 600px) {
  /* Squarer on a phone: a wide strip of a satellite image at this width shows
     the roof and nothing either side of it. */
  .address-map iframe { aspect-ratio: 4 / 3; }
}

/* In the quote box, where the last step has to fit without scrolling: a
   fixed short strip, enough to recognise the roof, and a one-line caption. */
.address-map--compact { margin-top: 0.5rem; height: auto; }  /* not the old fixed 14-16rem */
.address-map--compact iframe { aspect-ratio: auto; height: 8.5rem; }
.address-map--compact figcaption { padding: 0.35rem 0.6rem; font-size: 0.78rem; }


/* --- choosing how to pay ---
   Their iframe renders one method at a time, so these are links to different
   frames rather than tabs over one. Styled as tabs anyway, because that is
   what the customer thinks they are, and the page reload is quick. */
.pay-methods {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 auto 1.1rem;
  max-width: 34rem;
}

.pay-methods__tab {
  flex: 1 1 auto;
  text-align: center;
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
  text-decoration: none;
  font-size: 0.9rem;
  line-height: 1.3;
}

.pay-methods__tab:hover { border-color: color-mix(in srgb, currentColor 38%, transparent); }

.pay-methods__tab.is-current {
  border-color: transparent;
  background: var(--color-navy, #14213d);
  color: var(--color-paper, #fff);
  font-weight: 600;
}

/* BPAY leaves the page, so it should not look like a peer of the two that
   stay on it. */
.pay-methods__tab--away {
  flex: 0 0 auto;
  opacity: 0.85;
  border-style: dashed;
}

@media (max-width: 520px) {
  .pay-methods__tab { flex: 1 1 100%; }
}

/* --- the suburb on a job card ---
   Above the address and louder than it. A cleaner covers a whole region now,
   and a region is wide: the suburb is what tells them whether the job is ten
   minutes away or ninety. */
.job-card__area {
  margin: 0 0 0.15rem;
  font-family: var(--font-mono, monospace);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.job-card__area + .job-card__where {
  font-size: 0.95rem;
  font-weight: 400;
  opacity: 0.75;
  margin-top: 0;
}

/* --- the suburb list on a region page ---
   Columns rather than a long ribbon: a hundred and fifty names down one
   column is a wall, and the point of the list is that somebody can find
   their own suburb in it quickly. */
.suburb-list {
  list-style: none;
  margin: 0 0 2rem;
  padding: 0;
  columns: 2;
  column-gap: 2rem;
}

.suburb-list li {
  break-inside: avoid;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.32rem 0;
  border-bottom: 1px solid color-mix(in srgb, currentColor 8%, transparent);
  font-size: 0.92rem;
}

.suburb-list__code {
  font-family: var(--font-mono, monospace);
  font-size: 0.8rem;
  opacity: 0.6;
}

@media (min-width: 700px)  { .suburb-list { columns: 3; } }
@media (min-width: 1000px) { .suburb-list { columns: 4; } }

/* --- do we clean where you are? ---
   The box that replaced a list of every suburb. It is the first thing in that
   section now, so it gets room. */
.coverage { margin: 0; }

.coverage__label {
  display: block;
  font-family: var(--font-mono, monospace);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.72rem;
  opacity: 0.75;
  margin-bottom: 0.6rem;
}

.coverage__row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.coverage__input {
  flex: 1 1 14rem;
  min-width: 0;
  padding: 0.75rem 0.9rem;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid color-mix(in srgb, currentColor 24%, transparent);
  background: var(--color-surface, #fff);
  font: inherit;
  color: inherit;
}

.coverage__input:focus-visible {
  outline: 2px solid var(--color-accent, currentColor);
  outline-offset: 1px;
}

.coverage__answer {
  margin: 0.85rem 0 0;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-sm, 6px);
  border-left: 3px solid color-mix(in srgb, currentColor 30%, transparent);
  background: color-mix(in srgb, currentColor 5%, transparent);
  font-size: 0.95rem;
  line-height: 1.5;
}

.coverage__answer.is-yes { border-left-color: var(--color-lime, currentColor); }
.coverage__answer.is-no  { border-left-color: color-mix(in srgb, currentColor 40%, transparent); }

/* The old wall of suburb names. Nothing renders it any more; the rule is
   removed rather than left to rot. */

/* ---------- Dropping photos onto a form ----------

   Wrapped around the real file input rather than replacing it. The input
   stays in the markup and stays functional: with no JavaScript this is a
   label and a file picker, which is the same field it always was. The rest
   of the careers form submits without JavaScript, so this had to as well. */

.dropzone { display: grid; gap: 0.75rem; }

/* The input is present for the form and for the keyboard, and out of the way
   visually. Not `display: none`: that takes it out of the tab order, and the
   label points at it, so a keyboard user needs it focusable. */
.dropzone input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* `.dropzone .dropzone__area`, not `.dropzone__area`. The drop target is a
   <label>, and `.form label { display: block }` above is two selectors to
   this one's one - so a bare class lost, and the three lines inside sat on
   one line reading "Drag photos here, or click to choose JPG, PNG or HEIC.
   Up to 10." as a single sentence. The register warns about exactly this. */
.dropzone .dropzone__area {
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  padding: 2rem 1.25rem;
  border: 2px dashed color-mix(in srgb, var(--color-navy) 22%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--color-navy) 3%, transparent);
  cursor: pointer;
  text-align: center;
}

.dropzone .dropzone__area:hover,
.dropzone input[type="file"]:focus-visible + .dropzone__area,
.dropzone.is-over .dropzone__area {
  border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 10%, transparent);
}

.dropzone__icon { font-size: 1.4rem; line-height: 1; opacity: 0.5; }
.dropzone__lead { font-weight: 600; }
.dropzone__hint { font-size: 0.85rem; color: var(--color-navy-soft); }

.dropzone__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
  font-size: 0.88rem;
}

.dropzone__list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
  border-radius: 6px;
}

/* The filename can be anything the uploader's phone called it, including
   something very long. It shrinks rather than pushing the size out of view. */
.dropzone__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.dropzone__size {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--color-navy-soft);
  flex-shrink: 0;
}

.dropzone__error {
  margin: 0;
  font-size: 0.88rem;
  color: var(--color-danger, #A33B1F);
  font-weight: 600;
}

@media (prefers-reduced-motion: no-preference) {
  .dropzone .dropzone__area { transition: border-color 120ms ease, background 120ms ease; }
}

/* ---------- "What we do", the one grouped nav item ----------

   A <details>, so it opens on click and on keyboard with no JavaScript. The
   rest of this site works without a script and a menu is the last thing that
   should stop.

   Two behaviours from one element: on a wide screen the panel floats over the
   page like a dropdown; in the stacked mobile menu it is simply a section that
   expands and pushes the rest down. */

.site-nav__group { position: relative; }

.site-nav__group > summary {
  font-size: 0.95rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid transparent;
  cursor: pointer;
  list-style: none;          /* the default triangle, replaced below */
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.site-nav__group > summary::-webkit-details-marker { display: none; }

/* Our own caret, so it can rotate when open and match the type around it. */
.site-nav__group > summary::after {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-0.1em, -0.1em);
  opacity: 0.6;
}
.site-nav__group[open] > summary::after { transform: rotate(-135deg) translate(-0.05em, -0.05em); }
.site-nav__group > summary:hover { border-bottom-color: var(--color-lime-ink); }

.site-nav__submenu { display: grid; }

@media (min-width: 900px) {
  /* Floating panel on a wide screen. `position: absolute` so opening it does
     not push the rest of the nav sideways, which on a horizontal bar would
     move every link the moment somebody hovered near it. */
  .site-nav__submenu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 40;
    min-width: 12rem;
    gap: 0.15rem;
    padding: 0.6rem;
    background: var(--color-surface);
    border: 1px solid color-mix(in srgb, var(--color-navy) 12%, transparent);
    border-radius: 8px;
    box-shadow: 0 12px 28px color-mix(in srgb, var(--color-navy) 14%, transparent);
  }
  .site-nav__submenu a:not(.btn) { padding: 0.4rem 0.5rem; border-bottom: 0; border-radius: 5px; }
  .site-nav__submenu a:not(.btn):hover {
    background: color-mix(in srgb, var(--color-navy) 6%, transparent);
    border-bottom: 0;
  }
}

@media (max-width: 899px) {
  /* Stacked menu: an indented section rather than a floating panel. */
  .site-nav__submenu { gap: 0.1rem; padding: 0.25rem 0 0.25rem 0.9rem; }
}
