/* ==========================================================================
   Aloha Dental Hygiene Studio
   Hand-authored stylesheet. No build step, no framework.

   Deep teal green and a deep navy on near-white, with a mint surface. Navy is
   the single accent in BOTH themes; the greens, mint and creams are grounds,
   not accents. Every pair is verified in scripts/check_contrast.py.

   LIGHT ONLY, on purpose. Every visitor gets the same page whatever their
   device is set to, which is what the reference design is. `color-scheme:
   light` on :root stops the browser auto-darkening form controls and
   scrollbars. There is no dark block and no theme toggle: if one is ever
   wanted, it gets built as a second set of values for these same tokens.

   Corner radius rule, applied everywhere:
     buttons and pills  -> fully rounded
     cards, panels, media -> var(--r)
     inputs             -> var(--r-input)
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------- */

@font-face {
  font-family: 'Cormorant Garamond';
  src: url('assets/fonts/cormorant-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Figtree';
  src: url('assets/fonts/figtree-latin.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens --------------------------------------------------------------- */

:root {
  color-scheme: light;

  --canvas: #F8FAF8;
  --card: #FFFFFF;
  --surface: #E4EFE9;
  /* The second alternate band. Deliberately a cool neutral rather than a
     paler mint: the page already carries three mint bands, and the whole
     point of the palette repaint was that a fourth reads as green
     everywhere. This alternates against the canvas without adding a hue. */
  --band: #EFF2F6;
  --deep: #0E3A34;
  --deep-2: #17554C;
  --ink: #1A2622;
  --ink-display: #073F46;
  --ink-soft: #4C5A53;
  --line: #D2DCD5;
  --accent: #0D574B;
  --accent-strong: #0D574B;
  --accent-hover: #09453C;
  --accent-on: #FBFAF6;
  --accent-wash: #E3EEE9;
  --hero-glow: #79B8A8;

  /* Buttons, 2026-10-07: every button on the site takes the Home & Community
     Care page's green, at Skylar's request, on all four pages. They used to
     borrow --accent-strong, which is navy everywhere but that page. The
     on-deep group below rebinds these to the cream button, because green on
     the deep ground would not read. Since 2026-10-13 headings, links and
     labels are green too (--accent, --ink-display): no navy is left. */
  --btn: #0D574B;
  --btn-hover: #09453C;
  --btn-on: #FBFAF6;

  /* Text and rules that sit on the deep ground, in either theme. */
  --on-deep: #F2F7F4;
  --on-deep-soft: #B6C6C0;
  --on-deep-line: #2C5A50;

  --shadow-1: 0 2px 10px rgb(12 40 34 / 0.05);
  --shadow-2: 0 14px 34px rgb(12 40 34 / 0.10);
  --shadow-3: 0 28px 70px rgb(8 32 27 / 0.18);

  --display: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --text: 'Figtree', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --r: 18px;
  --r-input: 12px;
  --measure: 62ch;
  --wrap: 1200px;
  --gutter: clamp(20px, 5vw, 56px);
  --section: clamp(56px, 8vw, 112px);
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
}

/* --- Base ----------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 90px;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--text);
  font-size: 1.0625rem;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg,
picture {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

a {
  color: inherit;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

::selection {
  background: var(--accent-strong);
  color: var(--accent-on);
}

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: 16px;
  top: -60px;
  z-index: 100;
  padding: 12px 20px;
  border-radius: 999px;
  background: var(--deep);
  color: var(--on-deep);
  font-weight: 600;
  text-decoration: none;
  transition: top 160ms var(--ease);
}

.skip-link:focus {
  top: 16px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Type ----------------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  margin: 0;
  text-wrap: balance;
}

h1,
h2 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.012em;
  color: var(--ink-display);
}

h1 {
  font-size: clamp(2.5rem, 5.4vw, 4.15rem);
}

h2 {
  font-size: clamp(2rem, 3.9vw, 3.1rem);
}

h3 {
  font-family: var(--text);
  font-size: 1.2rem;
  font-weight: 650;
  line-height: 1.3;
  letter-spacing: -0.008em;
}

h4 {
  font-family: var(--text);
  font-size: 1.0625rem;
  font-weight: 650;
  line-height: 1.35;
}

p {
  margin: 0;
  text-wrap: pretty;
}

.lead {
  font-size: clamp(1.125rem, 1.7vw, 1.3rem);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 54ch;
}

.eyebrow {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 650;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent);
}

.muted {
  color: var(--ink-soft);
}

.fine {
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* --- Layout --------------------------------------------------------------- */

.wrap {
  width: min(var(--wrap), 100% - (var(--gutter) * 2));
  margin-inline: auto;
}

.section {
  padding-block: var(--section);
}

.section--surface {
  background: var(--surface);
}

/* Restating `color` is required: `color` inherits as a computed value, so
   redefining --ink alone would leave descendants on the body's ink.

   Every block that paints itself onto the deep ground has to rebind these, not
   only the full-width sections. Navy clears neither the 4.5:1 text threshold
   nor the 3:1 UI one against #0E3A34, so a block that sets
   `background: var(--deep)` without this inherits an unreadable headline,
   label and primary button. The promo card and the footer were both doing
   exactly that, which is why the promo read as an odd green slab and the
   footer's VISIT / CONTACT labels looked washed out. */
.section--deep,
.promo,
.footer,
.header:not(.header--light),
.hm-comm,
.hero {
  --ink: var(--on-deep);
  --ink-display: var(--on-deep);
  --ink-soft: var(--on-deep-soft);
  --line: var(--on-deep-line);
  --surface: var(--deep-2);
  --card: var(--deep-2);
  --accent: #A8D8C4;
  --accent-strong: #F3F8F5;
  --accent-hover: #FFFFFF;
  --accent-on: #0E3A34;
  --accent-wash: #17554C;
  --btn: #F3F8F5;
  --btn-hover: #FFFFFF;
  --btn-on: #0E3A34;
  color: var(--ink);
}

.section--deep {
  background: var(--deep);
}

/* Bands alternate so no two neighbouring sections share a ground. Reading down
   the home page: mint, canvas, mint, band, canvas, band, mint, canvas, band.
   Three mint, three band, and never two of anything in a row. Moving a section
   means checking its neighbours, or the run that made the middle of the page
   read as one long block comes straight back. */
.section--band {
  background: var(--band);
}

/* The band that carries only the terms row. Full section padding around one
   row of three short paragraphs read as a gap rather than a section. */
.section--terms {
  padding-block: clamp(30px, 3.4vw, 54px);
}

/* Centred over the section rather than ranged left. The heading and the
   standfirst get separate measures: a heading wants to break early and a
   paragraph wants a comfortable line length, and one max-width cannot do both
   once the block is centred. */
.section-head {
  max-width: 64ch;
  margin-inline: auto;
  margin-bottom: clamp(32px, 4vw, 56px);
  text-align: center;
}

.section-head h2 {
  max-width: 22ch;
  margin-inline: auto;
}

.section-head p {
  margin: 16px auto 0;
  max-width: 54ch;
  color: var(--ink-soft);
}

.icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  fill: currentColor;
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 52px;
  padding: 14px 28px;
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  font-size: 1rem;
  font-weight: 650;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 140ms var(--ease), background-color 140ms var(--ease),
    border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}

.btn .icon {
  width: 1.1em;
  height: 1.1em;
}

.btn--primary {
  background: var(--btn);
  color: var(--btn-on);
  box-shadow: var(--shadow-1);
}

/* The outline button is the same green, as an outline, so the pair still
   reads as first and second choice. */
.btn--secondary {
  background: transparent;
  color: var(--btn);
  border-color: color-mix(in srgb, var(--btn) 45%, transparent);
}

.btn--ghost {
  min-height: 44px;
  padding: 10px 18px;
  background: transparent;
  color: var(--ink);
  font-weight: 600;
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    background: var(--btn-hover);
    box-shadow: var(--shadow-2);
  }

  .btn--secondary:hover {
    border-color: var(--btn);
    background: color-mix(in srgb, var(--btn) 7%, transparent);
  }

  .btn--ghost:hover {
    border-color: var(--ink);
    background: color-mix(in srgb, var(--ink) 6%, transparent);
  }
}

.btn:active {
  transform: translateY(1px);
}

.text-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--accent);
  font-weight: 650;
  text-decoration: none;
}

.text-link .icon {
  width: 1em;
  height: 1em;
  transition: transform 160ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .text-link:hover {
    text-decoration: underline;
  }

  .text-link:hover .icon {
    transform: translateX(3px);
  }
}

/* --- Header --------------------------------------------------------------- */

.header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--deep) 92%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

@supports not (backdrop-filter: blur(4px)) {
  .header {
    background: var(--deep);
  }
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: 74px;
}

/* Icon as a small image, wordmark as live text. Keeps the header a couple of KB,
   stays crisp at any size, and needs no separate artwork for dark mode. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: none;
  color: var(--ink);
  text-decoration: none;
}

.brand__mark {
  width: 52px;
  height: auto;
}

.brand__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.brand__name {
  font-family: var(--display);
  font-size: 1.62rem;
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  /* Trailing letter-space would push the wordmark off centre. */
  margin-right: -0.2em;
}

.brand__sub {
  font-size: 0.545rem;
  font-weight: 650;
  line-height: 1;
  letter-spacing: 0.163em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-right: -0.163em;
}

.brand--footer {
  color: var(--on-deep);
}

.brand--footer .brand__mark {
  width: 58px;
}

.brand--footer .brand__sub {
  color: var(--on-deep-soft);
}

.nav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

.nav a {
  padding: 10px 14px;
  border-radius: 999px;
  font-size: 0.97rem;
  font-weight: 550;
  white-space: nowrap;
  text-decoration: none;
  color: var(--ink-soft);
  transition: color 140ms var(--ease), background-color 140ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .nav a:hover {
    color: var(--ink);
    background: color-mix(in srgb, var(--ink) 6%, transparent);
  }
}

.nav a[aria-current] {
  color: var(--ink);
  font-weight: 650;
}

.header .btn {
  flex: none;
}

/* The nav carries its own CTA for the mobile drawer; on desktop the one in the
   header bar is the only one shown. */
.nav .btn {
  display: none;
}

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

.nav-toggle .icon {
  width: 22px;
  height: 22px;
}

.nav-toggle__close {
  display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__close {
  display: block;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__open {
  display: none;
}

/* Between the full bar and the drawer the six items only fit if they sit
   closer; with "Home & community care" in the menu they were wrapping onto a
   second row from about 1100px down. */
@media (min-width: 931px) and (max-width: 1180px) {
  .nav {
    gap: 0;
  }

  .nav a {
    padding: 10px 8px;
    font-size: 0.93rem;
  }
}

@media (max-width: 930px) {
  .header__inner {
    min-height: 66px;
  }

  .brand__mark {
    width: 44px;
  }

  .brand__name {
    font-size: 1.36rem;
  }

  .brand__sub {
    font-size: 0.48rem;
  }

  .nav-toggle {
    display: inline-flex;
  }

  .nav {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin: 0;
    padding: 12px var(--gutter) 22px;
    background: var(--deep);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-2);
  }

  .nav:not([data-open]) {
    display: none;
  }

  .nav a {
    padding: 14px 12px;
    font-size: 1.05rem;
    border-radius: var(--r-input);
  }

  .header__cta {
    display: none;
  }

  .nav .btn {
    display: inline-flex;
    margin-top: 10px;
  }
}

/* --- Hero ----------------------------------------------------------------- */

/* Full-bleed deep ground, continuous with the header, so the portrait is in
   the room rather than in a box. Three planes: a soft glow and the reversed
   palm mark behind, the cut-out portrait in front, each moving at its own rate
   under the pointer. The figure is bottom-anchored with no padding beneath it
   and the image runs past the figure's box, so the section's own bottom edge
   is what crops her. That is what makes her part of the hero rather than a
   picture placed on it. */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--deep);
  padding-top: clamp(28px, 4vw, 56px);
}

.hero__planes {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.hero__photo {
  position: absolute;
  inset: 0;
  background-image:
    /* The overall tint, and nothing else. The scrim that carries the copy is a
       separate layer on ::after, because the two want different things: the
       tint keeps the plate in the brand's green everywhere, and the scrim has
       to sit under the copy, which is beside the plate on two columns and
       above it on one. They used to be one gradient, which meant the plate
       could only ever be as visible as the sub-headline's contrast allowed,
       and that line has about four per cent of headroom over its 4.5 floor.
       Separated, the copy keeps the ground it had and the rest of the plate
       came up from 0.62 to 0.44. Re-run `npm run hero` after touching it. */
    linear-gradient(rgb(14 58 52 / 0.44), rgb(14 58 52 / 0.44)),
    /* 1400 as the 1x, not 900. The plate paints about 1500 wide at a 1440
       viewport, so asking for 900 put a 1.7x upscale on top of a photograph
       that is already out of focus, and the leaves smeared into the wall. */
    image-set(
      url('assets/hero-room-1400.webp') 1x,
      url('assets/hero-room-1672.webp') 2x
    );
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  transform: translate3d(calc(var(--px, 0px) * 0.35), calc(var(--py, 0px) * 0.35), 0)
    scale(1.04);
}

.hero__photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    96deg,
    rgb(14 58 52 / 0.90) 0%,
    rgb(14 58 52 / 0.84) 30%,
    rgb(14 58 52 / 0.34) 54%,
    rgb(14 58 52 / 0) 68%
  );
}

/* Decorative only, so it is a background rather than an <img>. Lifted from the
   9% it used on the old flat ground, because the same watermark over a
   photograph has to compete with the plate's own texture to stay visible, but
   held at 13%: its cream strokes fall right behind the hero's sub-headline, and
   at 16% they lifted that line's worst-case ground to 4.25:1, under the 4.5
   floor. lab/hero_probe.mjs measures it. Raising this means re-measuring. */
.hero__mark {
  position: absolute;
  left: -60px;
  bottom: -30px;
  width: min(520px, 44vw);
  aspect-ratio: 2460 / 1875;
  background: url('assets/logo-mark-watermark-rev.webp') no-repeat center / contain;
  opacity: 0.13;
  transform: translate3d(calc(var(--px, 0px) * -0.6), calc(var(--py, 0px) * -0.6), 0);
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  align-items: end;
  gap: clamp(24px, 4vw, 56px);
}

.hero__copy {
  align-self: center;
  padding-block: clamp(20px, 3vw, 40px) clamp(56px, 8vw, 112px);
}

.hero__copy > * + * {
  margin-top: 22px;
}

.hero h1 {
  max-width: 21ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 30px;
}

/* The lettering, over her left shoulder. It carries its own soft scrim rather
   than relying on the hero tint: the plate is brightest exactly here, where
   the window is, and white lettering on a lit wall would vanish. A scrim only
   where the words are, never a full-frame one. */
.hero__script {
  position: absolute;
  /* Held a fixed distance from the viewport edge rather than a percentage of
     the grid column. The column's right edge moves as the wrap hits its cap and
     the gutter grows, so a percentage offset drifted the lettering back over
     her face at some widths. --edge is that gutter, whichever rule is setting
     it. The clearest ground in the frame is the blurred plant at the far right,
     measured at 5.0:1 for white ink. */
  --edge: calc((100vw - min(100vw - var(--gutter) * 2, var(--wrap))) / 2);
  /* Both numbers are swept, not picked, over four rounds at four widths each.
     The two axes trade against each other and the trade is steep. Low, across
     her shoulder, it can come 42px nearer and clear 3:1 outright. Up here,
     where it now sits, it has to stay at the right margin: 14px nearer costs
     three quarters of a point at 960 and 42px nearer lands it on her cheek at
     1.3:1. This pair reads 2.98, 2.98, 2.97, 3.02 from 960 up, which is why
     the lettering's floor in check_hero_contrast.py is 2.9 rather than 3.0 and
     why that file explains itself. Re-run `npm run hero` after any change
     here, to the plate, or to her crop. */
  top: clamp(75px, 26%, 160px);
  right: calc(var(--edge) * -1 + 12px);
  z-index: 3;
  width: clamp(150px, 19vw, 248px);
  height: auto;
  padding: 3%;
  /* A halo that follows the strokes, in place of the soft oval that used to sit
     behind the whole box. The oval had to be wide enough to cover the lettering
     and therefore showed as a smudge wherever it crossed her; stacked
     drop-shadows darken only the few pixels around each stroke, which is what
     lets the lettering sit over her shoulder and stay readable. */
  filter:
    drop-shadow(0 0 2px rgb(6 26 22 / 0.9))
    drop-shadow(0 0 3px rgb(6 26 22 / 0.85))
    drop-shadow(0 0 6px rgb(6 26 22 / 0.8))
    drop-shadow(0 0 13px rgb(6 26 22 / 0.65))
    drop-shadow(0 2px 22px rgb(6 26 22 / 0.5));
  transform: translate3d(calc(var(--px, 0px) * 0.6), calc(var(--py, 0px) * 0.5), 0);
}

.hero__figure {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  height: clamp(440px, 42vw, 600px);
  transform: translate3d(calc(var(--px, 0px) * 0.35), calc(var(--py, 0px) * 0.25), 0);
}

/* Taller than its box on purpose: the overflow below the figure is clipped by
   the hero, which crops her at the forearms rather than at the counter. This is
   bound to the portrait by class, not to every image in the figure: the
   lettering is a sibling and took the same height when the selector was a bare
   descendant one. */
.hero__portrait {
  height: 126%;
  width: auto;
  max-width: none;
  object-fit: cover;
  object-position: top center;
  filter: drop-shadow(0 26px 36px rgb(3 20 16 / 0.45));
}

