/* ============================================================
   Components — nav, lockup, buttons, cards, chips, form, footer

   Built out in Tasks 2-5. Task 1 establishes only the shared
   primitives that later tasks compose.
   ============================================================ */

/* ---- Skip link ---- */
.skip-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  position: absolute;
  left: var(--space-m);
  top: var(--space-m);
  z-index: 100;
  padding: 0.6em 1.2em;
  background: var(--ink);
  color: var(--cream);
  border-radius: var(--radius-pill);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-160%);
  transition: transform var(--dur-fast) var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ---- Lockup ----
   One drawing at any size. Every dimension is in em of the lockup's own
   font-size, which is the nest width / 10, so the proportions of
   round2/nest-final-sage.html (the master: nest 229 / gap 48 / The 35 /
   Rested Mama 88 / Village 84) hold from the footer to the hero. The hero
   lockup and the nav lockup are the same drawing at two font-sizes, which is
   what lets one glide into the other. ---- */
.lockup {
  --lockup-size: var(--lockup-nav);
  display: flex;
  align-items: center;
  gap: 2.1em;
  width: fit-content;
  font-size: var(--lockup-size);
  text-decoration: none;
  flex: none;
}
.lockup img { width: 10em; height: auto; }
.lockup .tx { display: flex; flex-direction: column; align-items: center; }
.lockup .the {
  font-family: var(--font-display);
  font-size: 1.53em;
  line-height: 1;
  letter-spacing: var(--tracking-the);
  text-indent: var(--tracking-the);   /* recentre: tracking adds space after the last letter */
  margin-bottom: 0.29em;              /* 10 / 35 */
  color: var(--ink);
  -webkit-text-stroke: 0.03em currentColor;   /* same weighting as the exported files */
  paint-order: stroke fill;
}
.lockup .rm {
  font-family: var(--font-display);
  font-size: 3.84em;
  line-height: 1;
  margin-bottom: 0.23em;              /* 20 / 88 */
  color: var(--ink);
  white-space: nowrap;
  -webkit-text-stroke: 0.01em currentColor;
  paint-order: stroke fill;
}
.lockup .vr {
  display: flex;
  align-items: center;
  gap: 0.7em;
  width: 100%;
}
.lockup .vr .ln { height: 0.07em; min-height: 1px; background: var(--meadow); flex: 1; }
.lockup .gv {
  font-family: var(--font-script);
  font-size: 3.67em;
  line-height: 1;
  color: var(--meadow);
  -webkit-text-stroke: 0.01em currentColor;
  paint-order: stroke fill;
}

/* The stacked lockup: the cradle from round2/nest-final-sage.html without its
   ring (the client dropped the ring for the stacked version). Text above, the
   nest beneath it. The text block is the same drawing as the horizontal one,
   so only the nest changes size and place between the two, and the flight
   below can carry the two pieces separately. Badge master: nest 332 under a
   450-wide text block, 44 below it at Rested Mama 78. */
.lockup--stacked { flex-direction: column; gap: 2.2em; }
.lockup--stacked img { width: 16em; order: 2; }

/* ---- Nav ---- */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) var(--ease-out);
}
.site-nav__inner {
  position: relative;               /* the flying lockup is placed against this box */
  display: flex;
  align-items: center;
  gap: var(--space-l);
  min-height: var(--nav-h);
  /* A floor on the breathing room around the lockup, so the condensed bar can
     never crop to the mark's own height. */
  padding-block: var(--space-s);
  transition: min-height var(--dur-mid) var(--ease-out);
}

.site-nav.is-condensed {
  background: color-mix(in srgb, var(--cream) 90%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--rule);
}
.site-nav.is-condensed .site-nav__inner { min-height: var(--nav-h-condensed); }

/* ---- The lockup's flight ----
   The mark has two homes: the hero, where it is the headline (stacked), and
   the bar, where it lives once the page is scrolled (horizontal). Three
   copies make that work:

   .hero__lockup   the hero headline (h1), stacked. It is the logo on screen
                   whenever the mark is at rest in the hero, and goes
                   transparent (.is-ghost) only while the mark is in flight
                   or docked; without JS it simply is the logo.
   .lockup--slot   an invisible horizontal copy in the bar at bar size. It
                   reserves the nav home and is what the flight measures
                   against.
   .lockup--nav    the real link. Without JS it is an ordinary nav lockup.
                   With JS its two pieces, the nest and the text block, are
                   absolutely positioned at hero size and main.js carries
                   each between its two homes with one transform, so nothing
                   here is ever laid out per frame. Stacked becomes
                   horizontal because the pieces travel on their own paths.
                   It is shown (.is-flying on the bar) only in flight and
                   docked: inside the bar it would otherwise draw over the
                   bar while the page scrolls under it.

   The pieces are drawn at hero size and scaled DOWN into the bar, never up:
   a layer scaled up is rasterised at its layout size and goes soft. ---- */
.lockup--slot { display: none; }

.js .site-nav .lockup--slot {
  display: flex;
  visibility: hidden;
  --lockup-size: var(--lockup-nav-condensed);
}
.js .lockup--nav {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  --lockup-size: var(--lockup-hero);
  /* At rest in the hero the headline shows instead. Hidden with visibility as
     well as opacity, so the link also leaves the tab order: an invisible link
     that still takes focus is a dead stop for keyboard users. Visibility flips
     on immediately when the flight starts and off only after the fade. */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease-out),
              visibility 0s linear var(--dur-fast);
}
.js .lockup--nav > img,
.js .lockup--nav > .tx {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  will-change: transform;
}
.js .lockup--nav > img { width: 16em; max-width: none; }   /* the stacked nest at hero size; its containing block (the link) is empty, so no % cap */
.js .site-nav.is-flying .lockup--nav { opacity: 1; visibility: visible; transition-delay: 0s; }

/* The link's own box is empty, so its focus ring goes on the pieces. */
.js .lockup--nav:focus-visible { outline: none; }
.js .lockup--nav:focus-visible > img,
.js .lockup--nav:focus-visible > .tx {
  outline: 2px solid var(--accent);
  outline-offset: 0.4em;
  border-radius: var(--radius-s);
}

.site-nav__links { margin-left: auto; }

.menu, .menu__sub { list-style: none; margin: 0; padding: 0; }
.menu { display: flex; align-items: center; gap: var(--space-l); }

.menu > li > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                 /* touch target floor */
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-decoration: none;
  position: relative;
}
/* Hairline that grows from the left — the same rule language as the lockup's
   meadow flanks, not a generic underline sweep. Anchored to the text rather than
   to the padded hit area, so the touch target does not push the rule away. */
.menu > li > a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(50% - 0.8em);
  height: 1.2px;
  background: var(--meadow);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-mid) var(--ease-out);
}
.menu > li > a:hover::after,
.menu__item--sub:focus-within > a::after { transform: scaleX(1); }

/* ---- Services dropdown ----
   Hover and focus-within, no JS: keyboard users open it by tabbing in. */
