/* ===========================================================================
   DURHAM MOBILE MICROSUCTION — DESIGN SYSTEM
   ---------------------------------------------------------------------------
   ── WHO THIS IS DRAWN FOR ──────────────────────────────────────────────
   Not the client. The person booking. For ear wax removal that skews older,
   and a good share of the enquiries are somebody booking on behalf of a
   parent. So the accessibility IS the aesthetic here, not a pass done at the
   end:

     · body text starts at 18px, not 16px, and never shrinks below 16px
     · nothing on the page is under 4.5:1. There is no pale grey small print.
       The ratios below are measured, not eyeballed — tools/check-contrast.mjs
       recomputes them from this file and fails the build if one drops.
     · tap targets are 48px minimum, and the phone number is a link everywhere
       it appears, because a lot of people will be reading this ON the phone
       they are about to ring with
     · no hover-only anything

   ── WHY IT DOES NOT LOOK LIKE THE OTHER TEN ────────────────────────────
   Every ear wax site in the country is the same page: a stock photo of an
   ear, three rounded cards with circle icons, a blue gradient, "Professional,
   Safe, Affordable". Hers currently is too.

   What is here instead:
     · hairline rules divide things, not boxes and shadows
     · square corners, except on form controls where a radius helps them read
       as tappable
     · type sizes far apart — a real headline against real body text
     · left-aligned and asymmetric; a centred column of short paragraphs is
       the single most recognisable template tell
     · one dark pine band used once or twice a page, never a gradient

   ── THE PALETTE ────────────────────────────────────────────────────────
   No logo was supplied, so this is not sampled from anything — it is chosen,
   and it is chosen to be the opposite of medical-blue without being
   unserious: deep pine green, warm paper, a copper accent for the things you
   press.

   ⚠  COPPER FLIPS ON DARK. --brand is 5.04:1 on paper and only 3.61:1 on
      pine. Anything copper sitting on the dark band uses --sky instead
      (6.83:1). Getting this backwards is invisible on a laptop screen in a
      bright room and unreadable on a phone outdoors.
   ======================================================================== */

:root {
  /* --- Surfaces ---------------------------------------------------------
     Cool near-white, not the warm cream this site started with. Her mark is
     a cold blue on white; a warm paper behind it turns it slightly green. */
  --paper: #f6f8f9;        /* the page */
  --paper-sunk: #eef3f5;   /* recessed panels, quiet bands */
  --card: #ffffff;         /* raised surfaces */
  --deep: #123a4e;         /* the one dark surface */
  --deep-2: #0e2e3f;       /* footer, one step down from the band */

  /* --- Ink (ratios measured on --paper) --------------------------------- */
  --ink: #14212a;          /* 15.39:1 — body text */
  --ink-soft: #46545c;     /*  7.35:1 — secondary text */
  --ink-muted: #556169;    /*  5.97:1 — small print. The floor. Nothing paler
                              than this exists in this stylesheet on purpose. */

  /* --- Her brand --------------------------------------------------------
     --brand is the colour SAMPLED FROM HER LOGO FILE — #3C84A8, the exact
     blue of the ear mark and the wordmark. It is not adjusted, because it is
     hers.

     ⚠  IT IS 3.90:1 ON PAPER, WHICH IS BELOW AA FOR BODY TEXT.
        So it behaves exactly like Durham Clean's mocha: her real colour does
        the fills, the rules, the logo and the large display type, and
        --brand-text (7.04:1) carries anything that has to be READ. Setting
        body text or a link in --brand would be using her brand correctly and
        failing the person reading it, which is not a trade worth making on a
        site whose visitors skew older. */
  --brand: #3c84a8;        /*  3.90:1 — HER COLOUR. Fills and large type only */
  --brand-text: #215a78;   /*  7.04:1 — brand colour that has to be readable */
  --brand-pale: #e3eef4;   /* tints, chips, hovers */

  /* ⚠  HER BLUE FLIPS ON DARK: --brand is only 2.91:1 on --deep. Anything
        brand-coloured sitting on the dark band uses --sky instead (7.88:1).
        Getting this backwards is invisible on a bright laptop screen and
        unreadable on a phone outdoors. */
  --sky: #a8d8ee;          /*  7.88:1 on --deep */

  /* --- Lines ------------------------------------------------------------ */
  --rule: #dde5e9;         /* the hairline that does most of the dividing */
  --rule-strong: #c3cfd6;
  --rule-dark: #2a5a72;    /* rules inside the dark band */

  /* --- State (never colour alone — always a word or a mark too) ---------- */
  --good: #1f6b4a;
  --bad:  #a32017;

  /* --- Type ------------------------------------------------------------- */
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  /* ⚠ Headings are set in the SAME face as the body, not a serif.
     Her wordmark is a geometric sans in caps. An Old-Style serif headline
     over it is two different voices arguing on the same page — it reads as a
     template that happened to have a logo dropped into it, which is the exact
     impression this rebuild exists to undo. What keeps the page from looking
     generic is the layout — hairline rules, asymmetry, no cards, type sizes
     far apart — not a decorative typeface. */
  --display: var(--sans);

  /* --- Measure and rhythm ----------------------------------------------- */
  --measure: 34rem;        /* reading width — about 68 characters at 18px */
  --wide: 72rem;           /* page container */
  --gap: 1.5rem;
  --band: clamp(3rem, 7vw, 5.5rem);   /* vertical space between sections */

  --tap: 3rem;             /* 48px — the minimum anything tappable may be */
}

/* ---------------------------------------------------------------------------
   RESET — small and deliberate
   ------------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.125rem/1.6 var(--sans);   /* 18px */
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { font-family: var(--display); font-weight: 650; line-height: 1.15; margin: 0 0 .5em; letter-spacing: -.015em; }
h1 { font-size: clamp(2.1rem, 6vw, 3.4rem); letter-spacing: -.025em; }
h2 { font-size: clamp(1.6rem, 3.6vw, 2.3rem); }
h3 { font-size: 1.3rem; }
p, ul, ol { margin: 0 0 1.1em; }
p { max-width: var(--measure); }
a { color: var(--brand-text); text-underline-offset: .18em; text-decoration-thickness: .08em; }
a:hover { color: var(--ink); }
:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }
hr { border: 0; border-top: 1px solid var(--rule); margin: var(--band) 0; }

/* Anything a finger has to find is at least --tap tall. */
a[href^="tel:"], .btn, .nav a, .chip { min-height: var(--tap); }

/* ⚠  HER EMAIL ADDRESS IS ONE 35-CHARACTER WORD.
      info@durhammobilemicrosuction.co.uk has nothing in it a browser is
      allowed to break, so it renders as a single 313px-wide token. Inside a
      280px container — which is what `.wrap` comes to on a 320px phone — it
      pushed the footer, and with it the whole page, out to 333px and made
      EVERY PAGE ON THE SITE scroll sideways.

      It was invisible for two reasons worth remembering: the layout was only
      ever measured at 390px and up, where it happens to fit, and a full-page
      screenshot is as wide as the content it is shooting, so it looked
      perfect. tools/check-layout.mjs now measures from 320px.

      `anywhere` rather than `break-word` on purpose: only `anywhere` shrinks
      the element's min-content width, which is the number the grid column was
      actually sizing itself from. */