@media (max-width: 900px) {
  .hero__grid {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .hero__copy {
    padding-block: 8px 36px;
  }

  .hero__figure {
    height: clamp(340px, 92vw, 480px);
    transform: none;
  }

  /* One column, so the copy is above the plate rather than beside it and the
     scrim has to run down instead of across. check_page_contrast.py caught
     this at 4.20:1 on the eyebrow when the scrim was still horizontal. */
  .hero__photo::after {
    background: linear-gradient(
      176deg,
      rgb(14 58 52 / 0.90) 0%,
      rgb(14 58 52 / 0.86) 36%,
      rgb(14 58 52 / 0.32) 64%,
      rgb(14 58 52 / 0) 80%
    );
  }

  /* On one column she is centred under the copy, so the lettering moves to the
     top left corner of her block, which is the only clear ground left. */
  .hero__script {
    top: 0;
    right: auto;
    left: 0;
    width: clamp(132px, 34vw, 176px);
    transform: none;
  }

  .hero__portrait {
    height: 118%;
  }

  .hero h1 {
    max-width: 18ch;
  }
}

@media (max-width: 560px) {
  .hero {
    padding-top: 22px;
  }

  .hero h1 {
    font-size: clamp(2.1rem, 9.2vw, 2.9rem);
  }

  .hero__mark {
    display: none;
  }
}

/* --- Trust row ------------------------------------------------------------ */

.trust {
  border-block: 1px solid var(--line);
  background: var(--canvas);
}

/* Four equal columns, each item centred inside its own column, with a rule
   between them. Left-aligning items of different lengths inside equal columns
   is what made the spacing look arbitrary. */
.trust ul {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  padding-block: 20px;
  list-style: none;
}

.trust li {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  padding-inline: clamp(8px, 1.1vw, 18px);
  font-size: 0.93rem;
  font-weight: 550;
  line-height: 1.35;
  text-align: left;
}

/* All four labels sit on one line at desktop, or the taller cell makes the
   dividers look misaligned. */
@media (min-width: 861px) {
  .trust li span {
    white-space: nowrap;
  }
}

.trust li + li {
  border-left: 1px solid var(--line);
}

.trust .icon {
  width: 21px;
  height: 21px;
  color: var(--accent);
}

@media (max-width: 860px) {
  .trust ul {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 18px;
  }

  /* On two columns the rule belongs on every second item, not every item. */
  .trust li + li {
    border-left: 0;
  }

  .trust li:nth-child(even) {
    border-left: 1px solid var(--line);
  }
}

@media (max-width: 460px) {
  .trust ul {
    grid-template-columns: 1fr;
    row-gap: 14px;
  }

  .trust li {
    justify-content: flex-start;
    padding-inline: 0;
  }

  .trust li:nth-child(even) {
    border-left: 0;
  }
}

/* --- Two paths ------------------------------------------------------------ */

.paths {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(18px, 2.4vw, 28px);
}

.path {
  --path-pad: clamp(26px, 3vw, 38px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: var(--path-pad);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--canvas);
  box-shadow: var(--shadow-1);
  transition: transform 260ms var(--ease), box-shadow 260ms var(--ease),
    border-color 260ms var(--ease);
}

/* Full-bleed to the card edge, so it reads as the card's own lid rather than
   as a picture someone pasted inside it. It replaces the icon chip that used
   to sit here: a photograph of the visit says what the icon was standing in
   for, and keeping both would say it twice.

   The frame exists so the ribbon has something to be absolute against, and so
   the ribbon's corners are clipped to the card's radius. */
.path__media {
  position: relative;
  overflow: hidden;
  width: calc(100% + var(--path-pad) * 2);
  margin: calc(var(--path-pad) * -1) calc(var(--path-pad) * -1) 24px;
}

/* max-width: none matters. The base rule gives every image max-width: 100%,
   which resolves against the card's CONTENT box and silently clipped this to
   the card width minus its padding, leaving the photograph short of the right
   edge by exactly one pad. */
.path__photo {
  display: block;
  width: 100%;
  max-width: none;
  height: clamp(158px, 18vw, 212px);
  object-fit: cover;
}

.path--studio .path__photo {
  object-position: center 62%;
}

.path--mobile .path__photo {
  object-position: center 72%;
}

.path h3 {
  font-family: var(--display);
  font-size: clamp(1.6rem, 2.3vw, 2rem);
  font-weight: 600;
  line-height: 1.1;
}

.path > p {
  margin-top: 12px;
  color: var(--ink-soft);
}

.path ul {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.path li {
  display: flex;
  gap: 10px;
  font-size: 0.97rem;
  line-height: 1.5;
}

.path li .icon {
  width: 19px;
  height: 19px;
  margin-top: 3px;
  color: var(--accent);
}

.path__foot {
  margin-top: auto;
  padding-top: 26px;
}

.path__foot .btn {
  width: 100%;
}

.tag {
  display: inline-block;
  margin-bottom: 14px;
  padding: 5px 13px;
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (hover: hover) and (pointer: fine) {
  .path:hover {
    transform: translateY(-5px);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
    box-shadow: var(--shadow-2);
  }
}

@media (max-width: 800px) {
  .paths {
    grid-template-columns: 1fr;
  }
}

/* --- Services: one featured tile plus a plain two-column list ------------- */

.services {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(28px, 3.5vw, 52px);
  align-items: start;
}

.services__feature {
  position: relative;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--deep);
  color: var(--on-deep);
  box-shadow: var(--shadow-2);
}

.services__feature img {
  width: 100%;
  height: 230px;
  object-fit: cover;
  object-position: center 30%;
}

.services__feature-body {
  padding: 26px clamp(22px, 2.4vw, 30px) clamp(26px, 2.6vw, 32px);
}

.services__feature h3 {
  font-family: var(--display);
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.12;
}

.services__feature p {
  margin-top: 12px;
  color: var(--on-deep-soft);
  font-size: 0.99rem;
}

.services__feature .price {
  display: block;
  margin-top: 18px;
  color: var(--accent-strong);
  font-size: 1.05rem;
  font-weight: 650;
}

.service-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px clamp(24px, 3vw, 40px);
}

.service-list li {
  border-top: 1px solid var(--line);
}

.service-btn {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  width: 100%;
  padding: 18px 4px;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: 6px;
  transition: color 140ms var(--ease);
}

.service-btn > .icon {
  width: 23px;
  height: 23px;
  margin-top: 2px;
  color: var(--accent);
  transition: transform 200ms var(--ease);
}

.service-btn__text {
  flex: 1;
  min-width: 0;
}

.service-btn h4 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.service-btn h4 span:last-child {
  font-weight: 550;
  font-size: 0.95rem;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.service-btn p {
  margin-top: 4px;
  font-size: 0.93rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

@media (hover: hover) and (pointer: fine) {
  .service-btn:hover > .icon {
    transform: scale(1.12);
  }

  .service-btn:hover h4 span:first-child {
    text-decoration: underline;
    text-underline-offset: 3px;
  }
}

@media (max-width: 1000px) {
  .services {
    grid-template-columns: 1fr;
  }

  .services__feature {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    align-items: center;
  }

  .services__feature img {
    height: 100%;
    min-height: 210px;
  }
}

@media (max-width: 700px) {
  .service-list {
    grid-template-columns: 1fr;
  }

  .services__feature {
    grid-template-columns: 1fr;
  }

  .services__feature img {
    min-height: 0;
    height: 200px;
  }
}

/* --- Price menu ----------------------------------------------------------- */

/* --- A hygienist, not a dental office --------------------------------------
   The three people in your care, in the order they act: Jennifer, the
   collaborating dentist by video, then your own dentist for anything beyond
   hygiene. One row of three, the first carrying a photograph of Jennifer at
   work behind a white scrim, the other two an icon. Arrows hang in the gaps,
   which is why the steps do not clip their overflow.
   -------------------------------------------------------------------------- */

.team {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.team__step {
  position: relative;
  isolation: isolate;
  padding: 28px 26px 30px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-1);
}

.team__step--jenny {
  padding-top: 236px;
  border-color: color-mix(in srgb, var(--deep) 18%, var(--line));
  --tile-pos: 50% 46%;
  --tile-size: 100% auto;
  --tile: image-set(
    url('assets/clinical-loupes-900.webp') 1x,
    url('assets/clinical-loupes-1300.webp') 2x
  );
}

.team__step--jenny::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  border-radius: inherit;
  background: var(--tile) no-repeat var(--tile-pos) / var(--tile-size);
}

/* Pixel stops, not percentages: the tile's height follows its neighbours, so
   a percentage scrim would move off the text. */
.team__step--jenny::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.04) 0px,
    rgb(255 255 255 / 0.2) 150px,
    rgb(255 255 255 / 0.94) 226px,
    #FFFFFF 262px
  );
}

.team__icon {
  width: 46px;
  height: 46px;
  padding: 11px;
  margin-bottom: 18px;
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--accent);
}

.team__step h3 {
  font-family: var(--display);
  font-size: 1.62rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink-display);
}

.team__step h3 + p {
  margin-top: 10px;
  font-size: 0.98rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.team__list {
  display: grid;
  gap: 8px;
  margin: 18px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

.team__list li {
  display: flex;
  gap: 9px;
  font-size: 0.95rem;
  line-height: 1.5;
}

.team__list .icon {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  flex: none;
  color: var(--accent);
}

.team__arrow {
  position: absolute;
  top: 40px;
  left: -28px;
  z-index: 2;
  width: 32px;
  height: 32px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--canvas);
  color: var(--accent);
}

@media (max-width: 900px) {
  .team {
    grid-template-columns: 1fr;
    gap: 28px;
  }

  .team__arrow {
    top: -30px;
    left: 50%;
    transform: translateX(-50%) rotate(90deg);
  }

  .team__step--jenny {
    padding-top: 210px;
  }

  .team__step--jenny::after {
    background: linear-gradient(
      180deg,
      rgb(255 255 255 / 0.04) 0px,
      rgb(255 255 255 / 0.2) 130px,
      rgb(255 255 255 / 0.94) 200px,
      #FFFFFF 236px
    );
  }
}

/* --- Statement band ------------------------------------------------------
   Used by for-care-communities.html only. The home page had a section on
   these classes too until it was rebuilt as the three-step care chain, and
   deleting them with it left that page rendering its comparison lists as raw
   bullets with the tick icons stacked above the text. scripts/check_classes.py
   is the guard against doing it again.
   ------------------------------------------------------------------------ */

.statement {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.86fr);
  gap: clamp(30px, 5vw, 74px);
  align-items: start;
}

.statement h2 {
  max-width: 16ch;
}

.statement__body > * + * {
  margin-top: 16px;
}

.compare {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  margin-top: 30px;
}

.compare > div {
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: color-mix(in srgb, var(--surface) 60%, transparent);
}

.compare h3 {
  margin-bottom: 12px;
  font-size: 1.0625rem;
  letter-spacing: 0;
}

.compare ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}

.compare li {
  display: flex;
  gap: 9px;
  font-size: 0.95rem;
  line-height: 1.5;
}

.compare .icon {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  flex: none;
  color: var(--accent);
}

@media (max-width: 880px) {
  .statement {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 520px) {
  .compare {
    grid-template-columns: 1fr;
  }
}

/* --- About ---------------------------------------------------------------- */

.about {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(30px, 4.5vw, 66px);
  align-items: center;
}

.about__figure {
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: var(--shadow-2);
}

.about__figure img {
  width: 100%;
  aspect-ratio: 3 / 3.7;
  object-fit: cover;
  object-position: center 18%;
}

/* The heading and the lettering share one row, with the heading centred
   against it. Letting the art set the row height is what keeps it from
   overlapping the paragraph below, which an absolutely placed version did. */
.about__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 2.5vw, 34px);
}

.about__script {
  flex: none;
  width: clamp(116px, 15vw, 176px);
  height: auto;
  /* Overhangs the row it sits in. Without this the art sets the row height and
     leaves a band of dead space under the heading before the first paragraph.
     The two ends are not equal: the art hangs well above the heading, where
     there is nothing to hit, and only a little below it, where the heart's
     tail was coming down onto the ascenders of the first paragraph.
     check_page_contrast.py caught that at 3.11:1 against the heart's stroke. */
  margin-block: clamp(-38px, -3vw, -18px) clamp(-16px, -1.1vw, -4px);
}

.about__body > * + * {
  margin-top: 18px;
}

.credentials {
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 12px;
}

.credentials li {
  display: flex;
  gap: 12px;
  line-height: 1.5;
}

.credentials .icon {
  width: 21px;
  height: 21px;
  margin-top: 3px;
  color: var(--accent);
}

@media (max-width: 860px) {
  .about {
    grid-template-columns: 1fr;
  }

  .about__figure {
    max-width: 380px;
  }
}

/* --- Facilities promo ----------------------------------------------------- */

.promo {
  position: relative;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--deep);
  color: var(--on-deep);
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  align-items: center;
}

.promo__body {
  padding: clamp(32px, 4vw, 56px);
}

.promo h2 {
  max-width: 18ch;
}

.promo p {
  margin-top: 16px;
  max-width: 46ch;
  color: var(--on-deep-soft);
}

.promo .btn--secondary {
  color: var(--on-deep);
  border-color: rgb(242 247 244 / 0.4);
}

@media (hover: hover) and (pointer: fine) {
  .promo .btn--secondary:hover {
    border-color: var(--on-deep);
    background: rgb(254 251 242 / 0.1);
  }
}

.promo__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 30px;
}

.promo__phone {
  display: flex;
  align-items: center;
  gap: 8px;
}

.promo__phone .icon {
  width: 18px;
  height: 18px;
}

.promo__phone a {
  color: var(--on-deep);
  font-weight: 650;
}

.promo__figure {
  align-self: stretch;
}

.promo__figure img {
  width: 100%;
  height: 100%;
  min-height: 300px;
  object-fit: cover;
  object-position: center 14%;
}

@media (max-width: 900px) {
  .promo {
    grid-template-columns: 1fr;
  }

  .promo__figure {
    order: -1;
  }

  .promo__figure img {
    min-height: 220px;
  }
}

/* --- FAQ ------------------------------------------------------------------ */

/* Two columns, filled down then across, so the first four questions are the
   left column and the rest are the right one. grid-auto-flow: column is what
   gives that; the default row flow would deal them left, right, left, right.

   GRID-TEMPLATE-ROWS IS THE QUESTION COUNT, HALVED AND ROUNDED UP. CSS cannot
   work that out for itself, so adding questions here means changing this
   number, or the extra ones start a third column off the side of the page. */
.faq {
  --faq-rows: 4;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(4, min-content);
  grid-auto-flow: column;
  column-gap: clamp(28px, 4vw, 56px);
  max-width: 1040px;
  margin-inline: auto;
  border-top: 1px solid var(--line);
}

/* A rule under every question and one across the top of the whole block. Doing
   it this way rather than with a border on the last item means the count can
   change without the styling following it. */
.faq details {
  border-bottom: 1px solid var(--line);
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 22px 2px;
  font-size: 1.08rem;
  font-weight: 650;
  line-height: 1.4;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary .icon {
  width: 22px;
  height: 22px;
  flex: none;
  color: var(--accent);
  transition: transform 220ms var(--ease);
}

.faq details[open] summary .icon {
  transform: rotate(180deg);
}

.faq details > div {
  padding: 0 2px 24px;
  max-width: var(--measure);
  color: var(--ink-soft);
}

@media (max-width: 760px) {
  .faq {
    display: block;
  }
}

.faq details > div > * + * {
  margin-top: 12px;
}

/* --- Contact -------------------------------------------------------------- */

.contact {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 44px);
}

/* Real cards. Three columns of loose text under a shared heading read as one
   undifferentiated block, which is what these were: the only thing separating
   them was a hairline under each title. */
.contact__block {
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.6vw, 32px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-1);
}

.contact__block h3 {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 18px;
  font-size: 1.12rem;
}

.contact__block h3 .icon {
  width: 42px;
  height: 42px;
  padding: 10px;
  flex: none;
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--accent);
}

/* Pins the button to the foot of the card whatever the other two contain, so
   the three cards end on a line. */
.contact__foot {
  margin-top: auto;
  padding-top: 22px;
}

.contact__foot .btn {
  width: 100%;
}

.contact__block p + p {
  margin-top: 10px;
}

.contact__block a {
  font-weight: 600;
}

.hours {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  font-size: 0.97rem;
}

.hours dt,
.hours dd {
  padding-block: 8px;
  border-top: 1px solid var(--line);
}

/* The first row in each of the two columns, so the list opens on the heading
   rather than on a rule. */
.hours > *:nth-child(-n + 2) {
  border-top: 0;
  padding-top: 0;
}

.hours dt {
  font-weight: 600;
}

.hours dd {
  margin: 0;
  /* Padding, not a column gap. A gap leaves a hole in the row rule, which then
     reads as two broken lines rather than one. */
  padding-left: 20px;
  color: var(--ink-soft);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 820px) {
  .contact {
    grid-template-columns: 1fr;
  }
}

/* --- Forms ---------------------------------------------------------------- */

.form {
  display: grid;
  gap: 18px;
  max-width: 620px;
}

.field {
  display: grid;
  gap: 7px;
}

.field label {
  font-size: 0.95rem;
  font-weight: 650;
}

.field .hint {
  font-size: 0.87rem;
  color: var(--ink-soft);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 50px;
  padding: 13px 15px;
  border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent);
  border-radius: var(--r-input);
  background: var(--canvas);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}