.menu__item--sub { position: relative; }
.menu__item--sub > a { padding-right: 1.1em; }
.menu__item--sub > a::before {          /* small chevron */
  content: "";
  position: absolute;
  right: 0;
  top: calc(50% - 0.35em);
  width: 0.4em;
  height: 0.4em;
  border-right: 1.2px solid currentColor;
  border-bottom: 1.2px solid currentColor;
  transform: rotate(45deg);
  opacity: 0.7;
}
.menu__sub {
  position: absolute;
  top: 100%;
  left: calc(var(--space-m) * -1);
  min-width: 15rem;
  padding: var(--space-xs) 0;
  background: var(--cream);
  border: 1px solid var(--rule);
  border-radius: var(--radius-m);
  box-shadow: 0 12px 32px -18px color-mix(in srgb, var(--deep-green) 45%, transparent);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              visibility 0s linear var(--dur-fast);
}
.menu__item--sub:hover > .menu__sub,
.menu__item--sub:focus-within > .menu__sub {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
.menu__sub a {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  min-height: 44px;
  padding: 0 var(--space-m);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
}
.menu__sub a::before {
  content: "";
  flex: none;
  width: 10px;
  height: 13px;
  background-color: var(--meadow);
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
}
.menu__sub a:hover { background: var(--sage); }

/* ---- Toggle (small screens) ---- */
.nav-toggle {
  display: none;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  position: relative;
}
.nav-toggle__bar {
  position: absolute;
  left: 11px;
  width: 22px;
  height: 1.5px;
  background: var(--ink);
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.nav-toggle__bar:nth-child(1) { top: 15px; }
.nav-toggle__bar:nth-child(2) { top: 21px; }
.nav-toggle__bar:nth-child(3) { top: 27px; }
.site-nav.is-open .nav-toggle__bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.site-nav.is-open .nav-toggle__bar:nth-child(2) { opacity: 0; }
.site-nav.is-open .nav-toggle__bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* Below the links breakpoint the menu becomes a panel under the bar. The bar
   itself goes opaque while open so the panel never floats over hero copy. */
@media (max-width: 860px) {
  .site-nav__links { display: none; }
  .site-nav__cta { margin-left: auto; }
  .nav-toggle { display: block; }

  .site-nav.is-open {
    background: var(--cream);
    border-bottom-color: var(--rule);
  }
  .site-nav.is-open .site-nav__links {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    margin: 0;
    padding: var(--space-s) var(--gutter) var(--space-l);
    background: var(--cream);
    border-bottom: 1px solid var(--rule);
  }
  .menu { flex-direction: column; align-items: stretch; gap: 0; }
  .menu > li > a { display: flex; font-size: var(--step-0); }
  .menu__item--sub > a { padding-right: 0; }
  .menu__item--sub > a::before { display: none; }
  .menu__sub {
    position: static;
    min-width: 0;
    padding: 0 0 var(--space-s) var(--space-m);
    background: transparent;
    border: 0;
    box-shadow: none;
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .menu__sub a { padding-inline: 0; }
  .menu__sub a:hover { background: transparent; }
}

/* At 320 the full-size lockup plus the CTA is wider than the viewport, which
   would scroll the whole page sideways. Shrink the mark rather than drop it. */
@media (max-width: 420px) {
  .site-nav__inner { gap: var(--space-s); }
  .site-nav .lockup { --lockup-size: 4.4px; }
  .js .site-nav .lockup--nav { --lockup-size: var(--lockup-hero); }
  .site-nav__cta { padding: 0.5em 1em; }
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: 44px;                    /* touch target floor */
  padding: 0.7em 1.6em;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--btn-bg);
  color: var(--btn-ink);
  border-color: var(--btn-bg);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.01em;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:hover {
  background: var(--btn-hover-bg);
  color: var(--btn-hover-ink);
  border-color: var(--btn-hover-bg);
}
.btn:active { transform: translateY(1px); }

/* The primary action (the Send button) is the same deep-green pill as the nav
   "Contact", one size larger, carrying the meadow leaf the whole page already uses
   as its bullet. Attention comes from size and isolation, not from a second hue:
   Meadow cannot carry interactive elements (spec §5) and the tertiary, which used
   to fill this button, was the only warm object on the page. */
.btn--primary {
  font-size: var(--step-0);
  padding: 0.85em 2em;
  gap: 0.8em;                  /* the leaf needs more air than the base pill's 8px */
}
.btn__leaf {
  flex: none;
  width: 11px;
  height: 14px;
  background-color: var(--light-green);
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.btn:hover .btn__leaf { background-color: var(--deep-green); }

/* Kept for secondary actions in Tasks 3 and 5. */
.btn--outline {
  background: transparent;
  color: var(--accent);
  border-color: var(--accent);
}
.btn--outline:hover { background: var(--accent); color: var(--cream); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: currentColor;
}
.btn--ghost:hover { background: var(--ink); color: var(--bg); }

.btn--compact { padding: 0.5em 1.25em; }

/* ============================================================
   Hero
   ============================================================ */

/* The hero's tail is shorter than a section's: the services strip below it
   belongs to the hero as much as to the services, so it sits closer. */
#hero { padding-block: clamp(2.5rem, 1.5rem + 4vw, 5rem) clamp(3rem, 2rem + 4vw, 6rem); }

.hero__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2xl);
  align-items: center;
}

@media (min-width: 900px) {
  .hero__inner {
    grid-template-columns: 1.12fr 0.88fr;
    gap: var(--space-2xl);
  }
}

/* A stacked mark is a centred composition, so the column centres with it. */
.hero__text { text-align: center; }

/* The headline is the lockup itself, stacked, at hero size. It is a real h1
   with real text ("The Rested Mama Village"), so nothing is lost to a picture. */
.hero__lockup {
  --lockup-size: var(--lockup-hero);
  margin-inline: auto;
  font-weight: 400;
}
/* While the mark is in flight or docked, the headline only holds the space;
   the nav's flying copy starts exactly over it. Opacity, not visibility, so
   the h1 stays in the accessibility tree. */
.hero__lockup { transition: opacity var(--dur-fast) var(--ease-out); }
.hero__lockup.is-ghost { opacity: 0; }

/* ---- The plate ----
   Everything that used to sit around the mark now sits beneath it, in two
   lines with two jobs. The first says what she is and where, in the page's
   label voice. The second is the credentials, as a row of three leaf-marked
   items, the same device the services strip uses. A short meadow rule ties the
   plate to the lockup's own "── Village ──" language. ---- */
.hero__plate {
  container-type: inline-size;
  display: grid;
  justify-items: center;
  gap: var(--space-m);
  margin-top: var(--space-xl);
}
.hero__plate::before {
  content: "";
  width: 3rem;
  height: 1.2px;
  background: var(--meadow);
}
.hero__role {
  margin: 0;
  color: var(--ink);
  max-width: none;
}
/* Where she works, on its own line under the role (Vânia, 2026-09-09: eastern
   Pennsylvania and New Jersey, no longer Bucks County alone), set exactly like the
   role and tucked close under it; a second short meadow rule, the twin of the one
   above the role, separates the pair from the credentials (Daniel, 2026-09-09). */
.hero__area {
  margin: calc(-1 * var(--space-s)) 0 0;
  color: var(--ink);
  max-width: none;
  text-align: center;
}
.hero__area::after {
  content: "";
  display: block;
  width: 3rem;
  height: 1.2px;
  background: var(--meadow);
  margin: var(--space-m) auto 0;
}
.hero__creds {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-xs) var(--space-l);
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  line-height: var(--leading-snug);
  color: var(--ink-muted);
}
.hero__creds li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  white-space: nowrap;
}
.hero__creds li::before {
  content: "";
  flex: none;
  width: 10px;
  height: 13px;
  background-color: var(--meadow);
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
}
/* One row or one column, never two-and-one: when the three do not fit on a
   line, they stack. */
@container (max-width: 560px) {
  .hero__creds { flex-direction: column; align-items: center; gap: var(--space-xs); }
}

/* ---- The arch, with the badge's double-ring echoed as one meadow hairline ---- */
.hero__frame {
  position: relative;
  max-width: 460px;
  margin-inline: auto;
}
.hero__frame::before {
  content: "";
  position: absolute;
  inset: -14px;
  border: 1px solid var(--meadow);
  border-radius: var(--arch-outer);
  opacity: 0.5;
  pointer-events: none;
}
.hero__photo {
  border-radius: var(--arch);
  overflow: hidden;
  background: var(--oat);          /* edge definition against cream */
  aspect-ratio: 4 / 5;
}
.hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}

/* ============================================================
   The night timeline — now the feature of the Overnight service band
   ============================================================ */

.night-steps { list-style: none; padding: 0; margin: 0; }
.night-steps h4 {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  color: var(--accent);
  margin-bottom: var(--space-xs);
}
.night-steps p { color: var(--ink); max-width: 54ch; }

/* ---- The vine ----
   Evening -> through the night -> morning is a real sequence over a real span of
   hours, so a timeline describes the content rather than decorating it. The stem
   is drawn as a swaying vine and each beat is marked with one of the nest's own
   three leaf shapes, so the device belongs to the mark instead of being generic.

   Assets are opaque silhouettes used as masks, so the colour comes from --accent
   and inverts with the section rather than being baked into the file. */
.night--vine .night-steps { position: relative; padding-left: var(--vine-gutter); }
.night--vine .night-steps li { position: relative; }
.night--vine .night-steps li + li { margin-top: var(--space-xl); }

/* Each segment enters and leaves on the centre line, so segments stay joined no
   matter how long a beat's copy runs; alternating the flip produces the sway. */
.night--vine .night-steps li::before {
  content: "";
  position: absolute;
  left: calc(var(--vine-gutter) * -1);
  top: 0;
  bottom: calc(var(--space-xl) * -1);
  width: var(--vine-w);
  background-color: var(--accent);
  opacity: 0.65;
  -webkit-mask: url("../brand/vine-seg.svg") no-repeat center / 100% 100%;
          mask: url("../brand/vine-seg.svg") no-repeat center / 100% 100%;
}
.night--vine .night-steps li:last-child::before { bottom: 0.5em; }
.night--vine .night-steps li:nth-child(even)::before { transform: scaleX(-1); }