a[href^="mailto:"] { overflow-wrap: anywhere; }

/* Hidden by clipping rather than by parking it at left:-9999px. The old
   trick puts a real 227px-wide box on the page and relies on nobody's layout
   noticing; clip-path takes it out of the flow properly. */
.skip {
  position: absolute; top: 0; left: 0; z-index: 99;
  background: var(--deep); color: var(--paper); padding: .75rem 1rem;
  clip-path: inset(50%); white-space: nowrap;
}
.skip:focus { clip-path: none; }

/* A heading that is there for the outline and for a screen reader, and not
   for the eye — the clinic strip under the hero needs a name in the document
   structure, but a visible "Her clinics" above four cards each labelled
   "Clinic" would be saying it twice. */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.wrap { width: min(100% - 2.5rem, var(--wide)); margin-inline: auto; }
.narrow { max-width: var(--measure); }

/* ---------------------------------------------------------------------------
   HEADER
   ------------------------------------------------------------------------ */
.site-head {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; z-index: 40;
}
.head-row {
  display: flex; align-items: center; gap: 1rem;
  padding: .6rem 0; min-height: 4rem;
}
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; color: var(--ink); }
.brand:hover { color: var(--ink); }
.brand-mark { flex: 0 0 auto; width: 2.5rem; height: 2.5rem; }
.brand-name {
  font-family: var(--display); font-size: .95rem; line-height: 1.15;
  font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
.brand-name span { display: block; font-family: var(--sans); font-size: .72rem;
  font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-muted); margin-top: .18rem; }

.head-spacer { flex: 1 1 auto; }

/* The header phone link. On a phone this is the whole point of the header. */
/* ⚠ THE HEADER DOES NOT FIT ON A PHONE WITH ALL THREE IN IT.
   Her name is 26 characters, the phone number is a pill, and the Menu button
   is 72px — at 390px wide the button was pushed 69px off the right edge and
   the whole page scrolled sideways. It looked completely fine in a
   screenshot, because a full-page shot is as wide as the content it is
   shooting.

   So below 48rem the header phone pill goes. Nothing is lost: the sticky call
   bar at the bottom of the screen is on exactly those widths and carries the
   same number, bigger and closer to the thumb. */
.head-call {
  display: inline-flex; align-items: center; gap: .5rem;
  font-weight: 600; text-decoration: none; color: var(--deep);
  border: 2px solid var(--deep); padding: .55rem .9rem;
  white-space: nowrap;
}
.head-call:hover { background: var(--deep); color: var(--paper); }

@media (max-width: 47.99rem) {
  .head-call { display: none; }
  /* Her name in caps already runs to two lines at this width; the strapline
     under it took the header to four lines of text before anything on the
     page had started. It is on every page below the fold anyway. */
  .brand-name { font-size: .85rem; }
  .brand-name span { display: none; }
}

.nav-toggle {
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: var(--tap); padding: .5rem .8rem;
  background: none; border: 2px solid var(--rule-strong); color: var(--ink);
  font: 600 1rem var(--sans); cursor: pointer;
}
.nav-toggle:hover { border-color: var(--deep); }

.nav { display: none; border-top: 1px solid var(--rule); padding: .5rem 0 1rem; }
.nav.is-open { display: block; }
.nav ul { list-style: none; margin: 0; padding: 0; }
.nav a {
  display: flex; align-items: center;
  padding: .7rem .25rem; text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--rule); font-weight: 500;
}
.nav a[aria-current="page"] { color: var(--brand-text); font-weight: 600; }

@media (min-width: 60rem) {
  .nav-toggle { display: none; }
  .nav { display: block; border: 0; padding: 0; }
  .nav ul { display: flex; gap: 1.4rem; align-items: center; }
  .nav a { border: 0; padding: .4rem 0; position: relative; }
  .nav a[aria-current="page"]::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -.15rem;
    height: 2px; background: var(--brand);
  }
}

/* ---------------------------------------------------------------------------
   BUTTONS
   ------------------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  padding: .85rem 1.4rem; border: 2px solid transparent; border-radius: 4px;
  font: 600 1.0625rem/1.2 var(--sans); text-decoration: none; cursor: pointer;
  text-align: center;
}
.btn-primary { background: var(--brand-text); color: #fff; }
.btn-primary:hover { background: var(--deep); color: #fff; }
.btn-secondary { background: transparent; color: var(--deep); border-color: var(--deep); }
.btn-secondary:hover { background: var(--deep); color: var(--paper); }
.btn-on-dark { background: var(--paper); color: var(--deep); }
.btn-on-dark:hover { background: var(--sky); color: var(--deep-2); }
/* The one button the whole site is pointing at. Bigger than the rest on
   purpose — this audience is often reading it one-handed with a blocked ear
   and no interest in hunting for the number. */
.btn-lg { padding: 1.05rem 1.7rem; font-size: 1.15rem; }
.btn .icon { width: 1.2rem; height: 1.2rem; flex: 0 0 auto; }
.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; margin: 1.5rem 0 1rem; }

/* ---------------------------------------------------------------------------
   HERO
   ------------------------------------------------------------------------ */
.hero { padding: var(--band) 0 calc(var(--band) * .8); }
.hero-eyebrow {
  font: 600 .8rem/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: var(--brand-text); margin: 0 0 1.1rem;
}
.hero h1 { max-width: 18ch; }
.hero-lede { font-size: 1.25rem; color: var(--ink-soft); max-width: var(--measure); }
.hero-grid { display: grid; gap: var(--band); }
@media (min-width: 62rem) {
  .hero-grid { grid-template-columns: 1.25fr .85fr; align-items: start; gap: 3.5rem; }
}

/* The three-line "what you need to know" block beside the hero. Not cards —
   a definition list with hairlines, which is how a printed page would do it. */
.facts { margin: 0; border-top: 2px solid var(--ink); }
.facts > div { border-bottom: 1px solid var(--rule); padding: .9rem 0; }
.facts dt {
  font: 600 .78rem/1 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: .35rem;
}
.facts dd { margin: 0; font-size: 1.0625rem; }

/* ---------------------------------------------------------------------------
   SECTIONS
   ------------------------------------------------------------------------ */
.section { padding: var(--band) 0; border-top: 1px solid var(--rule); }
.section-head { margin-bottom: 2rem; }
.section-head p { color: var(--ink-soft); }
.eyebrow {
  font: 600 .78rem/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: var(--brand-text); margin: 0 0 .8rem;
}