.field textarea {
  min-height: 130px;
  resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder {
  color: color-mix(in srgb, var(--ink-soft) 85%, transparent);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

.form__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

.form__gotcha {
  position: absolute;
  left: -9999px;
}

.form__status {
  padding: 16px 18px;
  border-radius: var(--r-input);
  background: var(--accent-wash);
  font-weight: 600;
}

.form__status:not([hidden]) + * {
  margin-top: 8px;
}

@media (max-width: 560px) {
  .form__row {
    grid-template-columns: 1fr;
  }
}

/* --- Section art (services page) -------------------------------------------
   Seven supplied compositions, one per section, each painted as that section's
   own full-bleed background. They are finished artwork rather than photographs
   to place: leaves, open field and lettering are already in the pixels, so the
   page's job is only to keep the live copy inside the open field and to keep
   the section close enough to the art's own proportions that the crop does not
   eat the parts that matter.

   Every rule here follows from three facts about the files:

   1. The six band compositions are 2172x724, which is 3:1, and the hero is
      1672x941, which is 16:9. A section wider in ratio than its art scales the
      art to the section's WIDTH and crops top and bottom, which is harmless:
      everything in these files sits in the middle band. A section TALLER in
      ratio scales to the height and crops the sides, which is not harmless,
      because the lettering and the scenic panels live at the edges. So the
      art-bearing sections carry tighter vertical padding than the rest of the
      site, to hold them near 3:1 and keep the crop on the safe axis.
   2. Where a section is wider in ratio than its art, no horizontal crop
      happens at all, so a position measured as a percentage of the ARTWORK is
      the same percentage of the SECTION. That is what makes the column widths
      below predictable rather than guessed.
   3. They paint full-bleed, so there is no honest 2x: a 1920px band would need
      3840px of source. check_sharpness.mjs names them as decor for the same
      reason as hero-room, and the note there explains it.

   Adding art means adding one modifier with --art and --art-sm and nothing
   else. Do not layer decoration over these; they have their own.
   -------------------------------------------------------------------------- */

.section--art {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-image: var(--art);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}

/* Tighter than --section on purpose, to hold these bands near the art's 3:1.
   See point 1 above before loosening it: every pixel of height past that ratio
   is two and a half pixels of width taken off the sides, and the sides are
   where the lettering is. lab/svc/measure.mjs prints the crop per section. */
.section.section--art {
  padding-block: clamp(26px, 3vw, 50px);
}

.section--art .svc-head {
  margin-bottom: clamp(16px, 2vw, 30px);
}

.section--art .svc {
  padding: clamp(16px, 1.7vw, 21px);
}

.section--art .svc__icon {
  width: 38px;
  height: 38px;
  margin-bottom: 12px;
}

.section--art .svc__icon .icon {
  width: 18px;
  height: 18px;
}

.section--art .svc__open {
  padding-top: 15px;
}

/* A shade smaller than the site's card heading, so "Denture and partial
   cleaning" sets on one line in a column that has to leave the right of the
   frame alone. */
.section--art .svc__name {
  font-size: 1.28rem;
}

/* .section--flush zeroes the section padding and .section--art puts some back;
   this is the one place both apply. */
.section--flush.section--art {
  padding-block: 0;
}

/* 2026-10-07: the Services page moved onto the homepage's paper, palm light
   and hibiscus (see the Services light plan at the end of this file). These
   four light bands no longer paint a composition: they are transparent over
   the paper on <main>, and Periodontal and Dentist evaluation take the sand
   band. Their old frames (svc-bg-cleanings / -prevention / -xrays /
   -whitening) stay on disk, unreferenced. What two of them carried that the
   page keeps is now separate: the pull-quote is real text (.svc-quote) and the
   handwriting is a cut-out (.svc-script). Without art to hold to 3:1, they
   take a little more air than the bands that still have frames. */
.art--cleanings,
.art--prevention,
.art--xrays,
.art--whitening {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.section.art--cleanings,
.section.art--prevention,
.section.art--xrays,
.section.art--whitening {
  padding-block: clamp(40px, 4.4vw, 68px);
}

/* The valley panel alone (svc-bg-coming-panel): the frame's cream and its
   bottom-left eucalyptus are matted out, so the paper runs up to the panel's
   angled edge. Same pixel size as the frame, so every crop rule below that
   was measured off the frame still holds. */
.art--coming {
  background-color: transparent;
  background-position: center right;
  --art: image-set(url('assets/svc-bg-coming-panel-1600.webp') 1x, url('assets/svc-bg-coming-panel-2172.webp') 2x);
  --art-sm: image-set(url('assets/svc-bg-coming-panel-1100.webp') 1x, url('assets/svc-bg-coming-panel-1600.webp') 2x);
}

.art--cta {
  background-color: var(--deep);
  background-position: center right;
  --art: image-set(url('assets/svc-bg-cta-1600.webp') 1x, url('assets/svc-bg-cta-2172.webp') 2x);
  --art-sm: image-set(url('assets/svc-bg-cta-1100.webp') 1x, url('assets/svc-bg-cta-1600.webp') 2x);
}

/* The hero frame matted (svc-bg-hero-photo): its left eucalyptus and flat
   cream are transparent, so the copy sits on the paper with palm light; the
   photograph, "Care looks good on you" and the quote are copied through. The
   branch at the lower right overlaps the photograph and is kept with it:
   the composition has no clean plate under it. */
.art--hero {
  background-color: transparent;
  --art: image-set(url('assets/svc-bg-hero-photo-1400.webp') 1x, url('assets/svc-bg-hero-photo-1672.webp') 2x);
  --art-sm: image-set(url('assets/svc-bg-hero-photo-900.webp') 1x, url('assets/svc-bg-hero-photo-1400.webp') 2x);
}

/* The column the live copy is allowed to use, so it never reaches the art's
   lettering. Both widths are percentages of the wrap, which at every width
   between 1100 and 2000px lands within a point or two of the same percentage
   of the viewport, and that is what the art is measured in.

   --narrow keeps the x-rays cards clear of the pull-quote, which begins at 58%
   of that file, and the travel cards clear of the scenic panel, whose angled
   edge reaches 57% at its narrowest.
   --wide keeps the whitening cards clear of the cursive at 78%. */
.art-col {
  width: 100%;
}

.art-col--narrow {
  width: min(100%, 54%);
}

.art-col--wide {
  width: min(100%, 71%);
}

/* The travel band needs more room than the other two: its scenic panel has an
   angled edge that reaches furthest left at the bottom, and the section runs
   taller than 3:1 at every width under about 1800, so the crop walks that edge
   further in again. */
.art--coming .art-col {
  width: min(100%, 43%);
}

/* One column inside either art column. The two-column head is sized for the
   full wrap; in 54% of it the standfirst is four words a line, and in 71% it
   squeezed the title into 444px, which is where "Whitening and denture care"
   was breaking while every other heading on the page sat on one line. */
.art-col--narrow .svc-head,
.art-col--wide .svc-head {
  grid-template-columns: 1fr;
  align-items: start;
}

/* --- Services hero ---------------------------------------------------------
   No plate and no photograph element: the whole composition is the section's
   background, and the only thing here is the copy that sits in the cream field
   at its left.

   The field ends where the lettering begins, at 34% of the frame. The copy is
   placed at 8% of the VIEWPORT rather than at the wrap's gutter, because the
   gutter grows past 300px on a wide screen and would have pushed the copy into
   the lettering; 8% is also the inset the reference uses. Everything else
   follows: the copy can be 24% wide before it reaches 32%, and the headline
   has to be small enough to set "Healthy smiles" inside that.
   -------------------------------------------------------------------------- */

.shero {
  display: flex;
  align-items: center;
  min-height: clamp(480px, 42vw, 680px);
  /* Biased up, not centred. The section is wider in ratio than the frame at
     every width this composition runs at, so the crop is vertical, and the
     hygienist's head is nearer the top edge than the bib is to the bottom. */
  background-position: center 20%;
}

/* A paper-toned lift under the copy, and only under the copy. It was cream
   over the frame's painted branch (0.62 took the darkest leaf under the
   sub-headline past 4.5:1). The branch is matted out now, so what it washes
   is the palm light, which keeps its fronds at the outer edge and lets the
   lines read on plain paper. Gone by 30%, before the lettering at 33%. */
.shero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
    90deg,
    rgb(248 238 223 / 0.30) 0%,
    rgb(248 238 223 / 0.62) 9%,
    rgb(248 238 223 / 0.62) 22%,
    transparent 31%
  );
  pointer-events: none;
}

.shero__copy {
  position: relative;
  z-index: 1;
  /* Margin, not padding: box-sizing is border-box everywhere on this site, so
     inline padding comes out of the width rather than being added to it, and
     the column was half the size it reads as. */
  margin-inline-start: max(var(--gutter), 8%);
  padding-block: clamp(30px, 4vw, 60px);
  width: min(24%, 44ch);
}

.shero__copy > * + * {
  margin-top: 18px;
}

.shero h1 {
  font-size: clamp(1.9rem, 3.4vw, 3.05rem);
  line-height: 1.06;
}

/* The second line in italic. Same family, same size: emphasis inside a
   headline belongs to the face it is already set in, not to a second serif
   dropped into the middle of it. */
.shero h1 em {
  display: block;
  font-style: italic;
}

.shero .lead {
  font-size: clamp(1rem, 1.15vw, 1.1rem);
  max-width: none;
}

.shero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 26px;
}

/* --- Section art ----------------------------------------------------------
   Two optional background plates. Both degrade to a plain ground if the image
   is not present, so the page is never broken by a missing asset.

   Drop the sources in assets/src/ and run `python scripts/build_assets.py`:
     leaf-corners.png  ->  the pale watercolour leaves for "What we can do for you"
     cta-leaves.jpg    ->  the dark tropical leaves behind the footer
   -------------------------------------------------------------------------- */

.section--leaves {
  position: relative;
  isolation: isolate;
  background-color: var(--canvas);

  --leaf-plate: image-set(
    url('assets/leaf-corners-1200.webp') 1x,
    url('assets/leaf-corners-1672.webp') 2x
  );
}

.section--leaves::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--leaf-plate, none) no-repeat center / 100% 100%;
  opacity: 0.34;
  pointer-events: none;
}

/* The plate is painted for a light ground, so in dark mode it is dropped
   rather than inverted into something muddy. */
/* --- Card flow (services) -------------------------------------------------

   With JavaScript off this is a plain responsive grid: every card is a real
   link to its entry on the services page and nothing is hidden. main.js adds
   `js` to the root element, and only then does it become a coverflow.

   Deep cards on the mint ground, so the section reads as one object rather than
   nine. The card face rebinds the on-deep tokens the same way .section--deep
   does, which is what keeps the accent visible on a dark card.
   -------------------------------------------------------------------------- */

.flow {
  margin-top: clamp(28px, 4vw, 44px);
}

.flow__stage {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(236px, 1fr));
  gap: 18px;
}

.flow__controls,
.flow__selector {
  display: none;
}

.flow__face {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
  min-height: 330px;
  padding: 28px 26px 26px;
  border: 1px solid color-mix(in srgb, var(--deep) 18%, var(--line));
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-2);
  text-decoration: none;
  color: var(--ink);
}

/* The photograph. --tile is set per card by the modifier classes below. */
.flow__face::before,
.serve__card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--tile, none) no-repeat var(--tile-pos, center)
    / var(--tile-size, cover);
}

/* The scrim. Light at the top so the picture reads, near-opaque behind the
   copy so the words never sit on raw photography. */
.flow__face::after,
.serve__card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.10) 0%,
    rgb(255 255 255 / 0.30) 30%,
    rgb(255 255 255 / 0.80) 56%,
    rgb(255 255 255 / 0.95) 70%,
    rgb(255 255 255 / 0.98) 100%
  );
}

.flow__face h3 {
  font-family: var(--display);
  font-size: 1.72rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink-display);
}

.flow__face p {
  margin-top: 12px;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* Each service's own copy for the dialog. Never shown on the card; main.js
   copies it into the dialog when the card is opened. */
.flow__detail {
  display: none;
}

/* Studio fees on the face, so the price is there before the dialog opens.
   A hairline above sets them apart from the description; each row is the
   service on the left and its fee on the right. */
.flow__prices {
  display: grid;
  gap: 2px;
  margin: 12px 0 0;
  padding: 9px 0 0;
  border-top: 1px solid var(--line);
  list-style: none;
  font-size: 0.86rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

.flow__prices li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

.flow__prices strong {
  flex: none;
  font-weight: 650;
  color: var(--ink-display);
  font-variant-numeric: tabular-nums;
}

.flow__more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  font-size: 0.86rem;
  font-weight: 650;
  color: var(--btn);
}

.flow__more .icon {
  width: 1em;
  height: 1em;
  transition: transform 160ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .flow__face:hover {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--on-deep-line));
  }

  .flow__face:hover .flow__more .icon {
    transform: translateX(3px);
  }
}

/* --- The coverflow, enhancement only -------------------------------------- */

.js .flow {
  --card-w: 340px;
  /* 452 until the faces carried their fees (2026-09-30). The extra height
     is the price rows, so the photograph shows as much as it did before. */
  --card-h: 500px;
  --near: 292px;
  /* The outer rank has to clear the middle one by more than the card's own
     28px padding or its heading loses its first word behind the card in
     front. At 340 wide that puts the floor at 550. */
  --far: 556px;
}

.js .flow__viewport {
  position: relative;
  height: calc(var(--card-h) + 40px);
  overflow: hidden;
  touch-action: pan-y;
}

.js .flow__stage {
  position: absolute;
  inset: 0;
  display: block;
  /* Perspective has to sit on the cards' direct parent or the rotateY on the
     side cards renders flat. */
  perspective: 2200px;
  grid-template-columns: none;
  gap: 0;
}

.js .flow__card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--card-w);
  height: var(--card-h);
  margin: calc(var(--card-h) / -2) 0 0 calc(var(--card-w) / -2);
  transition: transform 600ms var(--ease), opacity 600ms var(--ease);
  backface-visibility: hidden;
}

.js .flow__card[data-pos="0"] {
  transform: translate3d(0, 0, 0) scale(1);
  opacity: 1;
  z-index: 5;
}

.js .flow__card[data-pos="0"] .flow__face {
  border: 2px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: var(--shadow-3);
}

/* One tone step back, so the flanking cards read as behind the centre one
   rather than as three cards in a row. */
.js .flow__card:not([data-pos="0"]) .flow__face {
  background: var(--canvas);
}

.js .flow__card[data-pos="1"] {
  transform: translate3d(var(--near), 0, 0) scale(0.88) rotateY(-18deg);
  opacity: 1;
  z-index: 4;
}

.js .flow__card[data-pos="-1"] {
  transform: translate3d(calc(var(--near) * -1), 0, 0) scale(0.88) rotateY(18deg);
  opacity: 1;
  z-index: 4;
}

.js .flow__card[data-pos="2"] {
  transform: translate3d(var(--far), 0, 0) scale(0.74) rotateY(-22deg);
  opacity: 0.6;
  z-index: 3;
}

.js .flow__card[data-pos="-2"] {
  transform: translate3d(calc(var(--far) * -1), 0, 0) scale(0.74) rotateY(22deg);
  opacity: 0.6;
  z-index: 3;
}

.js .flow__card[data-pos="hidden"] {
  transform: scale(0.5);
  opacity: 0;
  pointer-events: none;
}

/* Only the centre card is reachable; the rest are inert until selected. */
.js .flow__card:not([data-pos="0"]) .flow__more {
  opacity: 0;
}

.js .flow__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 22px;
}

.flow__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--canvas);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 160ms var(--ease), background-color 160ms var(--ease);
}

.flow__arrow .icon {
  width: 20px;
  height: 20px;
}

@media (hover: hover) and (pointer: fine) {
  .flow__arrow:hover {
    border-color: var(--accent);
    background: var(--accent-wash);
  }
}

.flow__counter {
  min-width: 110px;
  text-align: center;
  font-size: 0.88rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* The panel names as one row that shares the width, two and one on a phone.
   Every panel stays reachable by name without a swipe, which is the only
   reason the row exists. */
.js .flow__selector {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  max-width: 1060px;
  margin: 30px auto 0;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: color-mix(in srgb, var(--card) 72%, transparent);
}

.flow__selector button {
  flex: 1 1 0;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 550;
  text-align: center;
  cursor: pointer;
  transition: color 160ms var(--ease), background-color 160ms var(--ease),
    border-color 160ms var(--ease);
}

.flow__selector button[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--btn) 40%, transparent);
  background: color-mix(in srgb, var(--btn) 9%, #FFFFFF);
  color: var(--btn);
  font-weight: 650;
}

@media (hover: hover) and (pointer: fine) {
  .flow__selector button:hover {
    color: var(--ink);
    background: color-mix(in srgb, var(--ink) 6%, transparent);
  }
}

@media (max-width: 1040px) {
  .js .flow {
    --card-w: 270px;
    --card-h: 424px;
    --near: 240px;
    --far: 440px;
  }

  /* At this card width a two-line name, a three-line description and three
     fees reach a third of the way up the card, so the scrim turns opaque
     sooner here than on the wider card. */
  .flow__face::after {
    background: linear-gradient(
      180deg,
      rgb(255 255 255 / 0.10) 0%,
      rgb(255 255 255 / 0.30) 16%,
      rgb(255 255 255 / 0.86) 32%,
      rgb(255 255 255 / 0.96) 46%,
      rgb(255 255 255 / 0.98) 100%
    );
  }
}

@media (max-width: 720px) {
  .js .flow {
    --card-w: min(300px, calc(100vw - 80px));
    --card-h: 448px;
    --near: 214px;
    --far: 372px;
  }

  .flow__selector button {
    flex-basis: calc(50% - 4px);
  }

  .flow__selector button {
    font-size: 0.83rem;
    padding: 10px 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .flow__card {
    transition: none;
  }
}

/* --- Who we work with ------------------------------------------------------
   Five photo tiles in one row, each one a link into the shared dialog. Same
   plate and scrim mechanism as the service cards, so the two sections are
   visibly one system without being the same layout.
   -------------------------------------------------------------------------- */

.serve {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 14px;
}

.serve__card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
  min-height: 330px;
  padding: 22px 20px 20px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-1);
  color: inherit;
  text-decoration: none;
  transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease),
    transform 180ms var(--ease);
}

.serve__card h3 {
  font-family: var(--display);
  font-size: 1.28rem;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--ink-display);
}

.serve__card > p {
  margin-top: 8px;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.serve__more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  font-size: 0.84rem;
  font-weight: 650;
  color: var(--accent);
}

.serve__more .icon {
  width: 1em;
  height: 1em;
  transition: transform 160ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .serve__card:hover {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
    box-shadow: var(--shadow-2);
    transform: translateY(-3px);
  }

  .serve__card:hover .serve__more .icon {
    transform: translateX(3px);
  }
}

/* Five into three leaves two over, and five into two leaves one over. In both
   cases the leftover cards stretch rather than leaving a hole in the grid. */