/* The three leaves are the nest's own, each used at its natural orientation so
   they alternate sides without being mirrored copies of one shape. */
.night--vine .night-steps li::after {
  content: "";
  position: absolute;
  top: -0.2em;
  height: 22px;
  background-color: var(--accent);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.night--vine .night-steps li:nth-child(1)::after {          /* up-right */
  width: 18px;
  left: calc(var(--vine-gutter) * -1 + var(--vine-c));
  -webkit-mask-image: url("../brand/leaf-a.svg");
          mask-image: url("../brand/leaf-a.svg");
}
.night--vine .night-steps li:nth-child(2)::after {          /* up-left */
  width: 23px;
  left: calc(var(--vine-gutter) * -1 + var(--vine-c) - 23px);
  -webkit-mask-image: url("../brand/leaf-b.svg");
          mask-image: url("../brand/leaf-b.svg");
}
.night--vine .night-steps li:nth-child(3)::after {          /* narrower leaf, same angle and length as the first */
  width: 16px;
  left: calc(var(--vine-gutter) * -1 + var(--vine-c));
  -webkit-mask-image: url("../brand/leaf-c.svg");
          mask-image: url("../brand/leaf-c.svg");
}
.night--vine .night-steps li:nth-child(4)::after {          /* a fourth beat sways back left */
  width: 23px;
  left: calc(var(--vine-gutter) * -1 + var(--vine-c) - 23px);
  -webkit-mask-image: url("../brand/leaf-b.svg");
          mask-image: url("../brand/leaf-b.svg");
}


/* ============================================================
   Services

   No prices on the page (Vânia, 2026-09-09; until then packages were priced,
   never the hourly rate). Every band ends on one line that points at the form.
   ============================================================ */

/* The services "intro" is only the jump strip — a ruled row of four links that
   leads straight into the first band. */
main > .services { padding-block: var(--space-l) var(--space-2xl); }
/* The index strip sets its own, much smaller top padding, so the blanket
   correction in base.css (which assumes --section-y) overshoots here and hides
   the four services behind the bar. Same landing, measured against its own
   padding. The id is needed to outrank `main > section[id]` in base.css. */
main > section#services { scroll-margin-top: calc(var(--space-m) - var(--space-l)); }

/* A way in: four services is long enough that visitors should be able to jump. */
.services__index {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-l);
  padding-block: var(--space-m);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
/* Once the four fit on one line they spread across the full rule, first and
   last flush with its ends, so the strip reads as a row of stops rather than
   a cluster at the left. */
@media (min-width: 900px) {
  .services__index { justify-content: space-between; }
}
.services__index a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;                 /* touch target floor */
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  color: var(--ink);
  text-decoration: none;
}
.services__index a::before {
  content: "";
  width: 10px;
  height: 13px;
  background-color: var(--meadow);
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
}
.services__index a:hover { color: var(--accent-hover); }

/* Each service is a full-bleed band; the ground alternates dark/light and the
   dark ones are the services that happen at night. */
.service__head {
  display: grid;
  gap: var(--space-xl);
}

/* The rail holds its own column on desktop and sits under the copy on narrow
   screens, where a side rail would squeeze the measure. */
@media (min-width: 900px) {
  .service__head {
    grid-template-columns: minmax(0, 1.45fr) minmax(0, 0.55fr);
    column-gap: var(--space-2xl);
    align-items: start;
  }
}

.service__feature-label,
.service__includes-label {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.service__body > .service__feature-label { margin-top: var(--space-xl); margin-bottom: var(--space-l); }
.service__includes-label { margin-top: var(--space-2xl); margin-bottom: var(--space-xs); }

.service__body > * + * { margin-top: var(--space-m); }

/* The service name runs across the whole band between two meadow rules, the
   lockup's own ── Village ── device, above both columns (Daniel, 2026-09-08: option G
   of site/_compare/taglines.html, with the title and the tagline's roles swapped). */
.service__title {
  display: flex;
  align-items: center;
  gap: var(--space-l);
  margin-bottom: var(--space-2xl);
  font-family: var(--font-display);
  -webkit-text-stroke: var(--display-weight) currentColor;
  paint-order: stroke fill;
  font-weight: 400;
  font-size: calc(var(--step-2) * 1.15);   /* step-3 was too big; still a clear step above the tagline */
  line-height: var(--leading-snug);
  text-align: center;
  color: var(--ink);
}
.service__title > span { flex: 0 1 auto; min-width: 0; text-wrap: balance; }
.service__title::before,
.service__title::after {
  content: "";
  flex: 1 0 1.5rem;
  height: 1.2px;
  background: var(--meadow);
  opacity: 0.7;
}
/* On phones the name wraps to two or three lines and the side rules would be
   stubs, so the title stacks over one short rule instead (the hero plate's device). */
@media (max-width: 639px) {
  .service__title { display: grid; justify-items: center; gap: var(--space-m); }
  .service__title::before { display: none; }
  .service__title::after { flex: none; width: 3rem; }
}

/* The tagline carries the old hero's device — an Italiana line whose closing
   phrase turns to script — so each service opens its column with the promise in
   the brand's own voice, clearly smaller than the ruled title above it. */
.service__tagline {
  font-family: var(--font-display);
  -webkit-text-stroke: var(--display-weight) currentColor;
  paint-order: stroke fill;
  font-weight: 400;
  font-size: calc(var(--step-1) * 1.25);   /* Italiana's hairlines need the size */
  line-height: var(--leading-snug);
  color: var(--ink);
  max-width: 34ch;
  text-wrap: balance;
}
.service__tagline .script { font-size: 1.18em; letter-spacing: 0.01em; }
.service__body > .service__tagline + p { margin-top: var(--space-l); }   /* more air under the promise line */

/* Every green fails on deep green except the light one; on every dark ground the
   script uses the dark-ground green, exactly as the footer lockup does. */
.section--night .script { color: var(--light-green); }

/* Hairline rows across the full band — the same editorial language as the
   specialties list, so the two read as one family rather than two list styles. */
.service__includes {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0;
}
@media (min-width: 700px)  { .service__includes { grid-template-columns: 1fr 1fr; column-gap: var(--space-xl); } }
@media (min-width: 1000px) { .service__includes { grid-template-columns: 1fr 1fr 1fr; column-gap: var(--space-2xl); } }
.service__includes li {
  display: flex;
  align-items: baseline;
  gap: var(--space-s);
  padding-block: var(--space-s);
  border-bottom: 1px solid var(--rule);
  font-size: var(--step--1);
}
.service__includes li::before {
  content: "";
  flex: none;
  width: 11px;
  height: 14px;
  background-color: var(--meadow);   /* icon fill — a sanctioned use of Meadow */
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
}

/* ---- The rail ----
   The right column is the "what's included" rail (Daniel, 2026-09-09: option B of
   site/_compare/bands.html, after the price cards came off). On the dark bands it
   is the photo with the includes list in one column beneath it, so the rail runs
   as tall as the timeline beside it; the light bands keep their wide photo under
   the text and their rail is the list alone. */
.service__rail { display: grid; gap: var(--space-m); align-content: start; }
.service__rail .service__includes-label { margin-top: var(--space-xs); margin-bottom: 0; }
.service__rail > .service__includes-label:first-child { margin-top: 0; }
.service__rail .service__includes { grid-template-columns: 1fr; column-gap: 0; margin-top: calc(-1 * var(--space-s)); }

.service__photo { margin: 0; border-radius: var(--radius-m); overflow: hidden; background: var(--rule); }
.service__photo img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.service__body > .service__photo--feature { margin-top: var(--space-xl); }
.service__photo--feature img { aspect-ratio: 16 / 9; }

.section--night .service__includes li::before { background-color: var(--accent); }

/* The line that closes every band, where the "personalized packages" card used
   to be: the one pointer from a service to the form. */
.service__cta { margin-top: var(--space-xl); font-size: var(--step-0); max-width: none; }
.service__cta a { font-weight: 500; }

/* ============================================================
   About
   ============================================================ */

.about { display: grid; gap: var(--space-xl); }
@media (min-width: 900px) {
  .about {
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    column-gap: var(--space-2xl);
    align-items: start;
  }
}
/* The photo bleeds to the edge of the viewport, so it is part of the band rather
   than an object placed on it: full width on small screens, to the left edge on
   large ones (the negative margin is the container's own centring offset plus the
   gutter, floored at the gutter for viewports narrower than the container). */
.about__photo {
  margin: 0;
  margin-inline: calc(-1 * var(--gutter));
  border-radius: 0;
  overflow: hidden;
  background: var(--rule);
}
.about__photo img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; }
@media (min-width: 900px) {
  .about__photo {
    margin-inline: calc(-1 * max(var(--gutter), (100vw - var(--container)) / 2 + var(--gutter))) 0;
    border-radius: 0 var(--radius-m) var(--radius-m) 0;
  }
}
.about__body > * + * { margin-top: var(--space-l); }
.about h2 { margin-top: var(--space-xs); margin-bottom: var(--space-m); }