/* The dark band. Used once or twice per page at most. */
.band { background: var(--deep); color: var(--paper); padding: var(--band) 0; }
.band h2, .band h3 { color: #fff; }
.band p { color: #e6ece9; }
/* ⚠ :not(.btn) IS LOad-BEARING. Without it this rule beats .btn-on-dark on
   specificity — `.band a` is (0,1,1), `.btn-on-dark` is (0,1,0) — and every
   button sitting on the dark band ends up with SAND text on its PAPER
   background, about 1.6:1. It is legible enough on a bright laptop screen to
   pass a glance and unreadable on a phone, and no token-pair check catches it
   because both tokens are correct; it is the pairing that is wrong.
   tools/check-contrast.mjs measures what the browser actually computed, which
   is the only thing that would have caught this. */
.band a:not(.btn) { color: var(--sky); }
.band a:not(.btn):hover { color: #fff; }
.band .eyebrow { color: var(--sky); }
.band .rule { border-color: var(--rule-dark); }

/* ---------------------------------------------------------------------------
   SERVICE LIST — a list with rules, not a grid of cards
   ------------------------------------------------------------------------ */
.svc-list { list-style: none; margin: 0; padding: 0; border-top: 2px solid var(--ink); }
.svc-list li { border-bottom: 1px solid var(--rule); }
.svc-list a {
  display: grid; gap: .3rem; padding: 1.35rem .25rem;
  text-decoration: none; color: var(--ink);
}
.svc-list a:hover { background: var(--card); }
.svc-name {
  font-family: var(--display); font-size: 1.3rem; font-weight: 600;
  color: var(--brand-text); display: flex; align-items: baseline; gap: .5rem;
}
.svc-name::after { content: '→'; font-family: var(--sans); color: var(--brand-text); }
.svc-lede { color: var(--ink-soft); font-size: 1rem; max-width: 52ch; }
@media (min-width: 48rem) {
  .svc-list a { grid-template-columns: 20rem 1fr; gap: 1.5rem; align-items: baseline; }
}

/* ---------------------------------------------------------------------------
   AREAS
   ------------------------------------------------------------------------ */
.chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.chip {
  display: inline-flex; align-items: center; padding: .6rem 1rem;
  background: var(--card); border: 1px solid var(--rule-strong);
  text-decoration: none; color: var(--ink); font-weight: 500;
}
.chip:hover { border-color: var(--deep); background: var(--deep); color: var(--paper); }
.band .chip { background: transparent; border-color: var(--rule-dark); color: var(--paper); }
.band .chip:hover { background: var(--paper); color: var(--deep); }

/* ---------------------------------------------------------------------------
   HOURS + CLINICS
   ------------------------------------------------------------------------ */
.hours { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); max-width: 26rem; }
.hours li {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .7rem 0; border-bottom: 1px solid var(--rule);
}
.hours .day { font-weight: 600; }
.hours .time { color: var(--ink-soft); }
.band .hours { border-color: var(--rule-dark); }
.band .hours li { border-color: var(--rule-dark); }
.band .hours .time { color: #e6ece9; }

/* ===========================================================================
   THE CLINIC SHELF
   ---------------------------------------------------------------------------
   She has four clinics to fill, so on the home page the clinics are not a
   section further down the page — they are a shelf of cards sitting on the
   bottom edge of the hero, overlapping it. That overlap is the whole idea:
   the first thing below the headline is the four places you can book.

   ⚠  REAL GAPS AND A BORDER PER CARD, never the 1px-gap-over-a-coloured-
      background trick. The number of clinics is the one thing here that is
      going to change — three today, four the moment she sends the name — and
      with that technique an incomplete last row paints the container's
      background as grey boxes that read as broken cards. Same lesson as the
      service grid above, and this grid changes count more often.
   ======================================================================== */
/* No top padding — the shelf provides its own by overlapping upwards — and a
   reduced bottom, because the section that follows brings a full --band of
   its own and the two together left a hole. */
.shelf-section { padding: 0 0 calc(var(--band) * .4); }

.clinic-shelf {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  position: relative; z-index: 2;
}
/* The overlap, and only where there is room for it. On a phone the cards sit
   below the hero in the ordinary way — a card half over a dark band on a
   390px screen is a mess, not a flourish. */
@media (min-width: 52rem) {
  .clinic-shelf { margin-top: calc(var(--band) * -.55); }
}
/* The flat variant: the same cards used mid-page, with no overlap. */
.clinic-shelf-flat { margin-top: 0 !important; }

.clinic-card {
  /* The stagger index. Defined here as well as set per-card in the markup, so
     the stylesheet stands up on its own and a card that arrives without one
     is simply first rather than undefined. */
  --i: 0;
  position: relative;
  background: var(--card);
  border: 1px solid var(--rule);
  border-top: 4px solid var(--brand);
  padding: 1.35rem 1.25rem 1.15rem;
  display: flex; flex-direction: column;
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.clinic-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 14px 30px -18px rgba(18, 58, 78, .55);
  border-color: var(--rule-strong);
  border-top-color: var(--brand-text);
}
/* The whole card is the target; the clinic's name is its accessible name.
   One link per card, so a keyboard user tabs through four things and not
   twelve, and a screen reader announces "Belmont, link" rather than "link,
   link, link". */
.clinic-card h3 { font-size: 1.5rem; margin-bottom: .6rem; }
.clinic-card h3 a { color: var(--ink); text-decoration: none; }
.clinic-card h3 a::after { content: ''; position: absolute; inset: 0; }
.clinic-card:hover h3 a { color: var(--brand-text); }
/* The focus ring has to go round the CARD, not round two words of heading,
   or tabbing to it looks like nothing happened. */
.clinic-card:focus-within { outline: 3px solid var(--brand); outline-offset: 2px; }
.clinic-card h3 a:focus-visible { outline: none; }

.clinic-kicker {
  display: flex; align-items: center; gap: .4rem;
  margin: 0 0 .5rem; font-size: .72rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--brand-text);
}
.clinic-kicker .icon { width: .95rem; height: .95rem; }

/* Every one of her clinics runs inside somebody else's business — a pharmacy,
   an opticians, a health practice, a salon. The venue is what a person is
   looking for from the pavement, so it is set like a name and not like a line
   of small print underneath one. */
.clinic-venue {
  margin: -.25rem 0 .7rem; max-width: 26ch;
  font-weight: 600; color: var(--brand-text); line-height: 1.3;
}
.hero-dark .clinic-venue { color: var(--sky); }

.clinic-meta { list-style: none; margin: 0 0 1rem; padding: 0; }
.clinic-meta li {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .28rem 0; font-size: 1rem; color: var(--ink-soft);
}
.clinic-meta .icon { flex: 0 0 auto; width: 1.15rem; height: 1.15rem; color: var(--brand); margin-top: .2rem; }
.clinic-meta span { max-width: 26ch; }

/* What a clinic she has told us nothing about says instead. It is the truth
   and it still gives them something to do — it is not a placeholder. */
.clinic-blank { font-size: 1rem; color: var(--ink-muted); margin: 0 0 1rem; max-width: 26ch; }

.clinic-card .more {
  margin-top: auto; padding-top: .5rem;
  font: 600 .95rem/1.3 var(--sans); color: var(--brand-text);
}
.clinic-card .arr { display: inline-block; transition: transform .22s ease; }
.clinic-card:hover .arr { transform: translateX(4px); }

.shelf-foot { margin: 1.5rem 0 0; max-width: 58ch; color: var(--ink-soft); font-size: 1rem; }
.chips-foot { margin-top: 1rem; font-size: 1rem; }

/* The same arrow behaviour on an ordinary inline link. */
.link-arrow { font-weight: 600; text-decoration: none; }
.link-arrow:hover { text-decoration: underline; }
.link-arrow .arr { display: inline-block; transition: transform .22s ease; }
.link-arrow:hover .arr { transform: translateX(4px); }

/* Clinic details repeated in the hero, on the dark ground. */
.hero-meta { margin: 1.25rem 0 0; }
.hero-meta .clinic-meta { margin-bottom: 0; }
.hero-meta .clinic-meta li { color: #dbe7ee; }
.hero-meta .clinic-meta .icon { color: var(--sky); }

/* A call-out for the one thing a clinic page must say when she has not given
   its address yet: ring, and you will be told. */
.notice {
  display: flex; gap: 1.1rem; align-items: flex-start;
  background: var(--brand-pale); border-left: 4px solid var(--brand);
  padding: 1.4rem 1.5rem;
}
.notice .icon { flex: 0 0 auto; width: 1.8rem; height: 1.8rem; color: var(--brand-text); margin-top: .2rem; }
.notice h2 { font-size: 1.3rem; margin-bottom: .35rem; }
.notice p { margin-bottom: 0; color: var(--ink-soft); }

.panel-note { font-size: .95rem; color: var(--ink-muted); margin-bottom: 0; }

/* ===========================================================================
   OPEN NOW
   ---------------------------------------------------------------------------
   Filled in and unhidden by site.js from her hours, in her timezone. Rendered
   empty, so a blocked script shows nothing at all rather than a stale claim
   about whether somebody is at the end of the phone.

   ⚠  The dot is decoration and carries no meaning on its own — the sentence
      beside it says "Open now" or "Closed now" in words. Colour alone is not
      a state on this site.
   ======================================================================== */
.open-now {
  display: flex; align-items: center; gap: .6rem;
  margin: 0; font-size: 1rem; font-weight: 600; color: var(--ink-soft);
}
.open-now .dot {
  width: .6rem; height: .6rem; border-radius: 50%;
  background: var(--ink-muted); flex: 0 0 auto;
}
.open-now[data-state="open"] .dot { background: #1f8a5a; box-shadow: 0 0 0 0 rgba(31,138,90,.55); animation: pulse 2.4s ease-out infinite; }
.open-now[data-state="shut"] .dot { background: #b06a3c; }
.hero-dark .open-now { color: #dbe7ee; }
.hero-dark .open-now[data-state="open"] .dot { background: #6fd39b; box-shadow: 0 0 0 0 rgba(111,211,155,.6); }
.hero-dark .open-now[data-state="shut"] .dot { background: #f0b48f; }

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 currentColor; }
  70%  { box-shadow: 0 0 0 .55rem transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ===========================================================================
   MOTION
   ---------------------------------------------------------------------------
   ⚠  EVERY ANIMATION IN THIS BLOCK IS SWITCHED OFF, NOT SLOWED DOWN, BY THE
      prefers-reduced-motion RULE AT THE END. This site's visitors skew older
      and a fair number arrive with a blocked ear, which is a balance problem
      as much as a hearing one. Motion here is a garnish on a page that reads
      perfectly still.
   ======================================================================== */

/* --- The rings ----------------------------------------------------------
   Sound leaving an ear that has just been unblocked. Three circles, drawn in
   the corner of the dark hero, spreading and fading on a long loop. */
.rings { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.rings span {
  position: absolute; left: -6rem; top: -6rem;
  width: 26rem; height: 26rem; border-radius: 50%;
  border: 1px solid rgba(168, 216, 238, .34);
  transform: scale(.35); opacity: 0;
  animation: ring 9s ease-out infinite;
}
.rings span:nth-child(2) { animation-delay: 3s; }
.rings span:nth-child(3) { animation-delay: 6s; }
@keyframes ring {
  0%   { transform: scale(.3);  opacity: 0; }
  12%  { opacity: .85; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* --- The hero arriving ---------------------------------------------------
   A short stagger down the first screen. It runs from CSS on load rather than
   from a scroll observer, because the hero is already in view — waiting for a
   script to reveal the headline is how a page ends up blank. The final state
   is the DEFAULT and the animation only plays backwards from it, so a browser
   that ignores the animation still shows a finished hero. */
@media (prefers-reduced-motion: no-preference) {
  .hero-dark .hero-eyebrow { animation: rise .55s .04s both ease-out; }
  .hero-dark h1            { animation: rise .6s  .12s both ease-out; }
  .hero-dark .hero-lede    { animation: rise .6s  .2s  both ease-out; }
  .hero-dark .btn-row      { animation: rise .6s  .28s both ease-out; }
  .hero-dark .open-now     { animation: rise .6s  .34s both ease-out; }
  .hero-dark .glance       { animation: rise .7s  .4s  both ease-out; }
  .hero-dark .hero-meta    { animation: rise .6s  .3s  both ease-out; }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* --- The clinic cards arriving one after another -------------------------
   `--i` is set per card in the markup. The reveal class is still added by the
   script, so this only ever changes the TIMING of something that is already
   visible without it. */
.clinic-card.reveal { transition-delay: calc(var(--i, 0) * 90ms); }

/* --- The ear drawing itself ---------------------------------------------
   Her own mark's strokes, drawn on as the panel comes into view. The dash
   length is generous rather than measured — an overlong dash simply means the
   line is complete before the animation ends, which looks identical. */
.icon-draw path { stroke-dasharray: 90; stroke-dashoffset: 90; }
.icon-draw.is-drawn path { animation: draw 1.5s ease forwards; }
.icon-draw.is-drawn path:nth-child(2) { animation-delay: .35s; }
.icon-draw.is-drawn path:nth-child(3) { animation-delay: .7s; }
@keyframes draw { to { stroke-dashoffset: 0; } }

/* --- The numbers counting up --------------------------------------------
   The figure is printed in the HTML. The script only replaces it, briefly,
   with smaller ones. `tabular-nums` stops the strip jittering as the width of
   each digit changes on the way up. */
.count { font-variant-numeric: tabular-nums; }

/* --- Panels and service cards lifting ------------------------------------ */
.svc-grid > li { transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease; }
.svc-grid > li:hover {
  transform: translateY(-3px);
  box-shadow: 0 14px 30px -18px rgba(18, 58, 78, .5);
  border-color: var(--rule-strong);
}

/* ⚠ THE SWITCH. Everything above stops. */
@media (prefers-reduced-motion: reduce) {
  .rings span,
  .open-now[data-state="open"] .dot,
  .icon-draw.is-drawn path { animation: none !important; }
  .icon-draw path { stroke-dashoffset: 0; }
  .hero-dark .hero-eyebrow, .hero-dark h1, .hero-dark .hero-lede,
  .hero-dark .btn-row, .hero-dark .open-now, .hero-dark .glance,
  .hero-dark .hero-meta { animation: none !important; }
  .clinic-card, .svc-grid > li, .clinic-card .arr, .link-arrow .arr { transition: none !important; }
  .clinic-card:hover, .svc-grid > li:hover { transform: none; }
}

/* ---------------------------------------------------------------------------
   PRICES — every rule here only matters once prices.publish is true
   ------------------------------------------------------------------------ */
.price-list { list-style: none; margin: 0; padding: 0; border-top: 2px solid var(--ink); max-width: 32rem; }
.price-list li { display: flex; align-items: baseline; gap: .75rem; padding: .9rem 0;
  border-bottom: 1px solid var(--rule); }
.price-name { font-weight: 600; }
.price-dots { flex: 1 1 auto; border-bottom: 2px dotted var(--rule-strong); transform: translateY(-.25rem); }
.price-amount { font-family: var(--display); font-size: 1.35rem; font-weight: 600; color: var(--brand-text); }
.price-note { display: block; font-size: .95rem; color: var(--ink-muted); font-weight: 400; }

/* ---------------------------------------------------------------------------
   PROSE — the body of a service page
   ------------------------------------------------------------------------ */
.prose { max-width: var(--measure); }

/* ⚠  `class="wrap prose"` on ONE element asks it to be two different
      containers at once: `.wrap` is a 72rem column centred in the page, and
      `.prose` is a 34rem reading measure. The narrower wins, so the whole
      block collapsed to 34rem and CENTRED ITSELF — leaving the About page's
      heading hard against the left margin and its body floating in the middle
      of the screen, twenty-odd millimetres adrift. It reads as a mistake
      rather than a choice, which is what it was.

      The container goes back to being a container; the measure is applied to
      what is inside it, which is where a reading width belongs. Paragraphs
      already carry it globally, so only the headings need saying. */
.wrap.prose { max-width: none; }
.wrap.prose > h2, .wrap.prose > h3, .wrap.prose > ul, .wrap.prose > ol {
  max-width: var(--measure);
}
.prose h2 { margin-top: 2.2rem; }
.prose ul { padding-left: 0; list-style: none; }
.prose ul li {
  position: relative; padding-left: 1.6rem; margin-bottom: .6rem;
}
.prose ul li::before {
  content: ''; position: absolute; left: 0; top: .62em;
  width: .7rem; height: 2px; background: var(--brand);
}

/* A quiet aside — used for the "speak to your GP" lines. Not an alarm box. */
/* --- Her photograph ------------------------------------------------------
   The only photograph on the site, and it earns its place by being HER. It
   sits second in the source order so that on a phone the words come first —
   somebody scrolling to find out who is coming through their door reads the
   sentence before they see the face, which is the right way round.

   `position: sticky` only where there is a column for it to stick in. */
.about-grid { display: grid; gap: 2.5rem; }
@media (min-width: 62rem) {
  .about-grid { grid-template-columns: minmax(0, 1fr) 20rem; gap: 3.5rem; align-items: start; }
  .portrait { position: sticky; top: 2rem; }
}
.portrait { margin: 0; }
.portrait img {
  width: 100%; max-width: 20rem; height: auto; display: block;
  border: 1px solid var(--rule);
  /* A hairline in her own blue along the top, the same as the clinic cards,
     so the photograph belongs to the page rather than sitting on it. */
  border-top: 4px solid var(--brand);
}

/* --- The one photograph of the actual work -------------------------------
   A frame from her own video, published with the client's permission. It sits
   FIRST in the source and first on a phone, because it is the single most
   persuasive thing on the page: it is her, doing the thing, in a real room.

   The two reassurance panels that used to share this row live to the right of
   the text now, so the photograph gets a column of its own rather than being
   squeezed into a sidebar. */
.shot-grid { display: grid; gap: 2.5rem; }
/* ⚠ THREE CHILDREN IN A TWO-COLUMN GRID: the photograph, the explanation, and
   the pair of reassurance panels. Left to itself the third one wraps under the
   photograph and leaves a column-high hole beside the text. The photograph
   takes both rows of column one instead, and the words stack down column two —
   which is also the right reading order on a phone, where the grid collapses
   and the photograph comes first. */
@media (min-width: 62rem) {
  .shot-grid { grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: 2rem 3.5rem; align-items: start; }
  .shot-grid > .shot { grid-row: 1 / span 2; }
}
.shot { margin: 0; }
.shot img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--rule);
  border-top: 4px solid var(--brand);
}
.shot figcaption {
  margin-top: .8rem; font-size: 1rem; color: var(--ink-soft); max-width: 34ch;
}

.aside {
  border-left: 3px solid var(--brand-text); padding: .1rem 0 .1rem 1.1rem;
  margin: 1.8rem 0; color: var(--ink-soft); max-width: var(--measure);
}
.aside p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------------------
   FAQ
   ------------------------------------------------------------------------ */
.faq { border-top: 2px solid var(--ink); max-width: 44rem; }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  padding: 1.1rem .25rem; cursor: pointer; font-weight: 600;
  font-family: var(--display); font-size: 1.15rem; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; font-family: var(--sans); color: var(--brand-text); font-size: 1.5rem; line-height: 1; }
.faq details[open] summary::after { content: '–'; }
.faq .answer { padding: 0 .25rem 1.2rem; color: var(--ink-soft); }
.faq .answer p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------------------
   FORM
   ------------------------------------------------------------------------ */
.form { max-width: 34rem; }
.field { margin-bottom: 1.25rem; }
.field label { display: block; font-weight: 600; margin-bottom: .4rem; }
.field .hint { display: block; font-weight: 400; font-size: .95rem; color: var(--ink-muted); margin-top: -.2rem; margin-bottom: .4rem; }
.field input, .field textarea, .field select {
  width: 100%; min-height: var(--tap); padding: .7rem .8rem;
  font: 400 1.0625rem var(--sans); color: var(--ink);
  background: var(--card); border: 2px solid var(--rule-strong); border-radius: 4px;
}
.field textarea { min-height: 8rem; resize: vertical; }
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--deep); }
.form-note { font-size: .95rem; color: var(--ink-muted); }
.hp { position: absolute; left: -9999px; }

.form-status { margin-top: 1rem; font-weight: 600; }
.form-status[data-state="ok"] { color: var(--good); }
.form-status[data-state="bad"] { color: var(--bad); }

/* ---------------------------------------------------------------------------
   STICKY CALL BAR — phones only
   ------------------------------------------------------------------------ */
.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: none; gap: .5rem; padding: .55rem;
  background: var(--deep); border-top: 2px solid var(--sky);
}
/* ⚠ "Call 07395 650820" plus its icon wrapped onto two lines inside a
     half-width button at 320px, which made the one control the whole site is
     pointing at look broken. The call gets the bigger share of the bar and
     the number is not allowed to break. */
.callbar .btn { flex: 1 1 0; padding: .8rem .5rem; font-size: 1rem; white-space: nowrap; }
.callbar .btn:first-child { flex: 1.7 1 0; }
@media (max-width: 47.99rem) {
  .callbar { display: flex; }
  body { padding-bottom: 4.75rem; }
}

/* On a desktop it is not a bar across the bottom of the screen — it is a
   pill in the corner that arrives once the hero has scrolled away, so the
   number is never more than a glance off. site.js adds `is-up`; with no
   script it simply never appears, and the number is still in the header, the
   hero, every call-to-action band and the footer. */
@media (min-width: 48rem) {
  .callbar {
    left: auto; right: 1.5rem; bottom: 1.5rem;
    display: flex; width: auto; padding: .5rem;
    border: 2px solid var(--sky); border-radius: 6px;
    box-shadow: 0 18px 40px -20px rgba(11, 30, 40, .75);
    opacity: 0; transform: translateY(1rem); pointer-events: none;
    transition: opacity .28s ease, transform .28s ease;
  }
  .callbar.is-up { opacity: 1; transform: none; pointer-events: auto; }
}
@media (min-width: 48rem) and (prefers-reduced-motion: reduce) {
  .callbar { transition: none; transform: none; }
}

/* ---------------------------------------------------------------------------
   FOOTER
   ------------------------------------------------------------------------ */
.site-foot { background: var(--deep-2); color: #dfe7e4; padding: var(--band) 0 2rem; }
.site-foot h2 { color: #fff; font-size: 1.15rem; }
.site-foot a { color: var(--sky); }
.site-foot a:hover { color: #fff; }
.foot-grid { display: grid; gap: 2rem; }
@media (min-width: 48rem) { .foot-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 62rem) { .foot-grid { grid-template-columns: 1.5fr 1fr 1fr 1fr; } }
.foot-grid ul { list-style: none; margin: 0; padding: 0; }
.foot-grid li { margin-bottom: .55rem; }
.foot-legal {
  margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--rule-dark);
  font-size: .95rem; color: #b9c7c3;
}
.foot-legal a { color: #dfe7e4; }
/* Her way in, and nobody else's business. Quiet enough that a customer's eye
   passes over it, findable enough that she is not typing a URL from memory on
   a phone in a car park. Still 4.9:1 on the footer, because "quiet" is not a
   licence to make something unreadable. */
.foot-owner { color: #93a5a1 !important; }
.foot-owner:hover { color: #fff !important; }

/* ---------------------------------------------------------------------------
   PRINT — somebody will print the contact page for a relative
   ------------------------------------------------------------------------ */
@media print {
  .site-head, .callbar, .nav-toggle, .btn-row { display: none !important; }
  body { background: #fff; font-size: 11pt; padding-bottom: 0; }
  .band, .site-foot { background: #fff !important; color: #000 !important; }
  .band h2, .band p, .site-foot a { color: #000 !important; }
  a[href^="tel:"]::after { content: ''; }
}

/* ---------------------------------------------------------------------------
   HER LOGO
   ---------------------------------------------------------------------------
   Sized in rem so it scales with the type rather than being pinned to a pixel
   size, and given an explicit height so the header cannot jump while it loads.
   ------------------------------------------------------------------------ */
.brand-mark { width: auto; height: 2.6rem; }
.foot-logo { width: 11rem; height: auto; margin-bottom: 1rem; }

/* ---------------------------------------------------------------------------
   COOKIE BANNER
   ---------------------------------------------------------------------------
   Only ever on the page when config.js has a Google tag id in it.

   ⚠  Accept and Reject are the same size, weight and distance from the thumb.
      A reject buried in a link is the pattern regulators have been fining
      people for, and it would be an odd thing to do on a site whose entire
      argument is that she can be trusted in your house.

   It sits above the sticky call bar, never over it — the phone number is the
   point of the page and must stay reachable while the banner is up.
   ------------------------------------------------------------------------ */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--card); border-top: 3px solid var(--brand);
  box-shadow: 0 -6px 24px rgba(20, 33, 42, .14);
  padding: 1.1rem 0 1.25rem;
}
.consent-inner {
  width: min(100% - 2.5rem, var(--wide)); margin-inline: auto;
  display: grid; gap: 1rem; align-items: center;
}
.consent h2 { font-size: 1.1rem; margin-bottom: .35rem; }
.consent p { font-size: 1rem; margin: 0; max-width: 60ch; color: var(--ink-soft); }
.consent-buttons { display: flex; gap: .75rem; flex-wrap: wrap; }
.consent-buttons .btn { flex: 1 1 9rem; }
@media (min-width: 52rem) {
  .consent-inner { grid-template-columns: 1fr auto; gap: 2rem; }
  .consent-buttons .btn { flex: 0 0 auto; min-width: 8rem; }
}
@media (max-width: 47.99rem) {
  /* Clear of the call bar underneath it. */
  .consent { bottom: 4.75rem; }
}

/* ===========================================================================
   THE SECOND PASS — depth, and something to look at
   ---------------------------------------------------------------------------
   The first version of this site was all hairlines and white space. It was
   clean, and clean is not the same as impressive: sat next to the site she
   already has, it read as unfinished rather than considered.

   What is added below is weight, not decoration:

     · a dark hero carrying her own mark at scale, so the first screen is hers
       rather than a white page with a headline on it
     · a strip of facts that are TRUE and specific — home visits, three
       clinics, fourteen towns, weekday hours — where most sites in this trade
       put "Professional · Trusted · Affordable"
     · drawn icons that match her mark's stroke, so a page of ten services
       reads as a set instead of a list
     · alternating grounds, so scrolling has a rhythm

   ⚠  What is deliberately NOT added: stock photography. A stock photo of a
      clinician in scrubs on a page about a one-woman mobile service is a
      picture of somebody who is not her, and everyone can tell. Her own
      photographs would beat every pixel below — that ask is in TO-CONFIRM.
   ======================================================================== */

/* --- The dark hero ------------------------------------------------------- */
.hero-dark {
  position: relative; overflow: hidden;
  background: var(--deep);
  color: var(--paper);
  padding: calc(var(--band) * 1.1) 0 var(--band);
}
/* A wash so the panel is not a flat rectangle of one colour. */
.hero-dark::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 12% 0%, rgba(60,132,168,.42), transparent 60%);
  pointer-events: none;
}
/* Her own mark, big, bleeding off the right edge. Low enough not to fight
   the headline, present enough that the first screen is unmistakably hers. */
.hero-watermark {
  position: absolute; right: -6%; top: 50%; transform: translateY(-50%);
  height: 128%; width: auto; opacity: .13; pointer-events: none;
  filter: saturate(0) brightness(2.4);
}
.hero-dark > .wrap { position: relative; }
/* ⚠  THE SAME BUG THAT THIS PROJECT'S CONTRAST TOOL WAS BUILT FOR, AGAIN.
       `a { color: var(--brand-text) }` is her readable blue and it is correct
       — on paper. On the dark hero it is 1.61:1, and the first link ever put
       into a dark hero (the "open it in maps" link on a clinic page) rendered
       exactly that. The dark BAND already had this fix as
       `.band a:not(.btn)`; the dark HERO never had a plain link in it before,
       so it never needed one. Both tokens were right. The pairing was not.

       `:not(.btn)` because a button on this ground is paper-on-deep and must
       keep its own colours — recolouring it to sky is how the original 1.67:1
       "Call" button happened. */
.hero-dark a:not(.btn) { color: var(--sky); }
.hero-dark a:not(.btn):hover { color: #fff; }
.hero-dark h1 { color: #fff; max-width: 16ch; }
.hero-dark .hero-lede { color: #dbe7ee; font-size: 1.3rem; }
.hero-dark .hero-eyebrow { color: var(--sky); }
@media (max-width: 47.99rem) {
  .hero-watermark { right: -22%; height: 92%; opacity: .1; }
}

/* --- At a glance --------------------------------------------------------
   Four true, checkable facts. Not adjectives. */
.glance {
  position: relative; margin-top: 2.75rem; padding-top: 1.75rem;
  border-top: 1px solid var(--rule-dark);
  /* Space under it as well as over it — pinned to the bottom edge of the
     hero it read as something that had fallen off the end of the page. */
  padding-bottom: .5rem;
  display: grid; gap: 1.25rem 2rem; grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 52rem) { .glance { grid-template-columns: repeat(4, 1fr); } }
.glance div { display: flex; gap: .7rem; align-items: flex-start; }
.glance .icon { flex: 0 0 auto; width: 1.6rem; height: 1.6rem; color: var(--sky); }
.glance strong { display: block; font-size: 1.05rem; color: #fff; line-height: 1.25; }
.glance span { font-size: .95rem; color: #c5d5de; }

/* --- Icons --------------------------------------------------------------- */
.icon { width: 1.5rem; height: 1.5rem; display: block; }

/* --- How it works -------------------------------------------------------- */
.steps { display: grid; gap: 2rem; counter-reset: step; }
@media (min-width: 52rem) { .steps { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; } }
.step { position: relative; padding-top: 1.25rem; border-top: 3px solid var(--brand); }
.step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.4rem; height: 2.4rem; margin-bottom: .9rem;
  background: var(--brand-pale); color: var(--brand-text);
  font: 650 1.05rem/1 var(--display); border-radius: 50%;
}
.step h3 { margin-bottom: .35rem; }
.step p { color: var(--ink-soft); margin-bottom: 0; }

/* --- Service cards -------------------------------------------------------
   Bordered panels, square, with a brand rule across the top — not the
   rounded drop-shadow card every other site in this trade uses. */
/* ⚠ NOT the 1px-gap-over-a-coloured-background trick. Ten services in three
   columns leaves two empty cells on the last row, and with that technique the
   background shows through them as grey boxes that look like broken cards.
   Real gaps and a border on each panel means an incomplete last row simply
   ends. */
.svc-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
@media (min-width: 44rem) { .svc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .svc-grid { grid-template-columns: repeat(3, 1fr); } }
.svc-grid > li { background: var(--card); border: 1px solid var(--rule); }
.svc-grid a {
  display: block; height: 100%; padding: 1.5rem 1.4rem 1.4rem;
  text-decoration: none; color: var(--ink);
  border-top: 3px solid transparent; transition: background .15s, border-color .15s;
}
.svc-grid a:hover, .svc-grid a:focus-visible {
  background: var(--paper); border-top-color: var(--brand);
}
.svc-grid .icon { width: 2rem; height: 2rem; color: var(--brand); margin-bottom: .9rem; }
.svc-grid .svc-name {
  font-family: var(--display); font-size: 1.15rem; font-weight: 650;
  letter-spacing: -.01em; color: var(--brand-text); display: block; margin-bottom: .35rem;
}
.svc-grid .svc-name::after { content: ''; }
.svc-grid .svc-lede { display: block; color: var(--ink-soft); font-size: 1rem; }
.svc-grid .more {
  display: inline-block; margin-top: .9rem; font-weight: 600; font-size: .95rem;
  color: var(--brand-text);
}
.svc-grid a:hover .more { text-decoration: underline; }

/* --- A quiet ground, to break up a run of white sections ----------------- */
.section-sunk { background: var(--paper-sunk); }

/* --- Panels -------------------------------------------------------------- */
.panel {
  background: var(--card); border: 1px solid var(--rule);
  border-top: 3px solid var(--brand); padding: 1.6rem 1.5rem;
}
/* (9 Oct 2026) was style="margin-top:1rem" in the markup, which this site's own CSP (style-src 'self')
   refuses: the gap never appeared on the live pages and every page logged an error. */
.panel--spaced { margin-top: 1rem; }

.panel h3 { color: var(--brand-text); margin-bottom: .4rem; }
.panel p:last-child { margin-bottom: 0; }
.panel .icon { width: 1.9rem; height: 1.9rem; color: var(--brand); margin-bottom: .8rem; }

/* --- The dark band gets the watermark treatment too ---------------------- */
.band { position: relative; overflow: hidden; }
.band > .wrap { position: relative; }
.band-watermark {
  position: absolute; left: -4%; bottom: -30%; height: 150%; width: auto;
  opacity: .08; pointer-events: none; filter: saturate(0) brightness(2.4);
}

/* --- A big pull quote of a fact, used once a page at most ---------------- */
.pull {
  font-family: var(--display); font-weight: 650; letter-spacing: -.02em;
  font-size: clamp(1.5rem, 3.4vw, 2.1rem); line-height: 1.25;
  max-width: 24ch; color: var(--brand-text);
}

/* --- Scroll reveal -------------------------------------------------------
   Small and once only. Anything more on a site whose visitors are mostly
   older and often on an old phone is showing off at their expense. */
.reveal { opacity: 0; transform: translateY(14px); }
.reveal.is-in { opacity: 1; transform: none; transition: opacity .5s ease, transform .5s ease; }
@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal.is-in { opacity: 1; transform: none; transition: none; }
}
/* ⚠ THE CLASS IS ADDED BY SCRIPT, NEVER WRITTEN INTO THE HTML.
   The markup carries `data-reveal`; site.js turns that into `.reveal` and
   then `.is-in`. So if the script never runs — blocked, failed, switched off —
   nothing was ever hidden in the first place. Writing `class="reveal"` into
   the HTML instead would make a broken script into a blank white page, which
   is the single worst failure this site could have. */

/* A ghost button, for a dark ground where a second solid button would fight
   the first. Border and text both sit at 11.3:1 on the band. */
.btn-ghost { background: transparent; color: var(--paper); border-color: var(--paper); }
.btn-ghost:hover { background: var(--paper); color: var(--deep); }


/* A shorter dark hero for the inner pages — the same treatment, less of it,
   so a service page does not look like a second home page. */
.hero-inner { padding: calc(var(--band) * .8) 0; }
/* The home hero carries extra bottom padding because the clinic shelf sits on
   its lower edge; without it the cards overlap the "at a glance" strip. */
@media (min-width: 52rem) {
  .hero-home { padding-bottom: calc(var(--band) * 1.5); }
}
.hero-inner .hero-eyebrow a { color: var(--sky); }
.hero-icon { margin-bottom: 1rem; }
.icon-lg { width: 3rem; height: 3rem; color: var(--sky); }

/* ===========================================================================
   THE OWNER PANEL
   ---------------------------------------------------------------------------
   Built phone-first, because that is where she will actually use it — stood
   in a kitchen between appointments, not at a desk. Every control is a full
   -width tap target at small sizes; the two-column layout only appears when
   there is genuinely room for it.
   ======================================================================== */
.owner { background: var(--paper-sunk); min-height: 100vh; padding-bottom: 6rem; }

.owner-head { background: var(--deep); color: var(--paper); position: sticky; top: 0; z-index: 30; }
.owner-head-row { display: flex; align-items: center; gap: .9rem; padding: .7rem 0; }
.owner-head .brand-mark { height: 2.1rem; filter: saturate(0) brightness(2.6); }
.owner-head strong { display: block; color: #fff; font-size: 1.05rem; }
.owner-head span { font-size: .85rem; color: #c5d5de; }
.owner-view { margin-left: auto; color: var(--sky); font-weight: 600; text-decoration: none; white-space: nowrap; }

.owner-main { padding: 1.5rem 0 2rem; }
.owner-gate { max-width: 26rem; }
.owner-grid { display: grid; gap: 1.5rem; }
@media (min-width: 62rem) {
  .owner-grid { grid-template-columns: 17rem 1fr; align-items: start; gap: 2.5rem; }
  .owner-side { position: sticky; top: 5.5rem; }
}

.owner-nav { display: none; }
@media (min-width: 62rem) {
  .owner-nav { display: grid; margin-bottom: 1.5rem; }
  .owner-nav a {
    padding: .5rem .6rem; text-decoration: none; color: var(--ink);
    border-left: 3px solid var(--rule); font-size: .95rem;
  }
  .owner-nav a:hover { border-left-color: var(--brand); background: var(--card); }
}

.owner-check { background: var(--card); border: 1px solid var(--rule); padding: 1.1rem; }
.owner-check h2 { font-size: 1.05rem; margin-bottom: .2rem; }
.checklist { list-style: none; margin: .6rem 0; padding: 0; }
.checklist li { display: flex; gap: .55rem; align-items: baseline; padding: .3rem 0; font-size: .95rem; }
.checklist .tick {
  flex: 0 0 auto; width: 1.15rem; height: 1.15rem; border: 2px solid var(--rule-strong);
  border-radius: 3px; font-size: .8rem; line-height: 1; text-align: center; color: #fff;
}
.checklist .done .tick { background: var(--good); border-color: var(--good); }
.checklist .done { color: var(--ink-muted); text-decoration: line-through; }
.checklist .todo { color: var(--ink); font-weight: 500; }

.panel-section { background: var(--card); border: 1px solid var(--rule); margin-bottom: .75rem; }
.panel-section > summary {
  display: flex; justify-content: space-between; align-items: center;
  padding: 1rem 1.1rem; cursor: pointer; font-weight: 650; font-size: 1.1rem;
  list-style: none; min-height: var(--tap);
}
.panel-section > summary::-webkit-details-marker { display: none; }
.panel-section > summary::after { content: '+'; color: var(--brand-text); font-size: 1.5rem; line-height: 1; }
.panel-section[open] > summary { border-bottom: 1px solid var(--rule); }
.panel-section[open] > summary::after { content: '–'; }
.section-body { padding: 1.1rem; }
.section-body .blurb { color: var(--ink-soft); margin-bottom: 1.2rem; max-width: 46ch; }

.owner .field { margin-bottom: 1.1rem; }
.owner .field input[type="text"], .owner .field input[type="password"], .owner .field textarea {
  width: 100%; min-height: var(--tap); padding: .65rem .75rem;
  font: 400 1.0625rem var(--sans); color: var(--ink);
  background: var(--card); border: 2px solid var(--rule-strong); border-radius: 4px;
}
.owner .field textarea { min-height: 5.5rem; resize: vertical; line-height: 1.5; }
.owner .field input:focus, .owner .field textarea:focus { border-color: var(--brand); }
.check { display: flex; gap: .6rem; align-items: center; font-weight: 600; min-height: var(--tap); cursor: pointer; }
.check input { width: 1.3rem; height: 1.3rem; flex: 0 0 auto; accent-color: var(--brand-text); }

.items { display: grid; gap: 1rem; }
.item { border: 1px solid var(--rule); border-left: 3px solid var(--brand); padding: 1rem; background: var(--paper); }
.item-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .9rem; }
.item-n { font-weight: 650; text-transform: capitalize; color: var(--brand-text); }
.item-tools { margin-left: auto; display: flex; gap: .35rem; }
.mini {
  min-width: 2.5rem; min-height: 2.5rem; padding: .3rem .6rem;
  background: var(--card); border: 1px solid var(--rule-strong); border-radius: 4px;
  font: 600 .9rem var(--sans); color: var(--ink); cursor: pointer;
}
.mini:hover { border-color: var(--brand); }
.mini.danger { color: var(--bad); border-color: var(--rule-strong); }
.mini.danger:hover { background: var(--bad); color: #fff; border-color: var(--bad); }

.owner-status { min-height: 1.6rem; font-weight: 600; margin-bottom: .8rem; }
.owner-status[data-state="ok"] { color: var(--good); }
.owner-status[data-state="bad"] { color: var(--bad); }

.owner-danger { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--rule-strong); display: grid; gap: .75rem; justify-items: start; }
.btn-quiet { background: transparent; color: var(--bad); border-color: var(--rule-strong); }
.btn-quiet:hover { background: var(--bad); color: #fff; border-color: var(--bad); }

/* The save bar. Fixed, because on a phone the Publish button being 40
   sections up the page is how an edit gets typed and then lost. */
.owner-savebar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--deep); color: var(--paper); padding: .7rem 0;
  border-top: 3px solid var(--sky);
}
.owner-savebar-row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.owner-savebar span { font-weight: 600; }
.owner-savebar .btn { margin-left: auto; }

/* A price field wears its £ outside the box, where it cannot be typed over. */
.field.money { position: relative; }
.field.money input { padding-left: 1.9rem; }
.field.money::after {
  content: '£'; position: absolute; left: .8rem; bottom: .72rem;
  font: 600 1.0625rem var(--sans); color: var(--ink-muted); pointer-events: none;
}