@media (max-width: 1080px) {
  .serve {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .serve__item:nth-child(4),
  .serve__item:nth-child(5) {
    grid-column: span 1;
  }

  .serve__item:nth-child(4) {
    grid-column: 1 / span 2;
  }

  .serve__item:nth-child(5) {
    grid-column: 3 / span 1;
  }
}

@media (max-width: 760px) {
  .serve {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .serve__item:nth-child(4) {
    grid-column: auto;
  }

  .serve__item:nth-child(5) {
    grid-column: span 2;
  }

  .serve__card {
    min-height: 260px;
  }
}

@media (max-width: 480px) {
  .serve {
    grid-template-columns: minmax(0, 1fr);
  }

  .serve__item:nth-child(5) {
    grid-column: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .serve__card {
    transition: none;
  }
}

/* --- Card plates ----------------------------------------------------------
   One rule per card. These are classes rather than style attributes because
   the CSP forbids inline styles: a style attribute here would be refused by
   the browser and the card would silently lose its picture.

   The photographs are stock from Pexels, fetched by scripts/fetch_stock.py
   and credited in assets/src/stock-credits.json. They illustrate the service,
   they are not Jennifer's patients, and no card claims otherwise. Replace
   them with her own photographs as she takes them.

   --tile is the pair for the card itself (1x, 2x). --tile-hd is the pair the
   dialog figure uses, one step larger, because it paints the same photograph
   about three times the size. A file smaller than the painted box times the
   device pixel ratio is upscaled and reads blurry; after any change to card,
   figure or tile sizes run `npm run sharp`, which measures exactly that.
   -------------------------------------------------------------------------- */

.flow__face--preventive {
  --tile: image-set(
    url('assets/svc-preventive-420.webp') 1x,
    url('assets/svc-preventive-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-preventive-700.webp') 1x,
    url('assets/svc-preventive-1300.webp') 2x
  );
}

.flow__face--deep {
  --tile: image-set(
    url('assets/svc-deep-420.webp') 1x,
    url('assets/svc-deep-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-deep-700.webp') 1x,
    url('assets/svc-deep-1300.webp') 2x
  );
}

.flow__face--gum {
  --tile: image-set(
    url('assets/svc-gum-420.webp') 1x,
    url('assets/svc-gum-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-gum-700.webp') 1x,
    url('assets/svc-gum-1300.webp') 2x
  );
}

.flow__face--sdf {
  --tile: image-set(
    url('assets/svc-sdf-420.webp') 1x,
    url('assets/svc-sdf-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-sdf-700.webp') 1x,
    url('assets/svc-sdf-1300.webp') 2x
  );
}

.flow__face--whitening {
  --tile-pos: 60% 38%;
  --tile: image-set(
    url('assets/svc-whitening-420.webp') 1x,
    url('assets/svc-whitening-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-whitening-700.webp') 1x,
    url('assets/svc-whitening-1300.webp') 2x
  );
}

.flow__face--xray {
  --tile: image-set(
    url('assets/svc-xray-420.webp') 1x,
    url('assets/svc-xray-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-xray-700.webp') 1x,
    url('assets/svc-xray-1300.webp') 2x
  );
}

.flow__face--teledentistry {
  --tile-pos: 45% 50%;
  --tile: image-set(
    url('assets/svc-teledentistry-420.webp') 1x,
    url('assets/svc-teledentistry-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-teledentistry-700.webp') 1x,
    url('assets/svc-teledentistry-1300.webp') 2x
  );
}

.flow__face--child {
  --tile-pos: 50% 42%;
  --tile: image-set(
    url('assets/svc-child-420.webp') 1x,
    url('assets/svc-child-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-child-700.webp') 1x,
    url('assets/svc-child-1300.webp') 2x
  );
}

.flow__face--denture {
  --tile: image-set(
    url('assets/svc-denture-420.webp') 1x,
    url('assets/svc-denture-700.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/svc-denture-700.webp') 1x,
    url('assets/svc-denture-1300.webp') 2x
  );
}

.serve__card--seniors {
  --tile-pos: 62% 30%;
  --tile-size: auto 130%;
  --tile: image-set(
    url('assets/aud-seniors-800.webp') 1x,
    url('assets/aud-seniors-1500.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/aud-seniors-1500.webp') 1x,
    url('assets/aud-seniors-2200.webp') 2x
  );
}

.serve__card--families {
  --tile-pos: 52% 62%;
  --tile-size: auto 150%;
  --tile: image-set(
    url('assets/aud-families-800.webp') 1x,
    url('assets/aud-families-1500.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/aud-families-1500.webp') 1x,
    url('assets/aud-families-2200.webp') 2x
  );
}

.serve__card--workplace {
  --tile-pos: 38% 30%;
  --tile-size: auto 125%;
  --tile: image-set(
    url('assets/aud-workplace-800.webp') 1x,
    url('assets/aud-workplace-1500.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/aud-workplace-1500.webp') 1x,
    url('assets/aud-workplace-2200.webp') 2x
  );
}

.serve__card--trades {
  --tile-pos: 55% 34%;
  --tile-size: auto 135%;
  --tile: image-set(
    url('assets/aud-trades-800.webp') 1x,
    url('assets/aud-trades-1500.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/aud-trades-1500.webp') 1x,
    url('assets/aud-trades-2200.webp') 2x
  );
}

.serve__card--business {
  --tile-pos: 38% 40%;
  --tile-size: auto 130%;
  --tile: image-set(
    url('assets/aud-business-800.webp') 1x,
    url('assets/aud-business-1500.webp') 2x
  );
  --tile-hd: image-set(
    url('assets/aud-business-1500.webp') 1x,
    url('assets/aud-business-2200.webp') 2x
  );
}
/* --- Reviews ---------------------------------------------------------------
   Two identical sets side by side, translated by exactly half the track, which
   is what makes the loop seamless. Each set is at least a viewport wide so the
   duplicate is never on screen next to its original.

   Under reduced motion the animation is dropped and the strip becomes an
   ordinary horizontal scroller, so the content is still reachable.
   -------------------------------------------------------------------------- */

.reviews__controls {
  display: flex;
  justify-content: flex-end;
  margin-top: -14px;
}

/* Five stars and one sentence, centred under the heading. Both are facts
   about the ten reviews below and not a rating we have assembled, which is
   also why no Review or AggregateRating markup is emitted anywhere. */
.reviews__badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: clamp(-18px, -1.5vw, -8px) auto 0;
  text-align: center;
}

.reviews__stars {
  display: flex;
  gap: 5px;
  color: var(--accent);
}

.reviews__stars .icon {
  width: 22px;
  height: 22px;
}

.reviews__badge p {
  margin: 0;
  max-width: 46ch;
  font-size: 1.02rem;
  font-weight: 550;
}

.reviews {
  margin-top: 26px;
  overflow: hidden;
  padding: 8px 0 16px;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.reviews__track {
  display: flex;
  width: max-content;
}

.reviews__set {
  display: flex;
  gap: 18px;
  padding-right: 18px;
  margin: 0;
  list-style: none;
  min-width: calc(100vw + 18px);
}

.review {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  width: clamp(304px, 30vw, 424px);
  padding: 26px 28px 22px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-1);
  transition: border-color 200ms var(--ease), box-shadow 200ms var(--ease);
}

/* A quote mark set in the display face, large and nearly transparent. It sits
   behind the text rather than beside it, which is what keeps it from reading as
   another piece of furniture in an already busy card. */
.review::before {
  content: '\201C';
  position: absolute;
  top: -6px;
  right: 16px;
  z-index: -1;
  font-family: var(--display);
  font-size: 6.5rem;
  line-height: 1;
  color: var(--accent);
  opacity: 0.11;
  pointer-events: none;
}

.review__stars {
  display: flex;
  gap: 3px;
  margin-bottom: 14px;
  color: var(--accent);
}

.review__stars .icon {
  width: 17px;
  height: 17px;
}

.review blockquote {
  flex: 0 0 auto;
  margin: 0 0 18px;
  font-size: 0.97rem;
  line-height: 1.6;
  color: var(--ink);
}

.review blockquote > * {
  margin: 0;
}

.review blockquote > * + * {
  margin-top: 0.85em;
}

.review blockquote ul {
  padding-left: 1.1em;
}

.review blockquote li + li {
  margin-top: 0.4em;
}

/* Six lines, then a button. main.js measures each card and only un-hides the
   button where the text actually overflows, so a one-line review never offers
   to expand nothing. With JS off the clamp is never applied and every review
   renders in full. */
.review.is-clamped blockquote {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  line-clamp: 6;
  overflow: hidden;
}

/* Every collapsed card is the same height by construction, not by the flex row
   stretching them to match. Three things have to hold for that:

     1. the quote box is always exactly six lines, whether the review runs to
        four words or four paragraphs. `height`, not `min-height`, because a
        clamped multi-paragraph review picks up margins between its paragraphs
        and would otherwise run past six lines' worth;
     2. the Read more button keeps its slot even on the three reviews short
        enough not to need one;
     3. only then can the row stop stretching.

   Without that third line, opening one review added its full height to all ten
   cards and left nine of them mostly white space. Guarded on .js because with
   JS off nothing is clamped and the reviews run to their real lengths, where
   stretching is what keeps them level. */
.js .review blockquote {
  min-height: 9.6em; /* six lines at line-height 1.6 */
}

.js .review.is-clamped blockquote {
  height: 9.6em;
}

.js .review__more[hidden] {
  display: block;
  visibility: hidden;
  pointer-events: none;
}

.js .reviews__set {
  align-items: flex-start;
}

.review__more {
  align-self: flex-start;
  /* The quote already carries an 18px bottom margin to hold the meta row off
     the text. The button sits inside that gap rather than adding to it. */
  margin: -8px 0 18px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--btn);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 650;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .review__more:hover {
    text-decoration: underline;
  }
}

.review__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  /* auto, not a fixed margin: this is what pins the meta row to the bottom of
     a card that is taller than its own content. */
  margin-top: auto;
  padding-top: 15px;
  border-top: 1px solid var(--line);
}

.review__avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--accent-strong);
  color: var(--accent-on);
  font-size: 0.88rem;
  font-weight: 650;
  letter-spacing: 0.02em;
}

.review__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.review__who {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.review__who strong {
  font-size: 0.95rem;
  font-weight: 650;
}

.review__who > span {
  font-size: 0.84rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

@media (prefers-reduced-motion: no-preference) {
  .reviews__track {
    animation: reviews-marquee 120s linear infinite;
  }

  .reviews:hover .reviews__track,
  .reviews__track:focus-within,
  .reviews__track[data-paused],
  .reviews__track[data-modal] {
    animation-play-state: paused;
  }

  @keyframes reviews-marquee {
    to {
      transform: translateX(-50%);
    }
  }
}

/* Under reduced motion the strip stops being a strip. A horizontal scroller
   would still need a sideways gesture to read, and the duplicate set only ever
   existed to make the animation loop, so both go and the six reviews become an
   ordinary grid. */
@media (prefers-reduced-motion: reduce) {
  .reviews {
    overflow: visible;
    mask-image: none;
    -webkit-mask-image: none;
  }

  .reviews__track {
    display: block;
    width: auto;
  }

  .reviews__set[aria-hidden='true'] {
    display: none;
  }

  .reviews__set {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
    gap: 18px;
    min-width: 0;
    padding-right: 0;
    width: min(var(--wrap), 100% - (var(--gutter) * 2));
    margin-inline: auto;
  }

  .review {
    width: auto;
    transition: none;
  }

  .reviews__controls {
    display: none;
  }
}

@media (hover: hover) and (pointer: fine) {
  .reviews__track:hover .review:not(:hover) {
    opacity: 0.5;
  }

  .review:hover {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
    box-shadow: var(--shadow-2);
  }
}

@media (max-width: 720px) {
  .review {
    width: min(78vw, 320px);
  }

  .reviews__controls {
    justify-content: flex-start;
    margin-top: 4px;
  }
}

/* --- Service dialog ------------------------------------------------------- */

.dialog {
  width: min(980px, calc(100% - 32px));
  max-height: calc(100dvh - 48px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--canvas);
  color: var(--ink);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}

.dialog::backdrop {
  background: rgb(6 26 22 / 0.62);
  backdrop-filter: blur(3px);
}

/* Photograph on the left, copy on the right. The layout is the scroll
   container, not the dialog, so the close button stays put on a phone where
   the copy runs longer than the screen. */
.dialog__layout {
  display: grid;
  grid-template-columns: minmax(0, 0.42fr) minmax(0, 0.58fr);
  max-height: calc(100dvh - 50px);
  overflow: auto;
}

/* Carries the trigger's own art class (flow__face--deep, serve__card--seniors)
   so the same --tile variables paint the same photograph with the same
   framing. No second list of file names, and no inline style for the CSP to
   refuse. */
.dialog__figure {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  padding: clamp(22px, 3vw, 32px);
  background: var(--deep);
  color: var(--on-deep);
  overflow: hidden;
}

/* The figure is far taller than a card, so it takes the card's larger files
   (--tile-hd) and paints them with cover rather than the card's zoomed
   --tile-size: the zoom was tuned to pull a subject into a small wide tile,
   and here it would only multiply the pixels needed. */
.dialog__figure::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--tile-hd, var(--tile, none)) no-repeat var(--tile-pos, center)
    / cover;
}

.dialog__figure::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgb(14 58 52 / 0.04) 0%,
    rgb(14 58 52 / 0.16) 44%,
    rgb(14 58 52 / 0.8) 76%,
    rgb(14 58 52 / 0.92) 100%
  );
}

.dialog__figure p {
  margin: 0;
  max-width: 16ch;
  font-family: var(--display);
  font-size: clamp(1.35rem, 2.2vw, 1.7rem);
  font-weight: 600;
  line-height: 1.15;
  text-wrap: balance;
}

.dialog__inner {
  padding: clamp(26px, 4vw, 40px) clamp(26px, 4vw, 44px);
}

.dialog__close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--canvas);
  color: var(--ink);
  cursor: pointer;
}

.dialog__close .icon {
  width: 20px;
  height: 20px;
}

.dialog__label {
  margin: 0 0 10px;
  padding-right: 48px;
  font-size: 0.78rem;
  font-weight: 650;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.dialog h3 {
  color: var(--ink-display);
  font-family: var(--display);
  font-size: clamp(1.6rem, 3vw, 2.05rem);
  font-weight: 600;
  line-height: 1.1;
  max-width: 18ch;
}

.dialog__price {
  display: inline-block;
  margin-top: 14px;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--accent);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.dialog__body {
  margin-top: 18px;
}

.dialog__body > * + * {
  margin-top: 14px;
}

.dialog__body h4 {
  margin-top: 22px;
}

.dialog__body ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}

.dialog__body li {
  display: flex;
  gap: 10px;
  line-height: 1.5;
}

.dialog__body li .icon {
  width: 19px;
  height: 19px;
  margin-top: 3px;
  color: var(--accent);
}

.dialog__detail > * + * {
  margin-top: 10px;
}

.dialog__detail > h4:first-child {
  margin-top: 0;
}

.dialog__who {
  margin-top: 22px;
  padding: 14px 18px;
  border-left: 3px solid var(--accent);
  border-radius: 0 12px 12px 0;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.dialog__who h4 {
  margin: 0 0 4px;
}

.dialog__who p {
  margin: 0;
  font-size: 0.97rem;
  line-height: 1.5;
}

.dialog__foot {
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

@media (max-width: 760px) {
  .dialog__layout {
    grid-template-columns: 1fr;
  }

  .dialog__figure {
    height: 220px;
    padding: 20px;
  }

  .dialog__figure p {
    font-size: 1.25rem;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .dialog[open] {
    animation: dialog-in 260ms var(--ease);
  }

  @keyframes dialog-in {
    from {
      opacity: 0;
      transform: translateY(10px) scale(0.985);
    }
  }
}

/* A review opens rather than growing its card. Expanding in place pushed the
   rest of the strip down the page and left the reader a long way from where
   they started, which is what made it feel wrong. */
.dialog--review {
  width: min(680px, calc(100% - 32px));
}

.dialog--review .dialog__inner {
  padding: clamp(30px, 4vw, 44px);
}

.dialog--review .review__stars {
  margin-bottom: 20px;
}

.dialog--review .review__stars .icon {
  width: 21px;
  height: 21px;
}

.dialog__quote {
  margin: 0;
  font-size: 1.06rem;
  line-height: 1.65;
}

.dialog__quote > * {
  margin: 0;
}

.dialog__quote > * + * {
  margin-top: 0.9em;
}

.dialog__byline {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}


/* --- Footer --------------------------------------------------------------- */

.footer {
  position: relative;
  isolation: isolate;
  padding-block: clamp(46px, 6vw, 76px) 34px;
  background: var(--deep);
  color: var(--on-deep);

  --cta-plate: image-set(
    url('assets/cta-leaves-1200.webp') 1x,
    url('assets/cta-leaves-2172.webp') 2x
  );

  /* Where the scrim stops. Since 2026-09-30 (Skylar: "see the background
     image better where it is not near words") it clears the whole margin
     beside the text column and a band along the top, and holds only over the
     column itself. --footer-edge is where the column starts: the wrap's own
     inset, or the gutter once the wrap fills the screen. The side ramp
     finishes 12px before the first letter. */
  --footer-edge: max(var(--gutter), (100% - var(--wrap)) / 2);
  --footer-ramp: 84px;
  --footer-clear-top: clamp(22px, 3.2vw, 48px);
}

.footer::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--cta-plate, none) no-repeat center / cover;
  pointer-events: none;
}

/* A scrim only over the plate, not a blanket overlay on the whole ground.
   check_contrast.py measures against a flat --deep ground, so wherever type
   sits the scrim still has to keep the photograph from lifting the real ground
   above what was measured.

   It used to be a flat 92% everywhere, which left the leaves at 8% and read as
   a plain dark band. The scrim is shaped now rather than uniform: two ramps
   intersected, holding over the type column and falling away over the top
   band and the two side margins, where the wrap leaves no words. Over the
   column it is 90%, down from 97, so a trace of the leaves shows through
   there too; check_page_contrast.py measures the small labels on it.

   The old washout of the small VISIT / CONTACT labels was never the scrim's
   fault; it was `.footer h3` drawing navy --accent-strong on the deep ground.
   Fixing that is what lets the scrim come down at all.

   mask-composite: intersect is the whole effect. Where it is unsupported the
   layers fall back to `add`, a union, which leaves MORE scrim rather than less,
   so the degradation is safe for contrast. */
.footer::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--deep) 90%, transparent);
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0, #000 var(--footer-clear-top),
      #000 calc(100% - 16px), transparent 100%),
    linear-gradient(90deg,
      transparent 0,
      transparent calc(var(--footer-edge) - var(--footer-ramp)),
      #000 calc(var(--footer-edge) - 12px),
      #000 calc(100% - var(--footer-edge) + 12px),
      transparent calc(100% - var(--footer-edge) + var(--footer-ramp)),
      transparent 100%);
  mask-image:
    linear-gradient(180deg, transparent 0, #000 var(--footer-clear-top),
      #000 calc(100% - 16px), transparent 100%),
    linear-gradient(90deg,
      transparent 0,
      transparent calc(var(--footer-edge) - var(--footer-ramp)),
      #000 calc(var(--footer-edge) - 12px),
      #000 calc(100% - var(--footer-edge) + 12px),
      transparent calc(100% - var(--footer-edge) + var(--footer-ramp)),
      transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  pointer-events: none;
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 56px);
  padding-bottom: 34px;
  border-bottom: 1px solid var(--on-deep-line);
}

.footer__top p {
  margin-top: 18px;
  max-width: 34ch;
  color: var(--on-deep-soft);
  font-size: 0.97rem;
}