/* The thesis of the whole practice, opening her introduction. */
.about__lede {
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  max-width: 30ch;
}

.creds {
  list-style: none;
  padding: 0;
  margin-top: var(--space-xl);
  display: grid;
  gap: var(--space-s);
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  color: var(--ink-muted);
  border-top: 1px solid var(--rule);
  padding-top: var(--space-l);
}

/* Credentials link out to the certifying bodies: ink, with a meadow underline so
   the links read as part of the list rather than as a row of buttons. */
.creds a {
  color: var(--ink);
  text-decoration-color: var(--meadow);
  text-decoration-thickness: 1px;
}
.creds a:hover { text-decoration-color: var(--ink); }

/* ---- The journey: the vine on its side ----
   The places she has worked, in order, on a horizontal run of the Overnight
   timeline's vine. One continuous stem (a repeating wave whose crossings land on
   the stops, with a tail past each end), a leaf at every stop, and the places
   alternating above and below the stem: above, below, above, below, above.
   Three grid rows: labels above / the stem / labels below; each stop owns one
   column and sits at its centre. Below 640px the stem goes and the stops become
   a leaf-marked "place · year" list, the hero credentials' device. ---- */
.journey {
  --stops: 5;
  list-style: none;
  padding: 0;
  margin: var(--space-l) 0 0;
  max-width: 34rem;                                /* tighter than the column: the stops sit closer */
  display: grid;
  grid-template-columns: repeat(var(--stops), 1fr);
  grid-template-rows: auto var(--vine-w) auto;
  row-gap: var(--space-2xs);
}
/* the stem, full width, so it runs half a column past the first and last stop */
.journey::before {
  content: "";
  grid-row: 2;
  grid-column: 1 / -1;
  height: var(--vine-w);
  background-color: var(--meadow);
  opacity: 0.8;
  /* one wave = two stops wide; it starts on a trough so the crossings fall on the column centres */
  -webkit-mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / calc(200% / var(--stops)) 100%;
          mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / calc(200% / var(--stops)) 100%;
}
.journey li {
  position: relative;
  text-align: center;
  padding-inline: var(--space-2xs);
}
.journey li:nth-child(1) { grid-column: 1; }
.journey li:nth-child(2) { grid-column: 2; }
.journey li:nth-child(3) { grid-column: 3; }
.journey li:nth-child(4) { grid-column: 4; }
.journey li:nth-child(5) { grid-column: 5; }
.journey li:nth-child(odd)  { grid-row: 1; align-self: end; }
.journey li:nth-child(even) { grid-row: 3; align-self: start; }
/* the stop: a leaf on the crossing, standing above it on odd stops, hanging
   below and turned a quarter clockwise on even ones */
.journey li::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 13px;
  height: 16px;
  background-color: var(--meadow-deep);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.journey li:nth-child(odd)::after {
  top: calc(100% + var(--space-2xs) + var(--vine-c) - 15px);
  translate: -20% 0;
  transform: rotate(-10deg);             /* leans back off the stem */
  -webkit-mask-image: url("../brand/leaf-a.svg");
          mask-image: url("../brand/leaf-a.svg");
}
.journey li:nth-child(even)::after {
  bottom: calc(100% + var(--space-2xs) + (var(--vine-w) - var(--vine-c)) - 15px);
  translate: calc(-80% + 4px) 0;         /* nudged right so the turned base meets the crossing */
  /* the same leaf as the upper stops, turned to hang from the stem */
  transform: rotate(115deg);
  -webkit-mask-image: url("../brand/leaf-a.svg");
          mask-image: url("../brand/leaf-a.svg");
}
.journey__place {
  display: block;
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  color: var(--ink);
  line-height: var(--leading-snug);
  white-space: nowrap;
}
/* The last stop is a region, not a town, and takes two lines: a 12em box centred
   on its column, spilling evenly either side (the neighbour's label hangs below
   the stem, so nothing collides). */
.journey li:last-child .journey__place {
  white-space: normal;
  text-wrap: balance;
  width: 12em;
  margin-left: calc(50% - 6em);
}
.journey__year {
  display: block;
  margin-top: 2px;
  font-size: var(--step--1);
  color: var(--ink-muted);
}
/* above the stem the year sits nearest the vine, so the two labels mirror */
.journey li:nth-child(odd) { display: flex; flex-direction: column-reverse; }
.journey li:nth-child(odd) .journey__year { margin: 0 0 2px; }
.journey li:nth-child(odd) { padding-bottom: 14px; }
.journey li:nth-child(even) { padding-top: 14px; }
@media (max-width: 639px) {
  .journey { display: flex; flex-wrap: wrap; gap: var(--space-s) var(--space-l); max-width: none; }
  .journey::before { display: none; }
  .journey li,
  .journey li:nth-child(odd),
  .journey li:nth-child(even) { display: block; padding: 0 0 0 1.2rem; text-align: left; }
  /* both nth-child rules, so this outranks the desktop's alternating leaves */
  .journey li:nth-child(odd)::after,
  .journey li:nth-child(even)::after {
    top: 0.3em; bottom: auto; left: 0; translate: none; width: 10px; height: 13px; transform: none;
    -webkit-mask-image: url("../brand/leaf-a.svg"); mask-image: url("../brand/leaf-a.svg");
  }
  .journey__place,
  .journey li:last-child .journey__place { display: inline; width: auto; margin-left: 0; }
  .journey__year,
  .journey li:nth-child(odd) .journey__year { display: inline; margin: 0 0 0 0.45em; }
}

.creds--specialties li::before {
  content: "";
  flex: none;
  width: 10px;
  height: 13px;
  background-color: var(--meadow);
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
}
.creds--specialties li { display: flex; align-items: baseline; gap: var(--space-xs); }
.creds--specialties li::before { translate: 0 1px; }

/* A second creds list, labelled, in two columns — six short items. */
.creds__label {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: var(--space-xl);
}
.about__body > .creds--specialties { margin-top: var(--space-s); }
@media (min-width: 600px) {
  .creds--specialties { grid-template-columns: 1fr 1fr; column-gap: var(--space-l); }
}

/* ============================================================
   Story — why "Village"
   ============================================================ */

.story > * + * { margin-top: var(--space-l); }
.story h2 { margin-top: var(--space-xs); margin-bottom: var(--space-m); }

/* Same optical rebalance as the hero: Great Vibes carries less weight than
   Italiana at equal size. */
.story h2 .script { font-size: 1.15em; }

/* The welcome promise closes the story under a hairline, in the same quiet
   language as the credentials list: no fill, no box. (It was a cream card on the
   sage ground and shouted over the story it belongs to.) */
.story__family {
  margin-top: var(--space-xl);
  padding-top: var(--space-l);
  border-top: 1px solid var(--rule);
}
.story__family h3 { margin-bottom: var(--space-xs); }

/* ============================================================
   FAQ — hairline rows, native disclosure, no JS
   ============================================================ */

.faq__head { margin-bottom: var(--space-xl); }
.faq__head h2 { margin-top: var(--space-xs); }

.faq details { border-bottom: 1px solid var(--rule); }
.faq details:first-of-type { border-top: 1px solid var(--rule); }

.faq details { position: relative; }
.faq summary {
  list-style: none;
  display: block;
  position: relative;
  min-height: 44px;                 /* touch target floor */
  padding-block: var(--space-m);
  padding-left: 1.75rem;            /* room for the leaf */
  padding-right: var(--space-m);
  cursor: pointer;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary h3 { font-size: var(--step-0); pointer-events: none; }  /* the row is the target */

/* The marker is the page's own bullet: the nest's leaf before the question, faint
   until the row opens (the same move as a chosen chip). No icon on the right; the
   open state is carried by the leaf, the question's colour, and the rule below. */
.faq summary::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 50%;
  translate: 0 -50%;
  width: 11px;
  height: 14px;
  background-color: var(--meadow);
  opacity: 0.4;
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
  transition: opacity var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.faq summary:hover::before { opacity: 0.8; }
.faq details[open] summary::before { opacity: 1; background-color: var(--meadow-deep); }
.faq details[open] summary h3 { color: var(--meadow-deep); }

/* When a row opens, its bottom hairline grows into a meadow rule from the left:
   the nav underline's move, and the lockup's "── Village ──" language. */
.faq details::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -1px;                     /* over the row's own hairline */
  width: 100%;
  height: 1.2px;
  background: var(--meadow);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-slow) var(--ease-out);
}
.faq details[open]::after { transform: scaleX(1); }