.footer h3 {
  margin-bottom: 14px;
  font-family: var(--text);
  color: var(--on-deep-soft);
  font-size: 0.83rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.footer a {
  color: var(--on-deep);
  font-size: 0.97rem;
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .footer a:hover {
    text-decoration: underline;
  }
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px 26px;
  padding-top: 26px;
  color: var(--on-deep-soft);
  font-size: 0.88rem;
}

@media (max-width: 800px) {
  .footer__top {
    grid-template-columns: 1fr 1fr;
  }

  .footer__brand {
    grid-column: 1 / -1;
  }
}

/* --- Page header (inner pages) -------------------------------------------- */

.page-head {
  padding-block: clamp(42px, 5.5vw, 78px) clamp(30px, 4vw, 52px);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.page-head p {
  margin-top: 18px;
  max-width: 56ch;
  font-size: clamp(1.05rem, 1.6vw, 1.22rem);
  line-height: 1.55;
  color: var(--ink-soft);
}

.breadcrumb {
  margin-bottom: 18px;
  font-size: 0.92rem;
  color: var(--ink-soft);
}

.breadcrumb a {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .breadcrumb a:hover {
    text-decoration: underline;
  }
}

/* --- Long-form prose ------------------------------------------------------ */

.prose {
  max-width: var(--measure);
}

.prose > * + * {
  margin-top: 16px;
}

.prose h3 {
  margin-top: 34px;
}

.prose ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.prose li {
  display: flex;
  gap: 10px;
  line-height: 1.55;
}

.prose li .icon {
  width: 19px;
  height: 19px;
  margin-top: 4px;
  color: var(--accent);
}

/* --- Section heads (services page) -----------------------------------------
   Ranged left with the standfirst beside them, not centred like the home
   page's. The home page's sections are each one idea and read best centred
   over their own block; this page is a price list, and a left-ranged heading
   with the qualifying sentence alongside it puts the reader's eye at the top
   left of the grid that follows, which is where the scanning starts.
   -------------------------------------------------------------------------- */

.svc-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  align-items: center;
  gap: clamp(16px, 3vw, 48px);
  margin-bottom: clamp(26px, 3.4vw, 46px);
}

.svc-head h2 {
  max-width: 28ch;
}

/* Under the heading, not over it. The heading names the group; this is the
   gloss on it, not a label introducing it. */
.svc-head__note {
  max-width: 46ch;
  color: var(--ink-soft);
}

/* A note set under its heading rather than beside it (the in-home band,
   2026-10-14): a full sentence reads better in body type than as a label. */
.svc-head h2 + .svc-head__note {
  margin-top: 12px;
}

/* The label over the first studio group, naming all four. */
.svc-head .eyebrow {
  margin-bottom: 10px;
}

/* A closing line and its button under a group's cards. */
.svc-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 22px;
  margin-top: clamp(18px, 2vw, 26px);
}

.svc-foot p {
  margin: 0;
  max-width: 46ch;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

@media (max-width: 760px) {
  .svc-head {
    grid-template-columns: 1fr;
    align-items: start;
  }
}

/* --- Service cards (services page) ----------------------------------------
   One card per service, carrying the name, the price and the qualifiers, so
   the page can be skimmed. The rest of the copy opens in a dialog rather than
   sitting on the page: thirteen services with two or three paragraphs each ran
   to a wall of prose nobody was going to read top to bottom, which is what
   made finding a price hard.

   Progressive enhancement, the same shape the reviews use. With no JavaScript
   the button stays hidden and the body stays on the page, which is the old
   layout and loses nothing. main.js reveals the button only once the dialog
   can actually open.
   -------------------------------------------------------------------------- */

.svc-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
}

.svc {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 30px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  box-shadow: var(--shadow-1);
  transition: border-color 200ms var(--ease), box-shadow 200ms var(--ease);
}

.svc__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 18px;
  border-radius: 50%;
  background: var(--surface);
  color: var(--deep);
}

.svc__icon .icon {
  width: 21px;
  height: 21px;
}

.svc__name {
  font-family: var(--display);
  font-size: 1.42rem;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--ink-display);
}

.svc__price {
  margin: 12px 0 0;
  font-size: 1.3rem;
  font-weight: 650;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.svc__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.svc__meta li {
  padding: 4px 11px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 68%, transparent);
}

/* The two travel cards run the icon beside the words rather than above them,
   which is what the reference has: they are short, and a stacked icon gave
   them the height of a full service card for two lines of copy. */
.svc--flat {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 16px;
  align-content: start;
}

.svc--flat .svc__icon {
  grid-column: 1;
  grid-row: 1 / -1;
  align-self: start;
  margin-bottom: 0;
}

.svc--flat > :not(.svc__icon) {
  grid-column: 2;
}

.svc--flat {
  padding: clamp(18px, 2vw, 24px);
}

.svc--flat .svc__price {
  margin-top: 6px;
}

.svc__note {
  margin: 14px 0 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.svc__open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding: 20px 0 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 650;
  color: var(--btn);
  text-align: left;
  cursor: pointer;
}

/* The button is the control, but the whole card is the target. Stretching the
   button over the card keeps one control and one accessible name rather than
   nesting anything inside a second clickable element. */
.svc__open::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.svc__open .icon {
  width: 1em;
  height: 1em;
  transition: transform 160ms var(--ease);
}

.svc__open:focus-visible {
  outline: none;
}

.svc__open:focus-visible::after {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.js .svc__body {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .js .svc:hover {
    border-color: color-mix(in srgb, var(--accent) 46%, var(--line));
    box-shadow: var(--shadow-2);
  }

  .js .svc:hover .svc__open .icon {
    transform: translateX(3px);
  }

  /* Nothing opens behind these two, so they must not offer the affordance the
     rest of the grid does. Listed after the rule above, not before: the two
     selectors carry the same weight and order is what decides. */
  .js .svc--flat:hover {
    border-color: var(--line);
    box-shadow: var(--shadow-1);
  }
}

/* Column counts per group, because the groups are not the same size: four
   cleanings, four preventive services, two records, three extras, two travel
   lines. auto-fit was tried first and is wrong here, since it stretches the
   two-card groups to half the page each and they stop looking like the same
   kind of card as the rows above. The media queries below come after these on
   purpose: they are the same specificity, so source order is what lets them
   collapse every count to two and then to one. */
.svc-grid--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.svc-grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.svc-grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.svc-grid--4 .svc {
  padding: clamp(18px, 2vw, 24px);
}

/* Paying, insurance and changes of plan: the three things people ring up to
   ask. They were three bolded sentences inside one bordered note, which is
   where a reader's eye stops going once everything above it is a grid. */
.terms {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: 0;
  padding: clamp(20px, 2.4vw, 30px) 0;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: var(--r);
  /* Warm white on the sand band since 2026-10-07, like the cards above it;
     it was the cool --band grey. */
  background: #FCFBF7;
  box-shadow: 0 6px 20px rgb(14 58 52 / 0.05);
  list-style: none;
}

.terms__item {
  display: flex;
  gap: 14px;
  padding-inline: clamp(18px, 2.4vw, 34px);
}

/* Drawn on the item rather than filled into the gap, so it takes itself away
   when the row wraps to one column. */
.terms__item + .terms__item {
  border-left: 1px solid var(--line);
}

.terms__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--r-input);
  color: var(--accent);
}

.terms__icon .icon {
  width: 19px;
  height: 19px;
}

.terms h4 {
  color: var(--ink-display);
}

.terms p {
  margin-top: 6px;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* The closing block runs to both edges rather than sitting in a rounded card.
   It is the last thing before the footer and the footer is deep too, so a card
   would have been a deep panel floating on its own colour. */
.section--flush {
  padding-block: 0;
}

/* Carries no ground and no figure of its own: the leaves, the lettering and
   the patient are all in the section's background. It keeps the .promo class
   only for the on-deep token block that class carries. */
.promo--flush {
  position: relative;
  display: block;
  border-radius: 0;
  background: none;
  box-shadow: none;
  min-height: clamp(280px, 24vw, 380px);
  display: flex;
  align-items: center;
}

/* A short lift under the copy only. The lettering on this frame starts at 48%
   and the portrait at 66%, so the gradient is finished well before either; it
   is there because the leaves behind the copy are a photograph and vary, not
   to darken the art. check_page_contrast.py is what sets how far it goes. */
.promo--flush::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
    90deg,
    rgb(9 40 35 / 0.52) 0%,
    rgb(9 40 35 / 0.30) 28%,
    transparent 46%
  );
  pointer-events: none;
}

.promo--flush > * {
  position: relative;
  z-index: 1;
}

/* .promo caps its heading at 18ch, which is right for the boxed one on the
   home page and two characters short of "Not sure what you need?" here.
   The size is a shade under the section headings for the same reason: the
   column cannot grow past the lettering at 48% of the frame, so the type has
   to fit the column rather than the column the type. Setting the heading on
   one line and the sentence on three also takes about 80px off the section,
   which at 1200 is the difference between a 92px side crop and none, and with
   no crop the lettering sits back at 48% instead of 44%. */
.promo--flush h2 {
  max-width: none;
  font-size: clamp(1.9rem, 2.85vw, 2.8rem);
}

.promo--flush .promo__body {
  padding-block: clamp(26px, 3vw, 44px);
  /* 8% of the viewport, like the hero, and for the same reason: the wrap's
     gutter grows past 300px on a wide screen and would have walked the copy
     into the lettering. */
  margin-inline-start: max(var(--gutter), 8%);
  /* Wide enough to set "Not sure what you need?" on one line and the sentence
     under it on three. The ceiling is the lettering, which starts at 48% of
     this frame: 8% in plus 36% wide leaves four points of clearance at the
     widths where the section crops hardest. */
  width: min(36%, 50ch);
}

.promo--flush .eyebrow {
  margin-bottom: 14px;
}

@media (max-width: 900px) {
  .terms {
    grid-template-columns: 1fr;
    padding-block: 0;
  }

  .terms__item {
    padding-block: clamp(20px, 5vw, 26px);
  }

  .terms__item + .terms__item {
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  /* The one-column rule for .promo lives further up the file, and this
     modifier is further down with the same specificity, so it has to undo
     itself here or the promo stays in two columns on a phone. */
  .promo--flush {
    grid-template-columns: 1fr;
  }

  .promo--flush .promo__body {
    padding-inline: var(--gutter);
  }

  /* One column, so the figure is a banner across the top and the arch moves to
     its bottom edge, where the copy meets it. */
  .promo--flush .promo__figure {
    border-radius: 0 0 50% 50% / 0 0 18% 18%;
  }
}

@media (max-width: 1000px) {
  .svc-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  .svc-grid {
    grid-template-columns: 1fr;
  }
}

/* The dialog the cards open into. */
.dialog--detail {
  width: min(650px, calc(100% - 32px));
}

.dialog--detail .dialog__inner {
  padding: clamp(28px, 4vw, 42px);
}

.dialog--detail .svc__meta {
  margin-top: 14px;
}

.dialog--detail .dialog__body {
  max-width: none;
}

.dialog--detail .dialog__body h4 {
  margin-top: 26px;
}

/* --- Reveal --------------------------------------------------------------- */

/* Opt in only when motion is welcome, so nothing is ever stuck invisible
   with JavaScript disabled or reduced motion on. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(16px);
  }

  .js .reveal.is-visible {
    opacity: 1;
    transform: none;
    transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- Print ---------------------------------------------------------------- */

@media print {
  .header,
  .footer,
  .hero__planes,
  .promo,
  .skip-link {
    display: none !important;
  }

  body {
    background: #fff;
    color: #000;
  }
}

/* --- Small spacing utilities ---------------------------------------------
   These exist so no page needs an inline style attribute, which lets the
   Content-Security-Policy stay strict (no 'unsafe-inline' for styles). */

.mt-0 { margin-top: 0; }
.mt-s { margin-top: 16px; }
.mt-m { margin-top: 26px; }
.mt-l { margin-top: 36px; }
.narrow { max-width: 820px; }

/* Per-photo focal points. object-position is picture-specific, so each image
   that needs a different crop gets a class rather than a blanket rule. */
.focus-high { object-position: center 12% !important; }
.focus-low { object-position: center 45% !important; }

/* Long-form pages put their sections directly under the h1, so those headings
   are h2 semantically. They keep h3's visual weight. */
.prose--doc h2 {
  font-family: var(--text);
  font-size: 1.2rem;
  font-weight: 650;
  line-height: 1.3;
  letter-spacing: -0.008em;
  margin-top: 34px;
}

.prose--doc h2.mt-0 {
  margin-top: 0;
}

/* --- The art on a narrow screen -------------------------------------------
   Five of the seven compositions carry something at the right edge that the
   page must not cover: a pull-quote, a line of cursive, a scenic panel, a
   portrait. On a wide screen the live copy simply sits to the left of it. In
   one column there is no left of it, so the composition stops being a
   background and becomes a band of its own underneath the content: copy first,
   then the artwork, full width, at a crop chosen to keep the right-hand side
   of the frame rather than its empty middle.

   That band is a ::after on the section rather than an <img>, for one reason
   worth writing down: it is the SAME art as the desktop background, so making
   it an element would mean a second copy in the markup that only ever paints
   on one of the two layouts, and the risk that a later edit changes one and
   not the other. There is nothing in it to describe: every word it carries is
   decoration, and the live heading beside it says what the section is.

   Cleanings and Prevention keep their backgrounds here. Both are flat fields
   with leaves at the edges, and at one column the crop lands in the middle of
   the field, which is empty. They lose their leaves and read as plain cream
   and plain mint, which is exactly what they would have been anyway.

   The switch is at 1120px rather than the 1024 a desktop breakpoint would
   normally sit at, because 1024 is where the compositions stop working rather
   than where the screen stops being a desktop: lab/svc/measure.mjs puts the
   side crop at 799px of a 2172px frame on the travel band there, which is more
   than a third of the picture gone and the scenic panel walked halfway across
   the cards. -------------------------------------------------------------- */

@media (max-width: 1420px) {
  .art--coming {
    background-image: none;
  }

  .art--coming::after {
    content: '';
    display: block;
    margin: clamp(26px, 5vw, 40px) 0 calc(-1 * clamp(26px, 3vw, 50px));
    aspect-ratio: 3 / 1;
    background-image: var(--art-sm);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
  }

  .art--coming .art-col {
    width: 100%;
  }

  .art--coming .svc-head {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  }
}

@media (max-width: 1120px) {
  .art--hero,
  .art--cta {
    background-image: none;
  }

  .art--cta::after {
    content: '';
    display: block;
    /* Bleeds to both edges and sits on the section's bottom edge, under its
       padding, so the band is the last thing in the section rather than a
       picture floating in it. */
    margin: clamp(26px, 5vw, 40px) 0 calc(-1 * clamp(26px, 3vw, 50px));
    /* The frame's own ratio, so the band shows the whole composition with
       nothing cropped. Below 760 it tightens to 5:3 and anchors right, where
       a 3:1 strip would be 70px of nothing. */
    aspect-ratio: 3 / 1;
    background-image: var(--art-sm);
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
  }

  .art--cta::after {
    margin-bottom: 0;
  }

  /* The matted frame as a band under the copy. Its left 40% is transparent
     now, so the band is cropped to the right of the frame (5:4 pinned right
     starts at 30%, just short of the lettering at 33%), capped at 520px tall
     for tablets, and its top edge fades in from the paper. width: 100% so
     max-height shortens the band rather than narrowing it through
     aspect-ratio. */
  .shero {
    display: block;
    min-height: 0;
  }

  .shero::before {
    display: none;
  }

  .shero::after {
    content: '';
    display: block;
    width: 100%;
    margin-top: clamp(20px, 4vw, 32px);
    aspect-ratio: 5 / 4;
    max-height: 520px;
    background: var(--art-sm) no-repeat right center / cover;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 12%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 12%);
  }

  .shero__copy {
    width: auto;
    margin-inline: var(--gutter);
    padding-block: clamp(26px, 6vw, 44px) 0;
  }

  .shero h1 {
    font-size: clamp(2.1rem, 6.4vw, 3rem);
  }

  /* The columns existed to leave the right of a frame alone. There is no right
     of the frame any more. */
  .art-col--narrow,
  .art-col--wide,
  .art--coming .art-col {
    width: 100%;
  }

  .art-col--narrow .svc-head {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  }

  .promo--flush {
    display: block;
    min-height: 0;
  }

  .promo--flush::before {
    display: none;
  }

  .promo--flush .promo__body {
    width: auto;
    margin-inline: var(--gutter);
    padding-block: clamp(30px, 6vw, 48px) 0;
  }
}

@media (max-width: 760px) {
  .art--coming::after,
  .art--cta::after {
    aspect-ratio: 5 / 3;
    background-position: center right;
  }

  /* This block sits last in the file, so it has to hand the section heads back
     to the one-column rule they get everywhere else at this width. */
  .art-col--narrow .svc-head,
  .art--coming .svc-head {
    grid-template-columns: 1fr;
  }
}

/* --- Home & Community Care page (for-care-communities.html) ----------------
   Rebuilt 2026-09-30 to Skylar's mock, in its order and words, and moved the
   same day onto the homepage's paper, palm light and hibiscus, with the sand
   band on alternate sections (see the Home & Community Care light plan at
   the end of the homepage light plan). The page's five supplied
   compositions (cc-bg-*, cc-small-visits) are retired and kept only as
   files; the notes below are kept for if they come back.

   The grounds are all 1672x941, which is 16:9 (1.777). The rule from the
   services block applies unchanged and is worth restating, because it is the
   one thing that breaks these if it is forgotten: a section WIDER in ratio
   than its art scales the art to the section's width and crops top and bottom;
   a section TALLER in ratio scales to the height and crops the SIDES. Every
   one of these files keeps its botanicals at the edges, so the crop has to
   stay on the vertical axis.

   Where each file puts its content, measured off the sources:

     cc-bg-why     leaves only         x 85-100%, y 0-39%   (top right)
     cc-bg-visit   botanicals in all four corners, y 0-20% and y 70-100%
     cc-bg-form    botanicals in the left and right corners, y 0-20% and 60-100%
   -------------------------------------------------------------------------- */

.cc-art {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-image: var(--art);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}

/* A page-local input radius. The site uses 12px everywhere else; the reference
   for this page sets its fields nearer 9, and the form is the only place the
   difference is visible. */
.cc-page {
  --r-input: 9px;
}

/* --- Header, light ---------------------------------------------------------
   The base header paints on the deep ground and rebinds its tokens to the
   on-deep set to stay readable there. The modifier opts out of that rebind
   (see the .section--deep group near the top of this file) and repaints the
   bar white.

   Every page carries it since 2026-10-06, when Skylar asked for the Services
   and Privacy bars to match Home and Home & Community Care. A page without it
   falls back to the old deep-green bar, so a new page must add it. */
.header--light {
  background: color-mix(in srgb, #FFFFFF 88%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
}

@supports not (backdrop-filter: blur(4px)) {
  .header--light {
    background: #FFFFFF;
  }
}

/* The phone drawer was drawn for the deep header: a --deep panel whose links
   read because the deep header rebinds --ink to cream. Under the light header
   nothing rebinds, so the panel stayed deep and its links and button went
   dark on dark (on Home and Home & Community Care from 2026-09-22, and on
   every page once all four took this header on 2026-10-06). Found and fixed
   2026-10-07: the light header's drawer is white. */
@media (max-width: 930px) {
  .header--light .nav {
    background: #FFFFFF;
  }
}

/* `.nav a` sets the link colour and outranks `.btn--primary`, which left the
   drawer's Book button with grey text on its fill. */
.nav a.btn--primary {
  color: var(--btn-on);
}

@media (hover: hover) and (pointer: fine) {
  .nav a.btn--primary:hover {
    color: var(--btn-on);
    background: var(--btn-hover);
  }
}

/* The active item gets the reference's underline rather than the pill the
   dark bar uses, because on white the pill reads as a button.

   Matched on aria-current="page" and not on aria-current alone: main.js runs a
   scrollspy that puts aria-current="location" on whichever in-page link is in
   view, and it only clears that attribute from the links it manages. On the
   homepage, where Home carries a hard-coded aria-current="page", matching the
   bare attribute drew a second underline under About as soon as that section
   scrolled past. The spy'''s link still takes the weight change from the base
   rule, which is the part that is useful. */
.header--light .nav a[aria-current="page"] {
  position: relative;
  background: none;
}

.header--light .nav a[aria-current="page"]::after {
  content: '';
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 4px;
  height: 2px;
  border-radius: 2px;
  background: var(--deep);
}

/* --- Hero ------------------------------------------------------------------
   Since 2026-10-07 the hero is Skylar's supplied composition, "Warm ivory
   dental care scene.png" (cc-bg-hero-scene, 1983x793, 2.5:1), painted as the
   section's ground like the homepage hero: Jennifer and the patient on the
   right, palm light and hibiscus line art on the left, flat warm cream
   between. It replaces the separate photograph and this section's own palm
   shadow, both of which are now in the artwork.

   Measured off the source (the darkest pixel under each band of the copy,
   against --ink-soft): the field clears 4.5:1 from 13% to 45% of the frame's
   width at every height, so the copy column is 14% to 44%. The headline is
   darker and larger, needs only 3:1, and is held on one line; at 3.3vw it
   ends near 48%, still open light field, short of the patient's hair at 50%.

   Pinned LEFT TOP. Wherever the copy makes the section taller in ratio than
   2.5:1, cover scales to the height and the extra width comes off the RIGHT,
   which is foliage until about 85%. Pinned left, the field only widens under
   the copy; pinned right, it would slide out from under it. Pinned top,
   because Jennifer's hair touches the top of the frame.

   One file at every size (62KB). The frame is only 1983px wide, so there is
   no smaller file that stays sharp across a desktop, and the stacked band on
   a phone paints it at nearly twice the phone's width.
   -------------------------------------------------------------------------- */

.cc-hero {
  display: flex;
  align-items: center;
  min-height: clamp(420px, 40vw, 760px);
  background-color: transparent;
  background-position: left top;
  --art: url('assets/cc-bg-hero-scene-1983.webp');
}

.cc-hero__copy {
  position: relative;
  z-index: 1;
  width: min(30%, 30rem);
  margin-inline-start: max(var(--gutter), 14%);
  padding-block: clamp(32px, 3.6vw, 56px);
}

.cc-hero h1 {
  margin: 0;
  font-size: clamp(1.9rem, 3.3vw, 3rem);
  line-height: 1.07;
  white-space: nowrap;
}

/* "Care that comes to you": the second line of the heading in the mock, set
   in the display face in the page's green. */
.cc-hero__sub {
  margin: 10px 0 0;
  font-family: var(--display);
  font-size: clamp(1.45rem, 2.2vw, 2rem);
  font-weight: 600;
  line-height: 1.15;
  color: var(--accent);
}

.cc-hero__copy > p:not(.cc-hero__sub) {
  margin: 18px 0 0;
  max-width: 48ch;
  color: var(--ink-soft);
  font-size: clamp(0.98rem, 1.1vw, 1.06rem);
}

.cc-hero .btn {
  margin-top: 28px;
}

/* Stacked: copy centred on the paper, then the scene as a band beneath it.
   The band is 4:3, pinned right, so Jennifer and the patient fill it (from
   about 47% of the frame) instead of sitting small at the end of a 2.5:1
   strip, and capped at 520px tall so a tablet does not get a wall of photo.
   Its top edge fades in from the paper. */
@media (max-width: 1000px) {
  .cc-hero {
    display: block;
    min-height: 0;
    background-image: none;
    text-align: center;
  }

  .cc-hero__copy {
    width: auto;
    max-width: 640px;
    margin-inline: auto;
    padding: clamp(32px, 6vw, 48px) var(--gutter) 0;
  }

  .cc-hero h1 {
    font-size: clamp(2.1rem, 3.9vw, 3.5rem);
    white-space: normal;
  }

  .cc-hero__copy > p:not(.cc-hero__sub) {
    margin-inline: auto;
  }

  /* width: 100% is needed: with an auto width, max-height carries through
     aspect-ratio and narrows the band instead of shortening it. */
  .cc-hero::after {
    content: '';
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    max-height: 520px;
    margin-top: clamp(18px, 4vw, 30px);
    background: var(--art) no-repeat right top / cover;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 16%);
  }
}

/* --- Care in familiar surroundings ------------------------------------------ */

.cc-why {
  padding-block: clamp(46px, 5.4vw, 72px);
  --art: none;
}

/* One line at every width: at the site's 22ch heading cap it broke after
   "familiar" and the section read as spread out. On the narrowest phones the
   size follows the viewport so the line still fits. */
.cc-why .section-head h2 {
  max-width: none;
}

@media (max-width: 440px) {
  .cc-why .section-head h2 {
    font-size: min(2rem, 7.4vw);
  }
}

.cc-card {
  background: color-mix(in srgb, #FFFFFF 94%, transparent);
  border-radius: 16px;
  padding: clamp(22px, 2.2vw, 32px);
  box-shadow: var(--shadow-1);
}

.cc-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  background: #DFEBDD;
}

.cc-card__icon .icon {
  width: 23px;
  height: 23px;
  color: var(--deep);
}

.cc-card h3 {
  font-family: var(--display);
  font-size: 1.42rem;
  font-weight: 600;
}

/* Private homes and care communities, side by side: icon beside the words,
   as the mock sets them. The pricing card further down uses the same shape. */
.cc-places {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 26px);
  max-width: 980px;
  margin-inline: auto;
}

.cc-place {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}

.cc-place h3 {
  margin: 6px 0 6px;
}

.cc-place p {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.cc-setup {
  max-width: 70ch;
  margin: clamp(30px, 3.6vw, 46px) auto 0;
  text-align: center;
}

.cc-setup h3 {
  font-family: var(--display);
  font-size: clamp(1.5rem, 2.2vw, 1.9rem);
  font-weight: 600;
  color: var(--ink-display);
}

.cc-setup p {
  margin: 12px auto 0;
  color: var(--ink-soft);
}

/* --- How your visit works ---------------------------------------------------
   Three steps in a row, read left to right. Rebuilt 2026-10-06 from a narrow
   vertical timeline Skylar found hard to read. Each step is a card with an
   icon for what happens in it and its number on a small badge, and a dashed
   rule in each gap carries the eye on to the next card. On a phone the row
   becomes a column and the rule turns with it. The numbers are aria-hidden:
   the <ol> already says the order. */

.cc-visit {
  padding-block: clamp(50px, 5.6vw, 78px);
  --art: none;
}

.cc-steps {
  --steps-gap: clamp(18px, 2.4vw, 34px);
  --mark: 64px;
  --card-pad: clamp(26px, 2.6vw, 36px);
  --card-pad-x: clamp(20px, 2.2vw, 30px);
  max-width: 1080px;
  margin: clamp(6px, 0.8vw, 12px) auto 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--steps-gap);
}

.cc-steps li {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--card-pad) var(--card-pad-x);
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 14px;
  box-shadow: 0 6px 20px rgb(14 58 52 / 0.05);
}

/* The rule across each gap, level with the centre of the icon. */
.cc-steps li:not(:last-child)::after {
  content: '';
  position: absolute;
  top: calc(var(--card-pad) + var(--mark) / 2 - 1px);
  left: calc(100% + 3px);
  width: calc(var(--steps-gap) - 6px);
  border-top: 2px dashed color-mix(in srgb, var(--accent) 50%, transparent);
}

.cc-steps__mark {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--mark);
  height: var(--mark);
  border-radius: 999px;
  background: #DFEBDD;
  color: var(--deep);
}

.cc-steps__mark .icon {
  width: 30px;
  height: 30px;
}

.cc-steps__n {
  position: absolute;
  top: -4px;
  right: -6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 2px solid #FCFBF7;
  border-radius: 999px;
  background: var(--accent);
  color: #FFFFFF;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.cc-steps h3 {
  margin: 20px 0 8px;
  font-family: var(--display);
  font-size: clamp(1.32rem, 1.7vw, 1.5rem);
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink-display);
}

.cc-steps p {
  margin: 0 auto;
  max-width: 34ch;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* Each card comes in just after the one before it. */
@media (prefers-reduced-motion: no-preference) {
  .js .cc-steps li.reveal.is-visible:nth-child(2) {
    transition-delay: 110ms;
  }

  .js .cc-steps li.reveal.is-visible:nth-child(3) {
    transition-delay: 220ms;
  }
}

.cc-visit__foot {
  max-width: 64ch;
  margin: clamp(28px, 3.4vw, 44px) auto 0;
  text-align: center;
}

.cc-visit__foot p {
  margin: 0 0 14px;
  color: var(--ink-soft);
}

/* --- For care communities ---------------------------------------------------- */

.cc-communities {
  padding-block: clamp(40px, 4.6vw, 64px);
  --art: none;
}

.cc-communities__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 60px);
  align-items: center;
}

.cc-communities__grid > div:first-child p {
  margin: 14px 0 0;
  max-width: 52ch;
  color: var(--ink-soft);
}

/* --- Request an on-site visit ------------------------------------------------ */

.cc-enquiry {
  padding-block: clamp(50px, 5.6vw, 78px);
  --art: none;
}

.cc-enquiry .section-head {
  margin-bottom: clamp(24px, 2.6vw, 34px);
}

.cc-form {
  max-width: 760px;
  margin-inline: auto;
  gap: 16px;
}

.cc-form .field input,
.cc-form .field textarea {
  min-height: 46px;
  padding: 11px 14px;
}

.cc-form .field textarea {
  height: 100px;
}

.cc-form .field .hint {
  margin-top: 6px;
}

/* Private home or care community: a real radio pair, drawn as two buttons.
   The inputs stay in the page (visually hidden, still focusable), so the
   choice works with the keyboard, with a screen reader and with JavaScript
   off, and it arrives in the submission as "setting". */
.cc-choice {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 0 0 4px;
  padding: 0;
  border: 0;
}

.cc-choice label {
  position: relative;
  flex: 0 1 220px;
}

.cc-choice input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.cc-choice span {
  display: block;
  padding: 11px 16px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  border-radius: var(--r-input);
  background: var(--card);
  color: var(--accent);
  font-size: 0.95rem;
  font-weight: 650;
  text-align: center;
  transition: background-color 160ms var(--ease), color 160ms var(--ease);
}

.cc-choice input:checked + span {
  border-color: var(--accent);
  background: var(--accent);
  color: #FFFFFF;
}