.faq details > p {
  padding-left: 1.75rem;
  padding-bottom: var(--space-l);
  max-width: var(--measure);          /* the body measure; the tight one boxed the answer under a full-width question */
}

/* ============================================================
   Reviews — we own the frame, Trustindex owns the contents
   ============================================================ */

/* The embed brings its own cards, stars, Google branding and carousel, so there
   is deliberately no card CSS here. Anything we add would either be overridden
   or fight the widget. Styling of the cards themselves is done in the Trustindex
   dashboard, not in this file.

   The section is hidden until the widget renders something — see main.js. */
.reviews__head { margin-bottom: var(--space-xl); }
.reviews__head h2 { margin-top: var(--space-xs); }

/* ============================================================
   Closer — the tagline, on the night's own ground
   ============================================================ */

.closer__inner { text-align: center; }
.closer h2 { font-size: var(--step-3); margin-inline: auto; }
.closer__line { display: block; }
.closer__line--script { margin-top: var(--space-s); }

/* Meadow fails on deep green even as display; the dark-ground green carries the
   script here, exactly as it does in the footer lockup. */
.closer .script { font-size: 1.18em; }

/* ============================================================
   Contact
   ============================================================ */

.contact h2 { margin-top: var(--space-xs); margin-bottom: var(--space-xl); }

.contact-form { display: grid; gap: var(--space-l); max-width: 34rem; }
.field { display: grid; gap: var(--space-xs); }
.field label { font-weight: 600; font-size: var(--step--1); }

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 0.75em 0.9em;
  border: 1px solid var(--ink-muted);   /* a control boundary needs 3:1; --rule was 1.27:1 */
  border-radius: var(--radius-s);
  background: var(--bg);
  color: var(--ink);
  font-size: var(--step-0);
}
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { border-color: var(--accent); }
.field textarea { resize: vertical; min-height: 8rem; }
.field input::placeholder { color: var(--ink-muted); opacity: 1; }

/* ---- Location: a city or county, and a state ----
   (Vânia, 2026-09-09.) One field, two controls on a row; the hint sits inside the
   label so it is read with it. The select is drawn like the inputs, with a
   deep-green chevron in place of the platform arrow. */
.field__hint { font-weight: 400; color: var(--ink-muted); }
.field__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(10rem, 0.55fr);
  gap: var(--space-xs);
}
@media (max-width: 480px) { .field__row { grid-template-columns: 1fr; } }
.field select {
  appearance: none;
  padding-right: 2.4em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%233C4A39' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9em center;
  cursor: pointer;
}
.field select:invalid { color: var(--ink-muted); }   /* the "State" prompt reads as a placeholder */

/* ---- Due date ----
   The field is the State select's twin: same chevron, same right padding, so the
   two controls that open something look like one another. main.js adds readonly,
   so the caret never appears and the panel is the only way in. The panel itself
   is drawn as the inputs are drawn, and it reads as the field opening rather
   than as a widget landing on top of it. */
.due { position: relative; }
.field--due input {
  padding-right: 2.4em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%233C4A39' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9em center;
  cursor: pointer;
}
.due__panel {
  position: absolute;
  z-index: 20;
  inset-inline: 0;
  top: calc(100% + 6px);
  padding: var(--space-m);
  border: 1px solid var(--ink-muted);
  border-radius: var(--radius-m);
  background: var(--bg);
  box-shadow: 0 10px 30px -18px rgba(60, 74, 57, 0.55);
  /* Closed, and genuinely out of the page: display:none keeps the panel out of
     the tab order and the a11y tree, which visibility alone does less cleanly.
     `allow-discrete` lets display flip at the right end of the fade, and
     @starting-style gives the opening transition a value to start from — without
     it an element arriving from display:none simply appears. The global
     reduced-motion rule collapses these durations to nothing. */
  display: none;
  opacity: 0;
  translate: 0 -6px;
  transition: opacity var(--dur-fast) var(--ease-out),
              translate var(--dur-fast) var(--ease-out),
              display var(--dur-fast) allow-discrete;
}
.due__panel.is-open {
  display: block;
  opacity: 1;
  translate: none;
}
@starting-style {
  .due__panel.is-open {
    opacity: 0;
    translate: 0 -6px;
  }
}

/* The year is a line of Italiana between two arrows rather than a second select:
   one control per question, and the panel opens with the months, not with chrome. */