.cc-choice input:focus-visible + span {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.cc-form__send {
  text-align: center;
}

.cc-form__send .fine {
  margin-top: 12px;
}

/* --- Prefer to talk? ---------------------------------------------------------- */

.cc-talk {
  padding-block: clamp(40px, 4.6vw, 60px);
  text-align: center;
}

.cc-talk p {
  margin: 10px auto 0;
  color: var(--on-deep-soft);
}

.cc-talk p:first-of-type {
  margin-top: 16px;
}

.cc-talk a {
  color: var(--on-deep);
  font-weight: 650;
}

/* --- Phone ------------------------------------------------------------------
   The hero stacks and centres, copy then photograph, as in the mock.
   ---------------------------------------------------------------------------- */

@media (max-width: 860px) {
  .cc-places,
  .cc-communities__grid {
    grid-template-columns: 1fr;
  }

  .cc-steps {
    --mark: 56px;
    --card-pad: 22px;
    --card-pad-x: 20px;
    grid-template-columns: 1fr;
    max-width: 560px;
  }

  .cc-steps li {
    flex-direction: row;
    align-items: flex-start;
    gap: 18px;
    text-align: left;
  }

  .cc-steps li:not(:last-child)::after {
    top: calc(100% + 3px);
    left: calc(var(--card-pad-x) + var(--mark) / 2 - 1px);
    width: 0;
    height: calc(var(--steps-gap) - 6px);
    border-top: 0;
    border-left: 2px dashed color-mix(in srgb, var(--accent) 50%, transparent);
  }

  .cc-steps__mark .icon {
    width: 26px;
    height: 26px;
  }

  .cc-steps h3 {
    margin: 6px 0 6px;
  }

  .cc-steps p {
    margin: 0;
    max-width: none;
  }
}

/* --- Phone, narrow ------------------------------------------------------------ */

@media (max-width: 520px) {
  .cc-place {
    grid-template-columns: 1fr;
    justify-items: start;
  }
}

/* --- Homepage --------------------------------------------------------------
   Ten supplied compositions, one per section, each painted as that section's
   full-bleed ground. Same convention as the services and care-communities
   pages, and the same hard rule: all ten are 1916x821, which is 2.33:1, so a
   section WIDER in ratio crops top and bottom (safe) and a section TALLER in
   ratio crops the sides, where every one of these keeps its botanicals. Every
   section below stays wider than 2.33.

   Where each frame puts its content, measured off the sources rather than
   guessed. Percentages are of the frame, and because no horizontal crop
   happens at these ratios they are also percentages of the section:

     home-hero         2172x724 (3:1, replaced 2026-09-23) | leaves x 0-15 |
                       OPEN x 15-55 | Jennifer x 58-82, head at y 9 | script
                       x 84-98, y 30-62
     home-two-ways     leaves both outer edges, x 0-13 and x 87-100
     home-services     leaves enter from the BOTTOM, y 42-100, x 0-12 and 82-100
     home-audiences    leaves x 0-9 and x 91-100
     home-hygienist    photo x 0-40, y 0-78 | OPEN x 42-78 | leaves x 80-97
     home-community    deep green, OPEN x 0-38 | people x 42-95, heads y 5-35
                       and y 35-90 | script x 80-95, y 20-40
     home-jenny        portrait card x 7-35, y 15-83 | OPEN x 38-75 | script
                       x 78-92, y 13-25 | leaves x 88-100
     home-testimonials leaves x 0-8 and x 92-100
     home-booking      pale blue, leaves x 0-7 and x 93-100
     home-footer       deep green, botanical texture at both edges

   Four of them carry people, and those four drive the section heights. A
   section at the reference's height shows only about 45-50% of the frame,
   which cuts heads off. The hygienist, care-communities and Meet Jenny bands
   are therefore taller here than the reference mockup is: the mockup is a
   generated picture of a page and re-drew its own versions of these frames at
   a smaller scale, which the real files cannot do without losing the subject.
   The note on each section says what it is holding on to.
   -------------------------------------------------------------------------- */

.hm-art {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-image: var(--art);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
}

.home-page {
  --r-input: 10px;
}

/* --- Tropical background system (2026-09-28) ---------------------------------
   Replaces the painted-leaf frames behind the light sections. Bottom to top:

     1. .aloha-surface  one ivory paper canvas on <main>, tiled, never
                        stretched. The light sections are transparent over
                        it, so the paper runs across every boundary with no
                        seam. The dark bands (benefit strip, care
                        communities, footer) keep their own opaque grounds
                        and read as breaks in it.
     2. palm light      .aloha-palm-left / -right: a large blurred shadow
                        cast from outside the page, clipped by its section.
     3. gold hibiscus   .aloha-flower-single / -cluster, used sparingly.
     4. content         every card, photo, button and line of text.

   Layers 2 and 3 are the ::before and ::after of one empty, aria-hidden
   .aloha-decor div per section. It is absolutely positioned at z-index -1
   inside the section's own stacking context (.hm-art is isolated and
   overflow-clipped), so it paints above the section's background and below
   all in-flow content. It takes no space, catches no pointer, and cannot
   scroll the page sideways. Sections place the layers through the custom
   properties below rather than through new rules.

   Sources in assets/src/aloha-backgrounds/, built by
   scripts/build_aloha_backgrounds.py, which also explains why the two
   shadows are rebuilt in one warm taupe and the flowers cleaned to their
   own gold. #F8EEDF is the paper's measured mean colour. */

.aloha-surface {
  background-color: #F8EEDF;
  background-image: url('assets/aloha-backgrounds/aloha-paper-texture.webp');
  background-repeat: repeat;
  background-size: 960px auto;
}

/* Every other light section carries a sand wash over the same paper, so
   neighbouring sections read as separate blocks instead of one continuous
   run of paper. The texture shows through; only the tone changes. Palm
   light and hibiscus still paint above it (they sit inside the section's
   own stacking context, above its background). */
.aloha-surface .aloha-band {
  background-color: rgb(222 199 163 / 0.30);
}

.aloha-decor {
  position: absolute;
  inset: 0 0 calc(-1 * var(--spill, 0px));
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.aloha-decor::before,
.aloha-decor::after {
  position: absolute;
  background-repeat: no-repeat;
  background-size: contain;
  pointer-events: none;
}

/* Palm light is a full-section layer carrying the shadow as its background,
   pushed past the section edge by --palm-edge, and placed vertically by
   --palm-y measured from --palm-from (top or bottom; a negative value hangs
   the shadow outside). Being section-sized lets it take
   a mask: where a section meets another LIGHT section, --fade-top /
   --fade-bottom dissolve the shadow over that distance, because real light
   does not stop on a straight line at an invisible boundary. Edges against
   the header or a dark band need no fade. */
.aloha-palm-left::before,
.aloha-palm-right::before {
  content: '';
  inset: 0;
  background-size: var(--palm-w, min(1100px, 76vw)) auto;
  opacity: var(--palm-o, 0.4);
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 var(--fade-top, 0px),
    #000 calc(100% - var(--fade-bottom, 0px)),
    transparent 100%
  );
}

.aloha-palm-left::before {
  background-image: url('assets/aloha-backgrounds/aloha-palm-shadow-left.webp');
  background-position: left var(--palm-edge, -120px) var(--palm-from, top) var(--palm-y, -60px);
}

.aloha-palm-right::before {
  background-image: url('assets/aloha-backgrounds/aloha-palm-shadow-right.webp');
  background-position: right var(--palm-edge, -120px) var(--palm-from, top) var(--palm-y, -60px);
}

.aloha-flower-single::after,
.aloha-flower-cluster::after {
  content: '';
  top: var(--flower-top, auto);
  right: var(--flower-right, auto);
  bottom: var(--flower-bottom, auto);
  left: var(--flower-left, auto);
  width: var(--flower-w, 150px);
  opacity: var(--flower-o, 0.8);
}

.aloha-flower-single::after {
  aspect-ratio: 640 / 638;
  background-image: url('assets/aloha-backgrounds/aloha-hibiscus-single.webp');
}

.aloha-flower-cluster::after {
  aspect-ratio: 720 / 622;
  background-image: url('assets/aloha-backgrounds/aloha-hibiscus-cluster.webp');
}

/* --- Light plan (final pass 2026-09-28) --------------------------------------
   Where the light falls, in one place. Warm paper first, occasional
   sunlight, rare gold. Most of the page is bare paper on purpose.

     hero          SHADOW  oversized, from the top-left corner (the right
                           half is photograph). The copy lift washes it under
                           the text, so it reads at the outer edge.
     two ways      .       single hibiscus tucked into the lower-left corner,
                           cropped by the viewport, at the turn into services
     services      SHADOWS (band) one from each side, in the margins beside
                           the coverflow; the right one sits lower so the pair
                           is not a mirror (added 2026-09-30)
     who we serve  SHADOW  the page's one big cross-section moment: enters low
     hygienist      (cont) at the right edge of Who we work with, runs down the
                           hygienist band's whole right margin and passes
                           under the dark care-communities band. A small
                           hibiscus is tucked into Who we work with's
                           upper-right corner.
     (care communities: dark band, untouched)
     meet jenny    .       the one cluster, cropped off the lower-right corner
     reviews       .
     questions     SHADOW  large and faint from the left edge, running on
     booking        (cont) into booking, with a small hibiscus tucked against
                           the left edge inside it

   Three shadows of different sizes and entry points at uneven intervals;
   four flowers (three singles, one cluster), every one of them against a
   viewport edge and cropped by it, none floating in open space.

   The shadow files carry their fronds in one corner each (left file: the
   left edge and top-left; right file: the top band and right edge), so the
   offsets below put THAT part on the page and crop the rest off the edge.
   Where a formula appears it keeps a chosen row of the file on a section
   boundary as the page scales: the file is 0.75 as tall as it is wide.

   --spill lets a section's decor run past its own bottom edge. It works only
   where the host does not clip (so .hm-aud drops the .hm-art clip) and the
   sections it runs into are positioned and later in the page, which then
   paint their own photographs, grounds and content over it. Negative spill
   cuts a layer short. */

.hm-hero {
  --palm-w: 100vw;
  --palm-edge: -8vw;
  --palm-y: -60px;
  --palm-o: 0.36;
}

/* The cross-section shadow. Row 2.5 (of 9) of the file sits on the
   Who we work with / hygienist boundary, so its dense top-right band crosses
   it in the right margin and its right-edge fronds run on down. The spill
   (560px) is longer than the hygienist band ever is (390-470px) and shorter
   than that band plus the dark band under it (at least 758px), so the
   shadow always ends out of sight beneath the dark band, never on a line. */
.hm-aud {
  overflow: visible;
  --palm-w: 118vw;
  --palm-edge: -8vw;
  --palm-from: bottom;
  --spill: 560px;
  --palm-y: calc(var(--spill) - 63.9vw);
  --palm-o: 0.3;
  --fade-top: 90px;
  --fade-bottom: 40px;
}

/* Row 4 of the file level with the top of the questions, so only its
   left-edge fronds are on the page. The spill (340px, fading over its last
   200px) always ends inside booking, which is at least 372px tall, so it
   never reaches the footer. */
.hm-faq {
  position: relative;
  isolation: isolate;
  --palm-w: 120vw;
  --palm-edge: -5vw;
  --palm-y: calc(60px - 40vw);
  --palm-o: 0.3;
  --fade-top: 150px;
  --spill: 340px;
  --fade-bottom: 200px;
}

/* Two ways: 40% cropped by the viewport; the 60% on screen clears the 940px
   card pair by at least 24px. */
.hm-two {
  --flower-w: min(200px, calc(((100% - 940px) / 2 - 24px) / 0.6));
  --flower-left: calc(var(--flower-w) * -0.4);
  --flower-bottom: 2%;
  --flower-o: 0.66;
}

/* What we can do for you: palm light on both sides of the coverflow, one
   decor layer per side, in the margins the side cards leave. Skylar,
   2026-09-30: the band read as bare tan without it. */
.hm-svc {
  --palm-w: 62vw;
  --palm-edge: -12vw;
  --palm-y: 30px;
  --palm-o: 0.3;
  --fade-top: 110px;
  --fade-bottom: 110px;
}

.hm-svc .aloha-palm-right {
  --palm-w: 58vw;
  --palm-edge: -10vw;
  --palm-from: bottom;
  --palm-y: -80px;
}

/* Who we work with: small, tucked into the upper-right corner at the turn
   from services, a third of it past the edge. */
.hm-aud {
  --flower-w: 112px;
  --flower-right: -37px;
  --flower-top: 3%;
  --flower-o: 0.72;
}

.hm-jenny {
  --flower-w: min(230px, 16vw);
  --flower-right: calc(var(--flower-w) * -0.3);
  --flower-bottom: 3%;
  --flower-o: 0.62;
}

/* Booking: small, against the left edge and partly past it, inside the
   questions shadow. */
.hm-book {
  --flower-w: 88px;
  --flower-left: -26px;
  --flower-top: 16%;
  --flower-o: 0.7;
}

/* Each flower goes when the margin beside it can no longer hold it clear of
   the cards. */
@media (max-width: 1240px) {
  .hm-aud .aloha-decor::after {
    display: none;
  }
}

@media (max-width: 1180px) {
  .hm-book .aloha-decor::after {
    display: none;
  }
}

@media (max-width: 1160px) {
  .hm-two .aloha-decor::after {
    display: none;
  }
}

/* Home & Community Care (2026-09-30). The homepage's rules, fewer moments:

     hero          .       its composition carries its own palm light
                           (2026-10-07), so the page adds none
     surroundings  .       (band) single hibiscus tucked into the lower-left
                           corner, cropped by the viewport
     visit         SHADOW  from the right edge, beside the timeline
     communities   .       (band)
     request       .       the cluster, cropped off the lower-right corner

   Shadows fade where they meet the next light section. Flowers go on
   narrower screens, where the margins beside the content close up. */
.cc-why {
  --flower-w: min(170px, calc(((100% - 980px) / 2 - 20px) / 0.65));
  --flower-left: calc(var(--flower-w) * -0.35);
  --flower-bottom: 5%;
  --flower-o: 0.62;
}

.cc-visit {
  --palm-w: 78vw;
  --palm-edge: -14vw;
  --palm-y: -30px;
  --palm-o: 0.28;
  --fade-top: 120px;
  --fade-bottom: 160px;
}

.cc-enquiry {
  --flower-w: min(260px, 20vw);
  --flower-right: calc(var(--flower-w) * -0.28);
  --flower-bottom: -6%;
  --flower-o: 0.58;
}

@media (max-width: 1180px) {
  .cc-why .aloha-decor::after,
  .cc-enquiry .aloha-decor::after {
    display: none;
  }
}

@media (max-width: 860px) {
  .cc-visit {
    --palm-w: 170vw;
    --palm-edge: -60vw;
    --palm-o: 0.2;
  }
}

/* Stacked layouts. The questions' side margins are gone, so that shadow
   goes, and so does Meet Jenny's cluster. Who we work with now ends on a
   row with an empty cell at the right, directly above the hygienist
   photograph band, whose right side is open paper: the shadow falls
   through both and fades out at the foot of the band (42.86vw tall),
   before the hygienist heading. Three across here: the boundary sits 1.5
   rows down the file. */
@media (max-width: 1080px) {
  .hm-faq .aloha-decor::before,
  .hm-jenny .aloha-decor::after {
    display: none;
  }

  .hm-aud {
    --palm-w: 88vw;
    --palm-edge: 0px;
    --palm-y: calc(20px - 12.14vw);
    --palm-o: 0.24;
    --spill: calc(42.86vw + 20px);
    --fade-bottom: 150px;
  }
}

/* Phones: that one shadow only, fainter, and no flowers. Two across: the
   boundary sits 2 rows down the file. */
@media (max-width: 860px) {
  .hm-hero .aloha-decor::before,
  .hm-svc .aloha-decor::before {
    display: none;
  }

  .hm-aud {
    --palm-w: 180vw;
    --palm-edge: -7.5vw;
    --palm-y: calc(16px - 62.14vw);
    --palm-o: 0.2;
    --spill: calc(42.86vw + 16px);
    --fade-bottom: 110px;
  }
}

/* --- Hero ------------------------------------------------------------------
   Pinned to the top so Jennifer's hair (from y 9) is never cut; the bottom
   gives way instead. Pinned RIGHT because this frame is 3:1: wherever the
   section is taller in ratio than that (about 860-1400px) the sides crop, and
   the script at x 84-98 is the thing that must survive. On phones the band is
   5:3, cropped the same way, so Jennifer is a subject rather than a
   thumbnail.

   Since 2026-09-28 the frame is home-hero-photo: the same file with its
   painted branch and flat cream matted to transparent (x 0-26%), fading in
   over 26-42%, so the paper canvas shows through on the left. Its palm
   light is placed in the light plan near the top of this file. */

.hm-hero {
  display: flex;
  align-items: center;
  min-height: clamp(400px, 32vw, 560px);
  background-color: transparent;
  background-position: right top;
  --art: image-set(url('assets/home-hero-photo-1400.webp') 1x, url('assets/home-hero-photo-1916.webp') 2x);
  --art-sm: image-set(url('assets/home-hero-photo-900.webp') 1x, url('assets/home-hero-photo-1400.webp') 2x);
}

/* A cream lift under the copy column only. The field is flat cream through
   most of it, but the watercolour branch at the left edge reaches past 9% at
   the height the sub-headline sets on, and check_hero_contrast.py had that
   line at 2.71:1 against a leaf at #9CA091. Cream over cream is invisible, so
   the only thing this touches is the branch where it crosses the copy.

   It starts transparent and only comes up at 9%, which is where the copy
   starts: the outer part of the branch is artwork the reference keeps, and
   washing the whole left edge would have flattened it. It is gone again by
   49%, well before anything of Jennifer at 42-82%.

   The branch is matted out now, but the photograph's soft left edge still
   reaches under the ends of the longer lines, so the lift stays. It is
   tinted to the paper's own mean so it reads as paper, not a lighter band. */
.hm-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgb(248 238 223 / 0.45) 7%,
    rgb(248 238 223 / 0.86) 12%,
    rgb(248 238 223 / 0.86) 46%,
    rgb(248 238 223 / 0.45) 51%,
    transparent 55%
  );
}

/* The open field runs from 9% to 40%, so the copy starts past the branch and
   stops before Jennifer. 31% of the viewport is the whole budget. */
.hm-hero__inner {
  position: relative;
  z-index: 1;
  margin-inline-start: max(var(--gutter), 13%);
  margin-inline-end: 0;
  padding-block: clamp(26px, 3vw, 44px);
  width: min(33%, 46ch);
}

.hm-hero h1 {
  margin: 12px 0 0;
  font-size: clamp(1.95rem, 3.4vw, 3.15rem);
  line-height: 1.06;
}

.hm-hero__lead {
  margin: 18px 0 0;
  max-width: none;
  color: var(--ink-soft);
  font-size: clamp(1rem, 1.15vw, 1.1rem);
}

.hm-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 24px;
}

/* --- Benefit strip ---------------------------------------------------------
   A plain cream rule between the hero and the first band. No artwork: the
   reference keeps it flat, and it is the one place on the page that reads as
   a list of facts rather than a composition. */

/* The strip is the one deep band between the hero and the mint "Two ways"
   section. A light tint read as part of whichever neighbour it touched, so it
   takes the deep ground instead, with hairline dividers between the four
   points on desktop and small tiles once they wrap. */
.hm-strip {
  background: var(--deep);
  color: var(--on-deep);
}

.hm-strip ul {
  display: grid;
  grid-template-columns: repeat(4, auto);
  justify-content: space-evenly;
  margin: 0;
  padding: 18px 0;
  list-style: none;
}

.hm-strip li {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 4px clamp(10px, 1.6vw, 24px);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--on-deep);
  text-align: left;
}

.hm-strip li + li {
  border-left: 1px solid var(--on-deep-line);
}

.hm-strip .icon {
  width: 34px;
  height: 34px;
  padding: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--deep-2);
  color: var(--on-deep);
}

/* --- Shared band + head ----------------------------------------------------- */

.hm-band {
  padding-block: clamp(28px, 3.1vw, 44px);
}

.hm-head {
  max-width: 62ch;
  margin-inline: auto;
  margin-bottom: clamp(16px, 1.9vw, 26px);
  text-align: center;
}

.hm-head h2 {
  margin: 0;
  font-size: clamp(1.55rem, 2.15vw, 2.05rem);
  line-height: 1.12;
}

.hm-head p {
  margin: 9px auto 0;
  max-width: 52ch;
  color: var(--ink-soft);
  font-size: clamp(0.92rem, 1vw, 1rem);
}

/* --- Two ways to be seen ----------------------------------------------------
   The two cards are held to 940px and centred, which leaves open margins on
   both sides at 1440. Since 2026-09-28 the section is transparent over the
   paper canvas (the painted-leaf frame is no longer used); its hibiscus is
   placed in the light plan. */

.hm-two {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.hm-two__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 26px);
  max-width: 940px;
  margin-inline: auto;
}

.hm-visit {
  display: flex;
  flex-direction: column;
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.04);

  /* The photograph's crop, as two numbers (they once also gave the
     retired Coming soon ribbon its diagonal). */
  --crop-w: 21;
  --crop-h: 9;
}

/* The frame for the visit photograph. It used to carry the studio card's
   "Coming soon" ribbon, retired 2026-10-07 when the studio opened; the
   clip still keeps the photograph inside the card's rounded corners. */
.hm-visit__media {
  position: relative;
  overflow: hidden;
}

.hm-visit img {
  display: block;
  width: 100%;
  aspect-ratio: var(--crop-w) / var(--crop-h);
  object-fit: cover;
}

.hm-visit__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: clamp(14px, 1.45vw, 19px);
}

.hm-visit h3 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.32rem;
  font-weight: 600;
}

.hm-visit__body > p {
  margin: 7px 0 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.hm-visit ul {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}

.hm-visit li {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 10px;
  align-items: start;
  font-size: 0.85rem;
  line-height: 1.35;
  color: var(--ink-soft);
}

.hm-visit li .icon {
  width: 18px;
  height: 18px;
  margin-top: 1px;
  color: var(--deep);
}

/* Pushed to the bottom so both cards' buttons line up even when one has a
   longer description. */
.hm-visit .btn {
  width: 100%;
  margin-top: auto;
  padding-top: 13px;
  padding-bottom: 13px;
  min-height: 46px;
}

.hm-visit__cta {
  margin-top: 14px;
}

/* --- What we can do for you -------------------------------------------------
   Transparent over the paper canvas since 2026-09-28 (the painted-leaf frame
   is no longer used). Deliberately no palm light: the coverflow's side cards
   are translucent, so a shadow behind them reads as a stain on the cards. */

.hm-svc {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.hm-svc__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 22px);
  max-width: 1120px;
  margin-inline: auto;
}

.hm-card {
  display: flex;
  flex-direction: column;
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 13px;
  padding: clamp(16px, 1.7vw, 22px);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.04);
}

.hm-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  background: #E2EDE2;
  margin-bottom: 14px;
}

.hm-card__icon .icon {
  width: 20px;
  height: 20px;
  color: var(--deep);
}

.hm-card h3 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.22rem;
  font-weight: 600;
  line-height: 1.2;
}

.hm-card p {
  margin: 8px 0 0;
  font-size: 0.87rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

.hm-card .text-link {
  margin-top: 14px;
  align-self: start;
  font-size: 0.88rem;
}

/* --- Who we care for -------------------------------------------------------
   Compact cards: photo, title, then one line saying who that is. Six of them,
   three across at every width above a phone, so they sit as two rows that
   group like with like. */

/* Transparent over the paper canvas since 2026-09-28. Hosts the shadow
   that runs on into the hygienist band, and a small hibiscus in its
   upper-right corner (see the light plan). */
.hm-aud {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.hm-aud__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(10px, 1.2vw, 18px);
  max-width: 1080px;
  margin-inline: auto;
}

.hm-aud-card {
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.04);
}

.hm-aud-card img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.hm-aud-card h3 {
  margin: 0;
  padding: 12px 14px 0;
  font-size: 0.92rem;
  font-weight: 650;
  line-height: 1.3;
  text-align: center;
  color: var(--ink);
}

/* Jennifer's own photograph is portrait; held high so the crop keeps her
   face and hands rather than the towel. */
.hm-aud-card--high img {
  object-position: 50% 22%;
}

.hm-aud-card p {
  margin: 0;
  padding: 4px 14px 14px;
  font-size: 0.84rem;
  line-height: 1.45;
  text-align: center;
  color: var(--ink-soft);
}

/* --- A hygienist, not a dental office ---------------------------------------
   The photograph is part of the frame, not a separate element, so there is no
   <img> here: the left 40% of the ground IS the picture. Biased up because
   the hygienist's head sits at y 2-25% and a centred crop opens at 21%.

   Since 2026-09-28 the frame is home-hygienist-photo: the photograph at
   full opacity, its cream ground and the leaves at x 84-97% matted to
   transparent, so it fades into the paper canvas. The palm light in its
   upper-right margin spills in from Who we work with (light plan). */

.hm-hyg {
  display: flex;
  align-items: center;
  min-height: clamp(320px, 27vw, 470px);
  background-color: transparent;
  background-position: center 15%;
  --art: image-set(url('assets/home-hygienist-photo-1400.webp') 1x, url('assets/home-hygienist-photo-1916.webp') 2x);
  --art-sm: image-set(url('assets/home-hygienist-photo-900.webp') 1x, url('assets/home-hygienist-photo-1400.webp') 2x);
}

/* The open field is x 42-78, so the copy starts at 44% and is 32% wide. */
.hm-hyg__copy {
  position: relative;
  z-index: 1;
  margin-inline-start: 44%;
  padding-block: clamp(24px, 3vw, 44px);
  width: min(33%, 48ch);
}

.hm-hyg__copy h2 {
  margin: 0;
  font-size: clamp(1.6rem, 2.5vw, 2.35rem);
  line-height: 1.12;
}

.hm-hyg__copy > p {
  margin: 14px 0 0;
  max-width: none;
  color: var(--ink-soft);
  font-size: clamp(0.9rem, 1vw, 0.99rem);
}

.hm-points {
  margin: 22px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 16px;
}

.hm-points li {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 14px;
  align-items: start;
}

.hm-points__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: #E2EDE2;
}

.hm-points__icon .icon {
  width: 19px;
  height: 19px;
  color: var(--deep);
}

.hm-points h3 {
  margin: 0 0 3px;
  font-size: 1rem;
  font-weight: 700;
  padding-top: 8px;
}

.hm-points p {
  margin: 0;
  font-size: 0.87rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* --- Oral care for care communities -----------------------------------------
   The deep band. Its two faces sit at y 5-35% and y 35-90%, so it needs about
   three quarters of the frame's height to keep both, which is why it is
   taller than the reference's 300px. Biased up for the same reason as the
   hygienist band. */

.hm-comm {
  display: flex;
  align-items: center;
  min-height: clamp(330px, 30vw, 520px);
  background-color: #083830;
  background-position: center 8%;
  --art: image-set(url('assets/home-community-1400.webp') 1x, url('assets/home-community-1916.webp') 2x);
  --art-sm: image-set(url('assets/home-community-900.webp') 1x, url('assets/home-community-1400.webp') 2x);
}

.hm-comm__copy {
  position: relative;
  z-index: 1;
  margin-inline-start: max(var(--gutter), 6%);
  padding-block: clamp(24px, 3vw, 44px);
  width: min(33%, 46ch);
}

.hm-comm__copy h2 {
  margin: 12px 0 0;
  font-size: clamp(1.6rem, 2.5vw, 2.35rem);
  line-height: 1.1;
}

.hm-comm__copy p {
  margin: 14px 0 0;
  max-width: none;
  font-size: clamp(0.9rem, 1vw, 0.99rem);
}

.hm-comm__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 22px;
}

/* --- Meet Jenny -------------------------------------------------------------
   Her portrait is inside the frame as a finished card at x 7-35%, y 15-83%,
   so there is no <img> here either and the copy sits in the open field beside
   it. The band has to keep about seven tenths of the frame's height or the
   portrait loses its head and its base.

   Since 2026-10-13 the section has no ground art at all: the handwritten
   signature and heart that sat at the right (home-jenny-script) were taken
   off at Skylar's request. The paper canvas shows through. Its hibiscus is
   placed in the light plan. */

.hm-jenny {
  padding-block: clamp(40px, 5vw, 76px);
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

/* Photograph and story side by side, the photograph framed in its own
   column with the section's padding above and below it, so it no longer
   runs into the dark care-communities band. */
.hm-jenny__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: clamp(28px, 4.4vw, 68px);
  align-items: center;
  max-width: 1120px;
}

.hm-jenny__figure {
  overflow: hidden;
  border-radius: 16px;
  box-shadow: var(--shadow-2);
}

/* The source is a 4:3 landscape; a 4:5 box takes the middle of it, and
   cover scales it to the box's height, which is why the srcset is chosen
   from a sizes value about 1.7 times the box width. */
.hm-jenny__figure img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center 20%;
}

.hm-jenny__copy h2 {
  margin: 0;
  font-size: clamp(1.6rem, 2.5vw, 2.35rem);
  line-height: 1.12;
}

.hm-jenny__copy p {
  margin: 14px 0 0;
  max-width: 60ch;
  color: var(--ink-soft);
  font-size: clamp(0.92rem, 1vw, 1rem);
}

/* Her sign-off under the letter (2026-10-14): name in bold, role beneath,
   as she set it in her email. */
.hm-jenny__copy .hm-jenny__sign {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 18px;
}

.hm-jenny__sign strong {
  font-weight: 700;
  color: var(--ink);
}

/* Meet Jenny stacked: photograph first, centred, then the words. */
@media (max-width: 860px) {
  .hm-jenny__grid {
    grid-template-columns: 1fr;
  }

  .hm-jenny__figure {
    width: 100%;
    max-width: 420px;
    justify-self: center;
  }
}

/* --- What patients say ------------------------------------------------------ */

/* Transparent over the paper canvas since 2026-09-28. Deliberately bare:
   the reviews are the focus here. */
.hm-revs {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.hm-revs__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 22px);
  max-width: 940px;
  margin-inline: auto;
}

.hm-rev {
  display: flex;
  flex-direction: column;
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 12px;
  padding: clamp(16px, 1.7vw, 22px);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.04);
}

.hm-rev__stars {
  display: flex;
  gap: 3px;
  margin-bottom: 12px;
}

.hm-rev__stars .icon {
  width: 15px;
  height: 15px;
  color: var(--deep);
}

.hm-rev blockquote {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.hm-rev blockquote p {
  margin: 0;
}

.hm-rev__meta {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-top: auto;
  padding-top: 18px;
}

.hm-rev__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 999px;
  background: var(--deep);
  color: var(--on-deep);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.hm-rev__who {
  display: flex;
  flex-direction: column;
  line-height: 1.3;
}

.hm-rev__who strong {
  font-size: 0.89rem;
}

.hm-rev__who span {
  font-size: 0.79rem;
  color: var(--ink-soft);
}

.hm-revs__note {
  margin: clamp(18px, 2vw, 26px) auto 0;
  max-width: 60ch;
  text-align: center;
  font-size: 0.84rem;
  color: var(--ink-soft);
}

/* --- Questions people ask ---------------------------------------------------
   No artwork on this one: it is the only section on the page that is pure
   text. Two columns of thin rules on the paper canvas since 2026-09-28.
   A large faint shadow enters its lower-left margin (see the light plan). */

.hm-faq {
  background: transparent;
}

.hm-faq__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 clamp(28px, 4vw, 64px);
  max-width: 1060px;
  margin-inline: auto;
}

.hm-faq details {
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
}

.hm-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 2px;
  cursor: pointer;
  list-style: none;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
}

.hm-faq summary::-webkit-details-marker {
  display: none;
}

.hm-faq summary .icon {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--ink-soft);
  transition: transform 160ms var(--ease);
}

.hm-faq details[open] summary .icon {
  transform: rotate(180deg);
}

.hm-faq details > div {
  padding: 0 2px 15px;
  font-size: 0.89rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.hm-faq details > div p {
  margin: 0 0 9px;
  max-width: 62ch;
}

.hm-faq details > div p:last-child {
  margin-bottom: 0;
}

/* --- Find us and book -------------------------------------------------------- */

/* Transparent over the paper canvas since 2026-09-28. The questions
   section's shadow runs on into this one; its hibiscus is placed in the
   light plan. */
.hm-book {
  background-color: transparent;
  --art: none;
  --art-sm: none;
}

.hm-book__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 22px);
  max-width: 1020px;
  margin-inline: auto;
}

.hm-book-card {
  display: flex;
  flex-direction: column;
  background: #FCFBF7;
  border: 1px solid rgb(10 70 60 / 0.08);
  border-radius: 13px;
  padding: clamp(16px, 1.7vw, 22px);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.04);
}

.hm-book-card__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.hm-book-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 999px;
  background: #E2EDE2;
}

.hm-book-card__icon .icon {
  width: 18px;
  height: 18px;
  color: var(--deep);
}

.hm-book-card h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
}

.hm-book-card p,
.hm-book-card address {
  margin: 0 0 5px;
  font-size: 0.86rem;
  line-height: 1.45;
  font-style: normal;
  color: var(--ink-soft);
}

.hm-book-card a:not(.btn) {
  color: var(--accent);
}

/* Hours and the reach-out note (2026-10-14): a little air between blocks,
   and the day lines in the ink colour so they read as the headline. */
.hm-book-card .hm-book-card__hours {
  margin-bottom: 10px;
}

.hm-book-card p strong {
  font-weight: 650;
  color: var(--ink);
}

.hm-book-card .btn {
  margin-top: 14px;
  align-self: start;
  min-height: 44px;
  padding-block: 12px;
}

/* --- Footer art --------------------------------------------------------------
   The shared footer keeps its markup and its tokens; this only swaps the flat
   deep ground for the supplied composition. The frame is already dark enough
   that the footer's own contrast floor holds, which check_page_contrast.py
   measures on the composite rather than against a flat swatch. */

.footer--art {
  background-image: var(--art);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  --art: image-set(url('assets/home-footer-1400.webp') 1x, url('assets/home-footer-1916.webp') 2x);
  --art-sm: image-set(url('assets/home-footer-900.webp') 1x, url('assets/home-footer-1400.webp') 2x);
}

/* --- Tablet ------------------------------------------------------------------ */

@media (max-width: 1080px) {
  .hm-svc__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .hm-book__grid {
    grid-template-columns: 1fr;
  }

  .hm-revs__grid {
    grid-template-columns: 1fr;
    max-width: 620px;
  }

  .hm-faq__grid {
    grid-template-columns: 1fr;
  }

  /* The three people bands stop being grounds behind copy and become a band
     above it, for the reason spelled out in the phone block below. */
  .hm-hyg,
  .hm-comm {
    display: block;
    min-height: 0;
    background-image: none;
  }

  .hm-hyg::before,
  .hm-comm::before {
    content: '';
    display: block;
    width: 100%;
    aspect-ratio: 1916 / 821;
    background: var(--art-sm) no-repeat center / cover;
  }

  .hm-hyg__copy,
  .hm-comm__copy {
    width: auto;
    margin-inline: auto;
    max-width: var(--wrap);
    padding-inline: var(--gutter);
    padding-block: clamp(26px, 4vw, 44px);
  }

  .hm-comm {
    background-color: #083830;
  }
}

/* --- Phone --------------------------------------------------------------------
   Below 860 every composition becomes a band rather than a ground. A 2.33:1
   frame held behind a section that is now three times taller than it is wide
   scales to the HEIGHT and crops the sides away, which is the one axis these
   files cannot lose. The hero follows the same rule as the other pages: copy
   on flat cream, then the whole composition at its own ratio underneath.
   ------------------------------------------------------------------------------ */

@media (max-width: 860px) {
  .hm-hero {
    display: block;
    min-height: 0;
    background-image: none;
  }

  .hm-hero::before {
    display: none;
  }

  .hm-hero__inner {
    width: auto;
    margin-inline: auto;
    max-width: var(--wrap);
    padding-inline: var(--gutter);
    padding-block: 26px 22px;
  }

  .hm-hero::after {
    content: '';
    display: block;
    width: 100%;
    aspect-ratio: 5 / 3;
    background: var(--art-sm) no-repeat right top / cover;
  }

  /* Every one of these frames keeps its botanicals in the outer eighth at
     both edges. Painted 100% wide there are no outer margins left, so the
     leaves come inboard and a centred section head runs straight into them:
     check_page_contrast.py had the "Two ways to be seen" sub-headline at
     2.93:1 at 560. Holding the head inside the middle 72% keeps every line on
     the flat field, and keeps the artwork rather than dropping it. */
  /* .section-head is in here as well as .hm-head because the restored
     coverflow section keeps the original head markup, and its sub-headline
     was the one line still reaching the leaves at 390 (1.82:1). Scoped to
     .hm-art so the other pages' heads are untouched. */
  .hm-head,
  .hm-art .section-head {
    max-width: min(62ch, 72vw);
  }

  .hm-strip ul {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    padding: 16px 0;
  }

  .hm-strip li,
  .hm-strip li + li {
    justify-content: flex-start;
    padding: 10px 12px;
    border: 1px solid var(--on-deep-line);
    border-radius: 12px;
  }

  .hm-two__grid {
    grid-template-columns: 1fr;
  }

  .hm-svc__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .hm-aud__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer--art {
    background-image: var(--art-sm);
  }
}

@media (max-width: 520px) {
  .hm-strip li,
  .hm-strip li + li {
    flex-direction: column;
    justify-content: flex-start;
    gap: 8px;
    padding: 12px 8px;
    font-size: 0.84rem;
    line-height: 1.3;
    text-align: center;
  }

  .hm-svc__grid {
    grid-template-columns: 1fr;
  }

  /* The audience cards stay two across even on the narrowest phone. One
     across turns six captioned photographs into six full-width blocks and
     about 1600px of scrolling for a section that is meant to be scanned. */
  .hm-aud__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .hm-aud-card h3 {
    padding: 10px 8px 0;
    font-size: 0.82rem;
  }

  .hm-aud-card p {
    padding: 3px 8px 10px;
    font-size: 0.78rem;
  }
}


/* --- Services light plan (2026-10-07) ----------------------------------------
   The Services & Fees page on the homepage's paper system, at Skylar's
   request, following a supplied brief: warm ivory paper, soft palm light and
   gold hibiscus in place of the painted eucalyptus and mint. The homepage's
   own layers are reused (the brief's two generated backgrounds are kept in
   assets/src/incoming/Aloha-Services-Backgrounds and not shipped, because
   the originals match the homepage exactly).

     hero          SHADOW  from the top-left, washed under the copy; the
                           photograph and its lettering are the matted frame
     preventive    .       a small hibiscus cropped off the lower-right corner
     periodontal   SHADOW  (band) from the right edge
     additional    .       bare paper, so the four cards carry it
     dentist eval  SHADOW  (band) from the left edge, the handwriting right
     on-site       .       a hibiscus cropped off the lower-left corner; the
                           valley panel is the matted frame
     payment       .       (band)
     (not sure what to book? and the footer: dark, untouched)

   Paper and sand alternate from Preventive down. Shadows fade where they meet
   another light section. Flowers go where the side margins close up, and the
   phone keeps only the paper and the bands. */
.shero {
  --palm-w: 96vw;
  --palm-edge: -10vw;
  --palm-y: -80px;
  --palm-o: 0.34;
  --fade-bottom: 120px;
}

.art--xrays {
  --flower-w: min(160px, 12vw);
  --flower-right: calc(var(--flower-w) * -0.32);
  --flower-bottom: calc(var(--flower-w) * -0.18);
  --flower-o: 0.62;
}

.art--prevention {
  --palm-w: 72vw;
  --palm-edge: -14vw;
  --palm-y: -30px;
  --palm-o: 0.26;
  --fade-top: 90px;
  --fade-bottom: 110px;
}

.art--whitening {
  --palm-w: 74vw;
  --palm-edge: -16vw;
  --palm-y: -40px;
  --palm-o: 0.24;
  --fade-top: 90px;
  --fade-bottom: 110px;
}

.art--coming {
  /* Sized off the side margin, so the part left on screen (60%) always ends
     short of the copy, which starts at the wrap edge. Below 1420 the valley
     panel turns into a band at the foot of the section with open paper on
     its left, and the flower moves into that corner instead (see below). */
  --flower-w: min(200px, calc(max(var(--gutter), (100% - var(--wrap)) / 2) * 1.5));
  --flower-left: calc(var(--flower-w) * -0.4);
  --flower-bottom: calc(var(--flower-w) * -0.12);
  --flower-o: 0.6;
}

/* The photograph's lower edge fades into the paper instead of stopping on a
   line where Preventive begins. Drawn by the hero's decor layer, which sits
   above the section's background (the matted frame) and below the copy, so
   it touches only the picture. Its flower slot is unused here. */
.shero .aloha-decor::after {
  content: '';
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(60px, 7vw, 110px);
  background: linear-gradient(to bottom, rgb(248 238 223 / 0), #F8EEDF 92%);
}

/* Preventive's pull-quote, set as text in the column beside the cards where
   the painted one stood. Upright: the display face is self-hosted upright
   only, and a synthesised italic would be worse than none. The opening mark
   hangs, and the short rule under it is the painted one's. */
.art--xrays .wrap {
  display: grid;
  grid-template-columns: minmax(0, 54fr) minmax(0, 46fr);
  align-items: center;
  column-gap: clamp(24px, 4vw, 64px);
}

.art--xrays .art-col--narrow {
  width: 100%;
}

.svc-quote {
  margin: 0;
  padding-inline-start: clamp(0px, 3vw, 48px);
}

.svc-quote blockquote {
  margin: 0;
}

.svc-quote p {
  max-width: 13ch;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.7rem, 2.4vw, 2.35rem);
  font-weight: 500;
  line-height: 1.18;
  color: var(--ink-soft);
  text-indent: -0.4em;
}

.svc-quote p::after {
  content: '';
  display: block;
  width: 44px;
  height: 1px;
  margin-top: 22px;
  background: color-mix(in srgb, var(--ink-soft) 55%, transparent);
}

/* "Small care. Big confidence.", cut from the old mint frame, in the space
   .art-col--wide leaves at the right of Dentist evaluation. Painted at the
   size it had in that frame at 1440 (about 184px); the cut is 278px wide.
   Decoration, so it is aria-hidden and catches no pointer. */
.svc-script {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}

.svc-script--small-care {
  top: 50%;
  right: calc(max(var(--gutter), (100% - var(--wrap)) / 2) + 24px);
  width: clamp(150px, 12.8vw, 200px);
  aspect-ratio: 278 / 222;
  background-image: url('assets/svc-bg-script-small-care.webp');
  transform: translateY(-50%);
}

/* Band mode: the copy is all above the band, so the flower can be a fixed
   size in the band's empty left corner without reaching any text. On a phone
   the band is cropped to the panel and there is no corner left. */
@media (max-width: 1420px) {
  .art--coming {
    --flower-w: min(230px, 24vw);
    --flower-left: calc(var(--flower-w) * -0.22);
    --flower-bottom: calc(var(--flower-w) * -0.1);
  }
}

@media (max-width: 760px) {
  .art--coming .aloha-decor::after {
    display: none;
  }
}

@media (max-width: 1180px) {
  .art--xrays .aloha-decor::after {
    display: none;
  }
}

@media (max-width: 1120px) {
  .svc-script {
    display: none;
  }

  /* Stacked, the picture is the band under the copy and fades at its own
     top and bottom, so the bottom wash has nothing to do. */
  .shero .aloha-decor::after {
    display: none;
  }

  .shero::after {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 12%, #000 90%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 12%, #000 90%, transparent 100%);
  }

  .art--xrays .wrap {
    grid-template-columns: minmax(0, 1fr);
  }

  .svc-quote {
    margin-top: clamp(26px, 5vw, 40px);
    padding-inline-start: 0;
  }

  .svc-quote p {
    max-width: 22ch;
    font-size: clamp(1.45rem, 4.6vw, 1.9rem);
  }
}

@media (max-width: 760px) {
  .shero .aloha-decor::before {
    display: none;
  }

  .art--prevention,
  .art--whitening {
    --palm-w: 170vw;
    --palm-edge: -70vw;
    --palm-o: 0.16;
  }
}