.due__year {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-s);
  margin-bottom: var(--space-m);
}
.due__year output {
  font-family: var(--font-display);
  -webkit-text-stroke: var(--display-weight) currentColor;
  paint-order: stroke fill;
  font-size: var(--step-1);
  line-height: 1;
  min-width: 4ch;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.due__step {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: none;
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.due__step:hover:not(:disabled) { border-color: var(--ink); background: var(--bg-alt); }
.due__step:disabled { opacity: 0.35; cursor: default; }
.due__step svg { width: 8px; height: 12px; }

/* The months are the service chips at a smaller size: same pill, same leaf,
   faint until chosen and then light green on deep green. */
.due__months { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2xs); }
.due__month {
  min-height: 44px;                    /* touch target floor */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45em;
  padding: 0.5em 0.4em;
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  background: none;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.due__month::before {
  content: "";
  flex: none;
  width: 9px;
  height: 12px;
  background-color: var(--meadow);
  opacity: 0.4;
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
  transition: opacity var(--dur-fast) var(--ease-out),
              rotate var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.due__month:hover { background: var(--bg-alt); border-color: var(--ink); }
.due__month:hover::before { rotate: -12deg; opacity: 0.8; }
.due__month[aria-pressed="true"] { background: var(--ink); color: var(--cream); border-color: var(--ink); }
.due__month[aria-pressed="true"]::before { background-color: var(--light-green); opacity: 1; rotate: 8deg; }
.due__month:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The day appears only once a month is chosen, so the common answer costs one
   press and the precise one costs two. */
.due__day { margin-top: var(--space-m); padding-top: var(--space-m); border-top: 1px solid var(--rule); }
.due__day-lead { font-size: var(--step--1); color: var(--ink-muted); margin-bottom: var(--space-m); }
.due__dow,
.due__days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  max-width: 19rem;                    /* the panel is field-wide; the grid is not */
  margin-inline: auto;
}
.due__dow { margin-bottom: var(--space-2xs); }
.due__dow span { text-align: center; font-size: 11px; letter-spacing: 0.06em; color: var(--ink-muted); }
.due__cell {
  aspect-ratio: 1;
  min-height: 36px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.due__cell:hover { background: var(--bg-alt); }
.due__cell[aria-pressed="true"] { background: var(--ink); color: var(--cream); }
.due__cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- Service toggles ----
   Native checkboxes drawn as leaf-marked pills, the same device as the services
   strip and the includes lists. The input stays in the DOM (visually hidden) so
   keyboard, screen readers and the form payload all work for free; the label is
   the chip. Needs :has() for the checked and focus states, which every current
   browser has; without it the chips still toggle, they just do not fill. */
.choices {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}
.choices legend {
  padding: 0;
  margin-bottom: var(--space-s);
  font-weight: 600;
  font-size: var(--step--1);
}
.choices__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  min-height: 44px;                    /* touch target floor */
  padding: 0.5em 1.1em 0.5em 0.95em;
  border: 1px solid var(--ink-muted);
  border-radius: var(--radius-pill);
  background: transparent;
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  user-select: none;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
/* The leaf is the checkmark: faint until chosen, then light green on deep green. */
.chip::before {
  content: "";
  flex: none;
  width: 10px;
  height: 13px;
  background-color: var(--meadow);
  opacity: 0.4;
  -webkit-mask: url("../brand/leaf-a.svg") no-repeat center / contain;
          mask: url("../brand/leaf-a.svg") no-repeat center / contain;
  transition: opacity var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.chip:hover { border-color: var(--ink); background: var(--bg-alt); }
.chip:has(input:checked) {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
}
.chip:has(input:checked)::before { background-color: var(--light-green); opacity: 1; }
.chip:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* The one place Clay survives: a warm colour that means "look here". */
.field__err { font-size: var(--step--1); color: var(--danger); }
.field[data-invalid] input,
.field[data-invalid] select,
.field[data-invalid] textarea { border-color: var(--danger); }

.contact-form .btn { justify-self: start; }
.form-note { font-size: var(--step--1); color: var(--ink-muted); }
.form-note a { white-space: nowrap; }   /* an email or a phone number never splits across lines */

/* ---- Sending, sent, failed ----
   The Turnstile box only takes space once the widget decides to show something
   (interaction-only), so an empty container collapses. */
.contact-form.is-sending .btn { opacity: 0.7; cursor: progress; }

/* The Turnstile box should only take space once the widget shows something.
   :empty covers the case where the script never ran. It is not enough once it
   has: in interaction-only mode Turnstile fills the container with a wrapper div
   and the hidden token input — nothing visible, but ~32px tall — so the container
   became a grid row of its own and the Send button sat 4rem below the message box
   where every other pair in the form sits 2rem apart.

   The iframe is what earns the row: Turnstile draws the actual challenge in one,
   and renders none while it is working invisibly. It has to leave the grid
   entirely rather than merely collapse — a zero-height row still costs both of
   its gaps, and a negative margin moves the item without shortening the row.
   :has() is live and settled during style recalc, before layout, so there is no
   frame in which the iframe exists and its container is still hidden; a challenge
   that does appear is laid out at the form's full width like any other row. The
   hidden token input keeps submitting either way — display has no bearing on
   FormData. */
.contact-form__turnstile:empty,
.contact-form:not(:has(.contact-form__turnstile iframe)) .contact-form__turnstile {
  display: none;
}

/* Didn't send. Sage, the page's own alternate ground, and no coloured bar down
   the left edge: a send that failed is not the visitor's mistake, and clay is
   reserved for the field errors that are. The panel assumes the send is not
   coming back and gets her out with her text intact. */
.contact-form__fail {
  margin: 0;
  padding: var(--space-m);
  border-radius: var(--radius-m);
  background: var(--bg-alt);
  font-size: var(--step--1);
  color: var(--ink);
}
.contact-form__fail a { color: var(--accent); }
.contact-form__fail--panel { display: grid; gap: var(--space-s); }
.contact-form__fail--panel p { margin: 0; max-width: var(--measure-tight); }
.contact-form__fail-lead { font-weight: 600; }
.contact-form__fail-row { display: flex; flex-wrap: wrap; gap: var(--space-xs); align-items: center; }
.contact-form__fail-row .btn { text-decoration: none; }

/* Sent. No panel and no ground of its own — the form's footprint simply becomes
   a sentence, in the hand the closer signs off in two sections above. */
.contact-form__done {
  display: grid;
  gap: var(--space-s);
  padding-block: var(--space-l);
  max-width: var(--measure-tight);
}
.contact-form__done p { margin: 0; }
/* .script sets line-height 1, which collides on the second line: a long name
   wraps here where the closer's fixed line never does. */
.contact-form__done-lead { font-size: clamp(2.2rem, 1.6rem + 2.4vw, 3.2rem); line-height: 1.15; }
.contact-form__done-rule { width: 46px; height: 1px; background: var(--rule); }

/* ============================================================
   Footer — closes the page on the same ground the night opened on
   ============================================================ */

.site-footer {
  background: var(--bg-inverse);
  --ink: var(--ink-inverse);
  --ink-muted: var(--ink-inverse-muted);
  --rule: var(--rule-inverse);
  --accent: var(--light-green);
  color: var(--ink);
  padding-block: var(--space-3xl);
}
.site-footer__inner {
  display: grid;
  gap: var(--space-xl);
  align-items: start;
}
/* Two columns: the mark on its own, then the contact lines with the legal line
   beneath them. Three columns spread the legal line to the far edge and left the
   mark cramped between (Daniel, 2026-09-08). */
@media (min-width: 760px) {
  .site-footer__inner {
    grid-template-columns: auto 1px auto;
    justify-content: center;          /* gathered at the centre, not pushed to the edges */
    column-gap: clamp(var(--space-xl), 2rem + 3vw, var(--space-3xl));
    row-gap: var(--space-l);
    align-items: center;
  }
  .site-footer .lockup { grid-row: 1 / 3; }
  /* a thin light-green strip between the mark and the contact block, the
     lockup's own rule colour, running the height of both rows */
  .site-footer__inner::before {
    content: "";
    grid-column: 2;
    grid-row: 1 / 3;
    align-self: stretch;
    background: var(--light-green);
    opacity: 0.6;
  }
  .site-footer__contact { grid-column: 3; align-self: end; }
  .site-footer__legal { grid-column: 3; align-self: start; }
}
.site-footer .lockup { --lockup-size: var(--lockup-footer); }

/* Reversed lockup. The nest uses the brand's own dark-ground asset — white body,
   Meadow leaves — rather than a CSS filter, which would whiten the leaves too. */
.site-footer .lockup .the,
.site-footer .lockup .rm { color: var(--ink); }
.site-footer .lockup .gv { color: var(--light-green); }
.site-footer .lockup .vr .ln { background: var(--light-green); }

.site-footer__contact { display: grid; gap: var(--space-2xs); font-size: var(--step--1); }
.site-footer__contact a { color: var(--accent); }
.site-footer__legal { font-size: var(--step--1); color: var(--ink-muted); }

.social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;      /* touch target floor */
  margin-left: -10px;             /* optically align the glyph with the text above */
}
.social__icon {
  width: 21px; height: 21px;
  background-color: var(--accent);
  -webkit-mask: url("../brand/icon-instagram.svg") no-repeat center / contain;
          mask: url("../brand/icon-instagram.svg") no-repeat center / contain;
}
.social:hover .social__icon { background-color: var(--ink); }

.hero__photo picture { display: block; width: 100%; height: 100%; }

/* ============================================================
   Motion — growth, everywhere
   One idea: the page grows. Vines draw themselves in, leaves unfurl
   from their base and then sway, script writes itself, the hero settles.
   Transforms, opacity and clip-path only. Every initial hidden state
   lives inside the no-preference guard, exactly like the reveal in
   base.css: under reduced motion nothing here is ever applied, and with
   JavaScript off nothing is ever hidden. Chosen from
   site/_compare/motion.html (Daniel, 2026-09-08).
   ============================================================ */

/* ---- The leaf lifts toward the question (FAQ, candidate A) ----
   At rest it leans back; hovering lifts it upright, like turning to the light;
   open, it settles a few degrees past. Interaction-driven, so no guard. */
.faq summary::before {
  transform-origin: 0 100%;
  rotate: -14deg;
  transition: opacity var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out),
              rotate var(--dur-fast) var(--ease-out),
              translate var(--dur-fast) var(--ease-out);
}
.faq summary:hover::before { rotate: 0deg; translate: 0 -55%; }
.faq details[open] summary::before { rotate: 10deg; translate: 1px -50%; }

/* The answer slides open instead of snapping. ::details-content plus
   interpolate-size (base.css) is Chrome 131+; elsewhere it snaps as before. */
.faq details::details-content {
  block-size: 0;
  overflow-y: clip;
  transition: block-size var(--dur-mid) var(--ease-out),
              content-visibility var(--dur-mid) allow-discrete;
}
.faq details[open]::details-content { block-size: auto; }
.faq details > p {
  opacity: 0;
  translate: 0 -4px;
  transition: opacity var(--dur-mid) var(--ease-out), translate var(--dur-mid) var(--ease-out);
}
.faq details[open] > p { opacity: 1; translate: none; transition-delay: 60ms; }

/* ---- The static leaves tilt when their row is hovered ----
   Each uses whichever of rotate / transform its reveal or sway is not already
   animating, so the tilt composes with them instead of being overridden. The
   journey leaves pivot at their centre (that is how they sit on the stem). */
.service__includes li::before,
.hero__creds li::before,
.menu__sub a::before,
.night--vine .night-steps li::after {
  transform-origin: 0 100%;
  transition: transform var(--dur-fast) var(--ease-out), rotate var(--dur-fast) var(--ease-out);
}
.service__includes li:hover::before,
.night--vine .night-steps li:hover::after { transform: rotate(-10deg); }
.hero__creds li:hover::before,
.menu__sub a:hover::before,
.creds--specialties li:hover::before { rotate: -10deg; }
/* the About journey's leaves stay still: they are stops on a map, not rows (Daniel, 2026-09-08) */

/* ---- Leaves perk up wherever a leaf marks something you can touch ---- */
.services__index a::before,
.chip::before,
.btn__leaf,
.creds--specialties li::before {
  transform-origin: 0 100%;
  transition: rotate var(--dur-fast) var(--ease-out),
              translate var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.services__index a:hover::before { rotate: -12deg; translate: 0 -1px; }
.chip:hover::before { rotate: -12deg; opacity: 0.8; }
.chip:has(input:checked)::before { rotate: 8deg; }
.btn:hover .btn__leaf { rotate: -14deg; translate: 0 -1px; }

@media (prefers-reduced-motion: no-preference) {

  /* ---- Reveal containers whose children carry their own motion ----
     These fade only; the lift belongs to the rows, leaves and stems inside.
     The three inner triggers are not hidden at all: they exist so the
     observer fires when the vine or the list itself is in view, not when
     the top of its tall band is. */
  .js .service__inner[data-reveal],
  .js .about[data-reveal],
  .js .closer__inner[data-reveal] { transform: none; transition-duration: var(--dur-mid); }
  .js .night-steps[data-reveal],
  .js .journey[data-reveal],
  .js .service__includes[data-reveal] { opacity: 1; transform: none; transition: none; }

  /* ---- The vine draws itself in (Overnight, Sleep Guidance) ----
     Stem first, the leaf as the stem passes it, then the words: one beat per
     step. Leaves unfurl from their base (leaf-a and leaf-c pivot bottom-left,
     leaf-b bottom-right). */
  .js .night--vine .night-steps[data-reveal] li::before {
    clip-path: inset(0 0 100% 0);
    transition: clip-path 900ms linear;
  }
  .js .night--vine .night-steps[data-reveal] li::after {
    scale: 0;
    opacity: 0;
    transition: scale 600ms var(--ease-out), opacity 300ms var(--ease-out), rotate 600ms var(--ease-out),
                transform var(--dur-fast) var(--ease-out);   /* the hover tilt */
  }
  .js .night--vine .night-steps[data-reveal] li:nth-child(odd)::after  { transform-origin: 0 100%;    rotate: -25deg; }
  .js .night--vine .night-steps[data-reveal] li:nth-child(even)::after { transform-origin: 100% 100%; rotate: 25deg; }
  .js .night--vine .night-steps[data-reveal] li > * {
    opacity: 0;
    translate: 0 10px;
    transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out);
  }
  .js .night--vine .night-steps[data-reveal].is-in li::before { clip-path: inset(0 0 -2px 0); }
  .js .night--vine .night-steps[data-reveal].is-in li::after  { scale: 1; opacity: 1; rotate: 0deg; }
  .js .night--vine .night-steps[data-reveal].is-in li > *     { opacity: 1; translate: none; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(1)::before { transition-delay: 0ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(1)::after  { transition-delay: 150ms, 150ms, 150ms, 0ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(1) > *     { transition-delay: 300ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(2)::before { transition-delay: 800ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(2)::after  { transition-delay: 950ms, 950ms, 950ms, 0ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(2) > *     { transition-delay: 1100ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(3)::before { transition-delay: 1600ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(3)::after  { transition-delay: 1750ms, 1750ms, 1750ms, 0ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(3) > *     { transition-delay: 1900ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(4)::before { transition-delay: 2400ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(4)::after  { transition-delay: 2550ms, 2550ms, 2550ms, 0ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(4) > *     { transition-delay: 2700ms; }

  /* ---- Once grown, the leaves sway: a few degrees, never in unison ----
     Starts from the resting angle so there is no jump when it begins. */
  @keyframes leaf-sway {
    0%   { rotate: 0deg; }
    50%  { rotate: 3.5deg; }
    100% { rotate: -3deg; }
  }
  .js .night--vine .night-steps[data-reveal].is-in li::after { animation: leaf-sway 5s ease-in-out infinite alternate; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(1)::after { animation-delay: 3000ms; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(2)::after { animation-delay: 4200ms; animation-duration: 6s; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(3)::after { animation-delay: 5100ms; animation-duration: 5.5s; }
  .js .night--vine .night-steps[data-reveal].is-in li:nth-child(4)::after { animation-delay: 6300ms; }

  /* ---- The journey draws left to right ----
     The stem runs across; each place appears as the stem reaches it. The
     journey leaves already carry a transform rotate, so their sway rides on
     that value rather than on the rotate property. */
  .js .journey[data-reveal]::before {
    clip-path: inset(0 100% 0 0);
    transition: clip-path 1600ms linear;
  }
  /* No transform-origin here: the journey leaves are placed by a centre-pivot
     rotate, and moving the pivot for the unfurl shifted the hanging ones off
     the stem. They grow from their centre instead. */
  .js .journey[data-reveal] li::after {
    scale: 0;
    transition: scale 500ms var(--ease-out);
  }
  .js .journey[data-reveal] li > span {
    opacity: 0;
    translate: 0 6px;
    transition: opacity 500ms var(--ease-out), translate 500ms var(--ease-out);
  }
  .js .journey[data-reveal] li:nth-child(even) > span { translate: 0 -6px; }
  .js .journey[data-reveal].is-in::before { clip-path: inset(0 -2px 0 0); }
  .js .journey[data-reveal].is-in li::after { scale: 1; }
  .js .journey[data-reveal].is-in li > span { opacity: 1; translate: none; }
  .js .journey[data-reveal].is-in li:nth-child(1) > span { transition-delay: 160ms; }
  .js .journey[data-reveal].is-in li:nth-child(1)::after { transition-delay: 160ms; }
  .js .journey[data-reveal].is-in li:nth-child(2) > span { transition-delay: 480ms; }
  .js .journey[data-reveal].is-in li:nth-child(2)::after { transition-delay: 480ms; }
  .js .journey[data-reveal].is-in li:nth-child(3) > span { transition-delay: 800ms; }
  .js .journey[data-reveal].is-in li:nth-child(3)::after { transition-delay: 800ms; }
  .js .journey[data-reveal].is-in li:nth-child(4) > span { transition-delay: 1120ms; }
  .js .journey[data-reveal].is-in li:nth-child(4)::after { transition-delay: 1120ms; }
  .js .journey[data-reveal].is-in li:nth-child(5) > span { transition-delay: 1440ms; }
  .js .journey[data-reveal].is-in li:nth-child(5)::after { transition-delay: 1440ms; }
  @keyframes leaf-sway-odd  { 0% { transform: rotate(-10deg); } 50% { transform: rotate(-6deg); }  100% { transform: rotate(-13deg); } }
  @keyframes leaf-sway-even { 0% { transform: rotate(115deg); } 50% { transform: rotate(119deg); } 100% { transform: rotate(112deg); } }
  .js .journey[data-reveal].is-in li:nth-child(odd)::after  { animation: leaf-sway-odd 5s ease-in-out 2000ms infinite alternate; }
  .js .journey[data-reveal].is-in li:nth-child(even)::after { animation: leaf-sway-even 6s ease-in-out 2000ms infinite alternate; }
  .js .journey[data-reveal].is-in li:nth-child(2)::after { animation-delay: 2100ms; }
  .js .journey[data-reveal].is-in li:nth-child(3)::after { animation-delay: 2600ms; }
  .js .journey[data-reveal].is-in li:nth-child(4)::after { animation-delay: 3300ms; }
  .js .journey[data-reveal].is-in li:nth-child(5)::after { animation-delay: 3900ms; }
  /* the narrow list has no stem and its leaves are upright bullets */
  @media (max-width: 639px) {
    .js .journey[data-reveal]::before { display: none; }
    /* both nth-child forms, so this outranks the sway rules above: on the phone
       list every leaf is the upright bullet, all facing the same way */
    .js .journey[data-reveal].is-in li:nth-child(odd)::after,
    .js .journey[data-reveal].is-in li:nth-child(even)::after { animation: none; }
  }

  /* ---- The includes rows cascade, each leaf unfurling as its row lands ---- */
  .js .service__includes[data-reveal] li {
    opacity: 0;
    translate: 0 10px;
    transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out);
  }
  .js .service__includes[data-reveal] li::before {
    scale: 0;
    transform-origin: 0 100%;
    rotate: -25deg;
    transition: scale 500ms var(--ease-out), rotate 500ms var(--ease-out),
                transform var(--dur-fast) var(--ease-out);   /* the hover tilt */
  }
  .js .service__includes[data-reveal].is-in li { opacity: 1; translate: none; }
  .js .service__includes[data-reveal].is-in li::before { scale: 1; rotate: 0deg; }
  .js .service__includes[data-reveal].is-in li:nth-child(1) { transition-delay: 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(2) { transition-delay: 70ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(3) { transition-delay: 140ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(4) { transition-delay: 210ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(5) { transition-delay: 280ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(6) { transition-delay: 350ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(7) { transition-delay: 420ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(8) { transition-delay: 490ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(1)::before { transition-delay: 150ms, 150ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(2)::before { transition-delay: 220ms, 220ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(3)::before { transition-delay: 290ms, 290ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(4)::before { transition-delay: 360ms, 360ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(5)::before { transition-delay: 430ms, 430ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(6)::before { transition-delay: 500ms, 500ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(7)::before { transition-delay: 570ms, 570ms, 0ms; }
  .js .service__includes[data-reveal].is-in li:nth-child(8)::before { transition-delay: 640ms, 640ms, 0ms; }

  /* ---- The script writes itself ----
     The Great Vibes phrase wipes in left to right after its line lands, like a
     pen: service taglines, the story title, the closer. The inset overshoots
     the box so ascenders and the final flourish are never clipped. */
  .js [data-reveal] .service__tagline .script,
  .js [data-reveal] .story h2 .script,
  .js .story[data-reveal] h2 .script,
  .js .closer__inner[data-reveal] .script {
    display: inline-block;
    clip-path: inset(-0.2em 100% -0.3em -0.1em);
    transition: clip-path 900ms cubic-bezier(0.4, 0, 0.6, 1) 400ms;
  }
  .js [data-reveal].is-in .service__tagline .script,
  .js .story[data-reveal].is-in h2 .script,
  .js .closer__inner[data-reveal].is-in .script { clip-path: inset(-0.2em -0.2em -0.3em -0.1em); }
  .js .closer__inner[data-reveal] .closer__line {
    opacity: 0;
    translate: 0 8px;
    transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out);
  }
  .js .closer__inner[data-reveal].is-in .closer__line { opacity: 1; translate: none; }
  .js .closer__inner[data-reveal].is-in .closer__line--script { transition-delay: 300ms; }
  .js .closer__inner[data-reveal].is-in .closer__line--script .script { transition-delay: 700ms; }
  /* The closer is the last line on the page and its one deliberately slow moment:
     the same pen, drawn at less than half speed. The taglines above stay brisk —
     this only reads as slow because everything else is not. */
  .js .closer__inner[data-reveal] .script { transition-duration: 1500ms; }

  /* ---- The hero settles in ----
     On load, once the fonts are in (main.js adds .is-ready to <html>): The,
     then Rested Mama, then Village writes itself and the rules grow out from
     it, then the nest, then the plate: the first rule, the role, the area line,
     the second rule, the credentials. The photo eases in underneath. The nest
     only fades: main.js measures it for the lockup's flight, and a translate
     would move that measurement. */
  .js .hero__lockup .the,
  .js .hero__lockup .rm,
  .js .hero__lockup .gv,
  .js .hero__role,
  .js .hero__area,
  .js .hero__creds li {
    opacity: 0;
    translate: 0 10px;
    transition: opacity 800ms var(--ease-out), translate 800ms var(--ease-out);
  }
  .js .hero__lockup img { opacity: 0; transition: opacity 800ms var(--ease-out); }
  .js .hero__lockup .gv {
    display: inline-block;
    translate: none;
    clip-path: inset(-0.2em 100% -0.3em -0.1em);
    transition: opacity 0ms, clip-path 1100ms cubic-bezier(0.4, 0, 0.6, 1);
  }
  .js .hero__lockup .vr .ln { scale: 0 1; transition: scale 900ms var(--ease-out); }
  .js .hero__lockup .vr .ln:first-child { transform-origin: 100% 50%; }
  .js .hero__lockup .vr .ln:last-child  { transform-origin: 0 50%; }
  .js .hero__plate::before,
  .js .hero__area::after { scale: 0 1; transition: scale 700ms var(--ease-out); }
  .js .hero__frame { opacity: 0; scale: 0.985; transition: opacity 1200ms var(--ease-out), scale 1200ms var(--ease-out); }

  .js.is-ready .hero__lockup .the,
  .js.is-ready .hero__lockup .rm,
  .js.is-ready .hero__lockup .gv,
  .js.is-ready .hero__lockup img,
  .js.is-ready .hero__role,
  .js.is-ready .hero__area,
  .js.is-ready .hero__creds li { opacity: 1; translate: none; }
  .js.is-ready .hero__lockup .gv { clip-path: inset(-0.2em -0.2em -0.3em -0.1em); transition-delay: 0ms, 500ms; }
  .js.is-ready .hero__lockup .the { transition-delay: 100ms; }
  .js.is-ready .hero__lockup .rm  { transition-delay: 250ms; }
  .js.is-ready .hero__lockup .vr .ln { scale: 1 1; transition-delay: 900ms; }
  .js.is-ready .hero__lockup img  { transition-delay: 1100ms; }
  .js.is-ready .hero__plate::before { scale: 1 1; transition-delay: 1500ms; }
  .js.is-ready .hero__role        { transition-delay: 1700ms; }
  .js.is-ready .hero__area        { transition-delay: 1850ms; }
  .js.is-ready .hero__area::after { scale: 1 1; transition-delay: 2050ms; }   /* the second rule grows after the area line */
  .js.is-ready .hero__creds li:nth-child(1) { transition-delay: 2250ms; }
  .js.is-ready .hero__creds li:nth-child(2) { transition-delay: 2350ms; }
  .js.is-ready .hero__creds li:nth-child(3) { transition-delay: 2450ms; }
  .js.is-ready .hero__frame { opacity: 1; scale: 1; transition-delay: 600ms; }
  /* the flying copy in the bar is a different element; the headline going
     ghost must still win over the entrance */
  .js .hero__lockup.is-ghost .the,
  .js .hero__lockup.is-ghost .rm,
  .js .hero__lockup.is-ghost .gv,
  .js .hero__lockup.is-ghost img { transition-delay: 0ms; }

  /* ---- A faint vine drifting through the dark bands ----
     The journey's wave (site/brand/vine-wave-h.svg), stretched to a 1000×200
     tile; the run travels exactly one tile per loop, so it never jumps. Leaves
     were tried on it and taken off again (Daniel, 2026-09-08). */
  @keyframes vine-drift {
    from { translate: 0 0; }
    to   { translate: -1000px 0; }
  }
  .js .section--night { position: relative; overflow: hidden; }
  .js .section--night > * { position: relative; }
  .js .section--night::before {
    content: "";
    position: absolute;
    left: 0;
    top: 295px;                       /* the service bands (Daniel's number) */
    width: calc(100% + 1000px);
    height: 200px;
    background: var(--light-green);
    opacity: 0.12;
    -webkit-mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / 1000px 200px;
            mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / 1000px 200px;
    animation: vine-drift 54s linear infinite;
    pointer-events: none;
  }
  /* the closer is short, so there the wave sits almost centred, just under the script */
  .js .closer::before { top: calc(50% - 95px); }
  /* the tall service bands carry a second vine along the bottom, drifting the
     other way and a touch slower, so the two never read as one pattern */
  @keyframes vine-drift-back {
    from { translate: -1000px 0; }
    to   { translate: 0 0; }
  }
  .js .service.section--night::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 295px;
    width: calc(100% + 1000px);
    height: 200px;
    background: var(--light-green);
    opacity: 0.12;
    -webkit-mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / 1000px 200px;
            mask: url("../brand/vine-wave-h.svg") repeat-x 0 0 / 1000px 200px;
    animation: vine-drift-back 62s linear infinite;
    pointer-events: none;
  }
}

/* ============================================================
   Long jumps — the veil over a multi-screen scroll
   ============================================================
   A nav click from the hero to the contact form travels ten screens, and the
   browser's smooth scroll draws every one of them: a smear of green and cream
   that is unpleasant to watch and, for some people, genuinely dizzying. The veil
   covers the journey. The page dims and blurs before it leaves, clears once it
   has arrived, and the bar stays put throughout as the one fixed thing to hold
   on to — so the effect reads as the page moving under the nav rather than as
   the site blinking out.

   Only long jumps are veiled (main.js decides), so a short hop between adjacent
   sections still behaves like an ordinary anchor. The whole block sits behind
   prefers-reduced-motion: with motion reduced, scroll-behavior is already auto,
   the jump is instant, and there is nothing to cover. */
@media (prefers-reduced-motion: no-preference) {
  :root {
    --jump-out:  260ms;   /* leaving: runs alongside the first of the travel, not before it */
    --jump-in:   650ms;   /* arriving: long, and overlapping the last of the travel */
    --jump-dim:  0.14;
    --jump-blur: 8px;
  }

  /* The footer travels with main; the nav deliberately does not. Neither side is
     delayed: the veil closes the instant the page starts moving, and main.js
     releases it partway through the travel (JUMP_REVEAL_AT), so the clearing
     overlaps the last of the deceleration instead of following it. */
  .js main,
  .js .site-footer {
    transition: opacity var(--jump-in) var(--ease-out),
                filter var(--jump-in) var(--ease-out);
  }
  .js.is-jumping main,
  .js.is-jumping .site-footer {
    opacity: var(--jump-dim);
    filter: blur(var(--jump-blur));
    transition-duration: var(--jump-out);
  }

}
