/* ===========================================================
   Pawfect Grooming — Design tokens
   (ported from the Pawfect Grooming design system)
   =========================================================== */
/* Self-hosted variable fonts (latin subset, ~110 KB total) — the live site no
   longer depends on Google's CDN staying up, and there's one less third-party
   connection to negotiate before text can paint. Files fetched from Google
   Fonts v17/v32; regenerate the same way if weights are ever added. */
@font-face {
  font-family: 'Fredoka';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('../assets/fonts/fredoka-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../assets/fonts/nunito-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: italic;
  font-weight: 400 800;
  font-display: swap;
  src: url('../assets/fonts/nunito-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Sky (primary) */
  --sky-50:  #F1F7FC;
  --sky-100: #DEEEF9;
  --sky-200: #BEDCF1;
  --sky-300: #97C6E7;
  --sky-400: #6FADDA;
  --sky-500: #4E97CE;
  --sky-600: #3B7DB2;
  --sky-700: #30648E;

  /* Blush (secondary / accent) */
  --blush-50:  #FDF2F4;
  --blush-100: #FBE4E7;
  --blush-200: #F6C9CF;
  --blush-300: #EFA9B3;
  --blush-400: #E68794;
  --blush-500: #D96979;
  --blush-600: #C0505F;
  --blush-700: #A84350;

  /* Cream / warm neutral surfaces */
  --cream-50:  #FFFDF9;
  --cream-100: #FBF6EE;
  --cream-200: #F5ECDD;
  --cream-300: #EBDFCB;

  /* Ink (deep friendly navy) */
  --ink-900: #1F2C46;
  --ink-800: #263551;
  --ink-700: #33456A;
  --ink-600: #4A5A7E;
  --ink-500: #6B7896;
  --ink-400: #97A1B8;
  --ink-300: #C3C9D6;
  --ink-200: #E1E4EC;
  --ink-100: #EFF1F5;

  /* Sage */
  --sage-100: #EAF1E7;
  --sage-300: #BFD6B6;
  --sage-500: #8FB584;
  --sage-700: #61815A;

  /* Sunbeam */
  --sun-100: #FDF3D6;
  --sun-300: #F6D98C;
  --sun-500: #EFC65B;

  --white: #FFFFFF;

  /* Semantic aliases.
     Contrast note (Aug 2026): primary/secondary/muted were darkened so white
     button text and small muted text pass WCAG AA. Fix contrast HERE (or with
     a new dark ramp step), never by editing a primitive hex above — the slice
     cursor reads --sky-500 directly and every decorative fill shares the ramps. */
  --color-primary: var(--sky-700);        /* white text 6.3:1 (sky-500 was 3.16:1) */
  --color-primary-hover: var(--sky-700);  /* a lighter hover would fail mid-hover; lift + shadow carry the state */
  --color-primary-press: var(--sky-700);
  --color-primary-soft: var(--sky-100);
  --color-secondary: var(--blush-600);       /* white text 4.6:1 (blush-400 was 2.54:1) */
  --color-secondary-hover: var(--blush-700);
  --color-secondary-soft: var(--blush-100);
  --color-accent: var(--blush-400);

  --surface-page: var(--cream-100);
  --surface-card: var(--white);
  --surface-raised: var(--white);
  --surface-sunken: var(--cream-200);
  --surface-inverse: var(--ink-800);
  --surface-primary: var(--sky-500);

  --text-heading: var(--ink-900);
  --text-body: var(--ink-700);
  --text-muted: var(--ink-600); /* ink-500 sat at 4.1:1 on cream — just under AA */
  --text-subtle: var(--ink-400);
  --text-on-primary: var(--white);
  --text-on-dark: var(--cream-100);
  --text-link: var(--sky-600);
  --text-link-hover: var(--sky-700);

  --border-soft: var(--ink-200);
  --border-default: var(--ink-300);
  --border-strong: var(--ink-400);
  --border-primary: var(--sky-300);

  --color-success: #5FA97E;
  --color-success-soft: #E5F2EA;
  --color-warning: #E0A84E;
  --color-warning-soft: #FBF0D8;
  --color-danger: #D96979;
  --color-danger-soft: #FBE4E7;
  --color-info: var(--sky-500);
  --color-info-soft: var(--sky-100);

  --focus-ring: var(--sky-400);

  /* Typography */
  --font-display: 'Fredoka', 'Trebuchet MS', system-ui, sans-serif;
  --font-body: 'Nunito', system-ui, -apple-system, sans-serif;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --fw-extra: 800;

  --text-hero: clamp(2.75rem, 6vw, 4.5rem);
  --text-display: clamp(2.25rem, 4.5vw, 3.25rem);
  --text-h1: 2.25rem;
  --text-h2: 1.75rem;
  --text-h3: 1.375rem;
  --text-h4: 1.125rem;

  --text-lg: 1.1875rem;
  --text-base: 1rem;
  --text-sm: 0.875rem;
  --text-xs: 0.75rem;

  --lh-tight: 1.1;
  --lh-snug: 1.25;
  --lh-normal: 1.5;
  --lh-relaxed: 1.7;

  --ls-tight: -0.02em;
  --ls-normal: 0;
  --ls-wide: 0.04em;
  --ls-label: 0.08em;

  /* Spacing & layout */
  --space-0: 0;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-8: 3rem;
  --space-10: 4rem;
  --space-12: 5rem;
  --space-16: 8rem;

  --container-sm: 640px;
  --container-md: 860px;
  --container-lg: 1120px;
  --container-xl: 1280px;
  --gutter: 1.5rem;
  --section-y: 5rem;

  /* Effects */
  --radius-xs: 6px;
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 22px;
  --radius-xl: 30px;
  --radius-2xl: 40px;
  --radius-pill: 999px;
  --radius-card: var(--radius-lg);

  --shadow-xs: 0 1px 2px rgba(31, 44, 70, 0.06);
  --shadow-sm: 0 2px 8px rgba(31, 44, 70, 0.07);
  --shadow-md: 0 8px 24px rgba(31, 44, 70, 0.09);
  --shadow-lg: 0 16px 40px rgba(31, 44, 70, 0.12);
  --shadow-xl: 0 28px 60px rgba(31, 44, 70, 0.16);
  --shadow-sky: 0 10px 28px rgba(78, 151, 206, 0.28);
  --shadow-blush: 0 10px 28px rgba(230, 135, 148, 0.28);
  --shadow-inset: inset 0 1px 3px rgba(31, 44, 70, 0.07);

  --ease-soft: cubic-bezier(0.33, 0.68, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur-fast: 0.15s;
  --dur-base: 0.25s;
  --dur-slow: 0.4s;

  --ring-focus: 0 0 0 4px rgba(111, 173, 218, 0.45);
}

/* ===========================================================
   Base / reset
   =========================================================== */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--cream-100); }
body {
  font-family: var(--font-body);
  color: var(--text-body);
  background: var(--cream-100);
  overflow-x: hidden;
}
h1, h2, h3, p, form { margin: 0; }
img, video { max-width: 100%; }
a {
  color: var(--text-link);
  text-decoration: none;
  transition: color var(--dur-base) var(--ease-soft);
}
a:hover { color: var(--text-link-hover); }
::selection { background: var(--blush-200); color: var(--ink-900); }
button { font: inherit; }

/* Keyboard focus that's actually visible — inputs keep their focus-within
   ring from .field__control, this covers links, buttons and the skip link. */
:focus-visible {
  outline: 3px solid var(--sky-500);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: fixed;
  top: 12px; left: 12px;
  z-index: 100;
  background: var(--ink-900);
  color: var(--white);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  padding: 0.6rem 1.1rem;
  border-radius: var(--radius-pill);
  transform: translateY(-64px);
  transition: transform var(--dur-base) var(--ease-soft);
}
.skip-link:focus-visible { transform: translateY(0); color: var(--white); }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

@keyframes pawFloat {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-22px) rotate(6deg); }
}
@keyframes pawFloatB {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(18px) rotate(-5deg); }
}
@keyframes heroRise {
  0% { opacity: 0; transform: translateY(30px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* Reveal-on-scroll (toggled by js/main.js) */
[data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s var(--ease-soft), transform 0.7s var(--ease-spring);
  will-change: opacity, transform;
}
[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* ===========================================================
   Layout helpers
   =========================================================== */
.container { max-width: var(--container-lg); margin: 0 auto; padding: 0 clamp(1.25rem, 5vw, 3rem); }

/* ===========================================================
   Header
   =========================================================== */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 0.85rem clamp(1.25rem, 5vw, 3rem);
  background: rgba(251, 246, 238, 0.78);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border-soft);
}
.site-header__logo {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: 1.4rem;
  color: var(--ink-900);
  letter-spacing: var(--ls-tight);
}
.site-header__logo span { font-size: 1.5rem; }
.site-header__nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: clamp(0.5rem, 2vw, 1.75rem);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
}
.site-header__link { color: var(--ink-700); font-size: var(--text-base); padding-block: 0.25rem; /* lifts the 20px text to a ≥24px target */ }
.site-header__book {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--color-secondary);
  color: var(--white);
  padding: 0.55rem 1.25rem;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
  font-size: var(--text-base);
  transition: background var(--dur-base) var(--ease-soft), box-shadow var(--dur-base) var(--ease-soft), transform var(--dur-fast) var(--ease-spring);
}
.site-header__book:hover {
  background: var(--color-secondary-hover);
  color: var(--white);
  box-shadow: var(--shadow-blush);
  transform: translateY(-2px);
}

/* ===========================================================
   Hero
   =========================================================== */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  /* Bottom-aligned, not centered: the running dog's face lives in the upper-
     middle of the video for most of the loop, and a vertically-centered
     headline sat right on top of it. In the lower third the copy covers
     grass and legs instead, so the dog stays visible the whole loop. */
  align-items: flex-end;
  overflow: hidden;
}
.hero__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.hero__overlay {
  position: absolute;
  inset: 0;
  /* Two layers: the original diagonal wash, plus a bottom band under the
     copy block. Worst-frame measurement (all ten half-second offsets of the
     loop composited behind each text box) had the tagline at 2.73:1 and the
     word "happy" at 2.75:1 when the dog runs into sunlight — a scrim tuned
     against the poster frame alone lies. Re-measure across frames if either
     gradient or the copy position changes. */
  background:
    linear-gradient(to top, rgba(31, 44, 70, 0.82) 0%, rgba(31, 44, 70, 0.55) 26%, rgba(31, 44, 70, 0) 52%),
    linear-gradient(105deg, rgba(31, 44, 70, 0.72) 0%, rgba(31, 44, 70, 0.45) 45%, rgba(31, 44, 70, 0.12) 100%);
}
/* Drawn paw marks drifting in the hero — the blurred blobs, retired, spoke
   nobody's language; these speak the signature dog's. The float animation
   lives on the wrapper and the static rotation on the svg, so the keyframes'
   transform never overwrites the tilt. */
.hero__paw {
  position: absolute;
  pointer-events: none;
  color: var(--white);
}
.hero__paw svg { display: block; width: 100%; height: auto; }
.hero__paw--a {
  top: 15%; right: 9%;
  width: clamp(70px, 9vw, 120px);
  opacity: 0.3;
  animation: pawFloat 9s var(--ease-soft) infinite;
}
.hero__paw--a svg { transform: rotate(18deg); }
.hero__paw--b {
  bottom: 20%; left: 5%;
  width: clamp(54px, 7vw, 92px);
  opacity: 0.22;
  animation: pawFloatB 11s var(--ease-soft) infinite;
}
.hero__paw--b svg { transform: rotate(-14deg); }
.hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--container-lg);
  margin: 0 auto;
  padding: 7rem clamp(1.25rem, 5vw, 3rem) 4rem;
}
.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* Navy frost, not white: a white 0.16 wash over any backdrop caps this
     12px text's best-possible contrast at ~4.2:1 — it could never pass AA. */
  background: rgba(31, 44, 70, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(6px);
  color: var(--cream-100);
  padding: 0.4rem 1rem;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  white-space: nowrap;
  animation: heroRise 0.7s var(--ease-soft) both;
}
.hero__title {
  margin: 1.25rem 0 0;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--text-hero);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--white);
  max-width: 15ch;
}
/* Word-by-word entrance. The animation moved from the block to the words —
   each is static markup, so with no JS the headline is simply there. */
.hero__title .w {
  display: inline-block;
  animation: heroRise 0.7s var(--ease-soft) both;
}
.hero__title .w:nth-child(1) { animation-delay: 0.12s; }
.hero__title .w:nth-child(2) { animation-delay: 0.22s; }
.hero__title .w:nth-child(3) { animation-delay: 0.34s; }
.hero__title .w:nth-child(4) { animation-delay: 0.46s; }
.hero__tagline {
  margin: 1.5rem 0 0;
  font-size: clamp(1.05rem, 2vw, var(--text-lg));
  line-height: var(--lh-normal);
  color: var(--cream-100);
  max-width: 44ch;
  animation: heroRise 0.7s var(--ease-soft) 0.62s both;
}
.hero__actions {
  margin-top: 2.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  animation: heroRise 0.7s var(--ease-soft) 0.78s both;
}

/* ===========================================================
   Button (ported from the design system's Button component)
   =========================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.01em;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 2px solid transparent;
  transition: transform var(--dur-fast) var(--ease-spring),
              background var(--dur-base) var(--ease-soft),
              box-shadow var(--dur-base) var(--ease-soft);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: scale(0.96); }
.btn-lg { padding: 1rem 2rem; font-size: var(--text-lg); gap: 0.6rem; }
.btn-md { padding: 0.75rem 1.5rem; font-size: var(--text-base); gap: 0.5rem; }
.btn-block { display: flex; width: 100%; }

.btn-primary { background: var(--color-primary); color: var(--text-on-primary); box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--color-primary-hover); box-shadow: var(--shadow-sky); transform: translateY(-2px); color: var(--text-on-primary); }
.btn-primary:active { box-shadow: var(--shadow-sm); }

.btn-secondary { background: var(--color-secondary); color: var(--white); box-shadow: var(--shadow-sm); }
.btn-secondary:hover { background: var(--color-secondary-hover); box-shadow: var(--shadow-blush); transform: translateY(-2px); color: var(--white); }
.btn-secondary:active { box-shadow: var(--shadow-sm); }

/* ===========================================================
   Section headers
   =========================================================== */
.section { max-width: var(--container-lg); margin: 0 auto; padding: var(--section-y) clamp(1.25rem, 5vw, 3rem); }
.section-head { text-align: center; max-width: 42ch; margin: 0 auto var(--space-8); }
.section-head__eyebrow {
  display: inline-block;
  color: var(--sky-700); /* default; dark-band sections override inline */
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}
.section-head__title {
  margin: 0.6rem 0 0;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--text-display);
  line-height: var(--lh-snug);
  color: var(--ink-900);
}
.section-head__lead {
  margin: 0.9rem 0 0;
  font-size: var(--text-lg);
  line-height: var(--lh-normal);
  color: var(--text-body);
}

/* ===========================================================
   Services (ServiceCard)
   =========================================================== */
.services-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5);
}
.service-card {
  position: relative; /* anchors the CTA's stretched ::after */
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--surface-card);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border-soft);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-slow) var(--ease-spring), box-shadow var(--dur-base) var(--ease-soft);
  font-family: var(--font-body);
}
.service-card:hover { box-shadow: var(--shadow-lg); transform: translateY(-6px); }
.service-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px; height: 64px;
  border-radius: var(--radius-lg);
  font-size: 1.9rem;
  /* Gentle idle bob — slow enough to read as breathing, not motion. The
     negative nth-child delays below start each card mid-cycle so the four
     icons never move in unison. */
  animation: icon-float 5.5s ease-in-out infinite;
}
.services-grid > div:nth-child(2) .service-card__icon { animation-delay: -1.4s; }
.services-grid > div:nth-child(3) .service-card__icon { animation-delay: -2.8s; }
.services-grid > div:nth-child(4) .service-card__icon { animation-delay: -4.2s; }
/* A quick happy shake on hover — like a wet dog. The keyframes end back at
   rest so the idle bob can take over without a visible jump. */
.service-card:hover .service-card__icon { animation: icon-wiggle 0.7s var(--ease-spring); }

@keyframes icon-float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-5px) rotate(3deg); }
}
@keyframes icon-wiggle {
  0%   { transform: rotate(0deg) scale(1); }
  30%  { transform: rotate(10deg) scale(1.12); }
  60%  { transform: rotate(-10deg) scale(1.08); }
  100% { transform: rotate(0deg) scale(1); }
}
.service-card--sky .service-card__icon { background: var(--sky-100); color: var(--sky-600); }
.service-card--blush .service-card__icon { background: var(--blush-100); color: var(--blush-500); }
.service-card--sage .service-card__icon { background: var(--sage-100); color: var(--sage-500); }
.service-card--sun .service-card__icon { background: var(--sun-100); color: #C79A2E; }
.service-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--text-h3);
  color: var(--text-heading);
}
.service-card__desc { margin: 0; font-size: var(--text-base); line-height: var(--lh-normal); color: var(--text-body); flex: 1; }
.service-card__price-row { display: flex; align-items: baseline; gap: 0.4rem; margin-top: var(--space-2); }
.service-card__price-note {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}
.service-card__price { font-family: var(--font-display); font-weight: var(--fw-bold); font-size: var(--text-h2); color: var(--text-heading); }
.service-card__cta {
  margin-top: var(--space-2);
  align-self: flex-start;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--text-sm);
  color: var(--sky-700); /* sky-600 was 4.41:1 on white — just under AA */
  text-decoration: none; /* it's an <a href="#book"> now, not a <button> */
  background: none;
  border: none;
  padding: 0;
  min-height: 24px; /* WCAG 2.5.8 floor for the visible link itself */
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  transition: color var(--dur-base) var(--ease-soft);
}
/* Stretched link: the whole card is the tap target (the CTA text alone was
   76×19px). The card contains no other interactive elements, so nothing is
   occluded; the card's own position: relative scopes the overlay. */
.service-card__cta::after { content: ""; position: absolute; inset: 0; }
.service-card__cta span { display: inline-block; transition: transform var(--dur-base) var(--ease-spring); }
.service-card:hover .service-card__cta { color: var(--sky-700); }
.service-card:hover .service-card__cta span { transform: translateX(4px); }

/* ===========================================================
   Gallery
   =========================================================== */
/* No background band: the warm page cream runs straight through, so the
   gallery reads as part of the page like every other content section.
   (It used to carry a --sky-50 band — removed by request, Aug 2026.) */
.gallery-section { padding: var(--section-y) 0; }
.gallery-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  grid-auto-rows: 1fr;
  gap: var(--space-4);
}
.gallery-tile { border-radius: var(--radius-xl); overflow: hidden; box-shadow: var(--shadow-md); min-height: 160px; }
.gallery-tile--main { grid-row: span 2; min-height: 340px; }
.gallery-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ===========================================================
   Bento — the nine service photos in "Happy tails"

   Presentation lives here, once, and is shared by two renderers: the static
   markup in index.html (the no-JS baseline) and the compiled React island in
   js/bento.js. Only the *motion* differs — the :has() rules below give the
   row/column expansion with no JavaScript at all, and the island upgrades
   that to real framer-motion spring physics when it mounts.

   The three column widths always sum to 3fr (likewise the rows), so the grid
   never changes overall size — only the split between tracks moves.
   =========================================================== */
.bento {
  --bento-grow: 1.62fr;   /* hovered track */
  --bento-shrink: 0.69fr; /* the other two */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: clamp(0.6rem, 1.1vw, 1rem);
  height: clamp(460px, 58vw, 740px);
  transition: grid-template-columns 0.55s var(--ease-spring),
              grid-template-rows 0.55s var(--ease-spring);
}
.bento__cell {
  position: relative;
  margin: 0;
  min-width: 0;  /* let the tracks shrink below the image's intrinsic size */
  min-height: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  transition: box-shadow var(--dur-slow) var(--ease-soft);
}
.bento__cell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.55s var(--ease-soft);
}
/* Keeps the caption readable over whatever part of the photo is in frame. */
.bento__cell::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Composited measurement against the actual webp pixels had the breed
     labels bottoming out at 2.27:1 and three service names under 4.5 with
     the old stops (0.82 / 0.34@38% / 0@68%). The caption band needs to stay
     near-opaque up to where the labels sit. */
  background: linear-gradient(to top,
    rgba(31, 44, 70, 0.92) 0%,
    rgba(31, 44, 70, 0.80) 30%,
    rgba(31, 44, 70, 0.28) 58%,
    rgba(31, 44, 70, 0) 76%);
}
.bento__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: clamp(0.65rem, 1.3vw, 1.05rem);
}
.bento__idx {
  font-family: var(--font-display);
  font-size: 0.8125rem; /* was 0.68rem = 10.9px, below the 13px legibility floor */
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sky-200);
}
.bento__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(0.9rem, 1.35vw, 1.15rem);
  line-height: 1.2;
  color: var(--cream-50);
}

/* Gated on a real hovering pointer so touch devices never get a stuck
   :hover, and on no-preference so reduced-motion visitors keep the even 3x3.
   Gating by media query rather than by a later override rule avoids a
   specificity fight with the :has() selectors. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .bento:has(.bento__cell[data-col="0"]:hover) { grid-template-columns: var(--bento-grow) var(--bento-shrink) var(--bento-shrink); }
  .bento:has(.bento__cell[data-col="1"]:hover) { grid-template-columns: var(--bento-shrink) var(--bento-grow) var(--bento-shrink); }
  .bento:has(.bento__cell[data-col="2"]:hover) { grid-template-columns: var(--bento-shrink) var(--bento-shrink) var(--bento-grow); }
  .bento:has(.bento__cell[data-row="0"]:hover) { grid-template-rows: var(--bento-grow) var(--bento-shrink) var(--bento-shrink); }
  .bento:has(.bento__cell[data-row="1"]:hover) { grid-template-rows: var(--bento-shrink) var(--bento-grow) var(--bento-shrink); }
  .bento:has(.bento__cell[data-row="2"]:hover) { grid-template-rows: var(--bento-shrink) var(--bento-shrink) var(--bento-grow); }
  .bento__cell:hover img { transform: scale(1.05); }
  .bento__cell:hover { box-shadow: var(--shadow-lg); }
}

/* ===========================================================
   Why choose us
   =========================================================== */
.why-section { max-width: var(--container-md); margin: 0 auto; padding: var(--section-y) clamp(1.25rem, 5vw, 3rem); text-align: center; }
.why-section__icon { font-size: 2.4rem; }
.why-section__title {
  margin: 0.8rem 0 0;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--text-display);
  line-height: var(--lh-snug);
  color: var(--ink-900);
}
.why-section__lead { margin: 1.25rem 0 0; font-size: var(--text-lg); line-height: var(--lh-relaxed); color: var(--text-body); }
.stats-grid {
  margin-top: var(--space-8);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-5);
}
.stat__value { font-family: var(--font-display); font-weight: var(--fw-bold); font-size: var(--text-h1); }
.stat__label { margin: 0.35rem 0 0; color: var(--text-muted); font-size: var(--text-sm); }

/* ===========================================================
   Booking / contact
   =========================================================== */
.book-section { max-width: var(--container-lg); margin: 0 auto var(--section-y); padding: 0 clamp(1.25rem, 5vw, 3rem); }
.book-card {
  background: var(--white);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.book-form-panel { padding: clamp(1.75rem, 4vw, 3rem); }
.book-form-panel__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--text-h1);
  line-height: var(--lh-snug);
  color: var(--ink-900);
}
.book-form-panel__lead { margin: 0.7rem 0 var(--space-6); font-size: var(--text-base); line-height: var(--lh-normal); color: var(--text-body); }

.booking-form { display: flex; flex-direction: column; gap: var(--space-4); }

.field { display: flex; flex-direction: column; gap: 0.4rem; font-family: var(--font-body); }
.field__label { font-size: var(--text-sm); font-weight: var(--fw-bold); color: var(--text-heading); }
.field__control {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--white);
  border: 2px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 0 0.9rem;
  box-shadow: var(--shadow-inset);
  transition: border-color var(--dur-base) var(--ease-soft), box-shadow var(--dur-base) var(--ease-soft);
}
.field__control:focus-within { border-color: var(--sky-400); box-shadow: var(--ring-focus); }
.field__control input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  padding: 0.7rem 0;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--text-body);
}
.field textarea {
  resize: vertical;
  background: var(--white);
  border: 2px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 0.7rem 0.9rem;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--text-body);
  line-height: var(--lh-normal);
  outline: none;
  box-shadow: var(--shadow-inset);
  transition: border-color var(--dur-base) var(--ease-soft), box-shadow var(--dur-base) var(--ease-soft);
}
.field textarea:focus { border-color: var(--sky-400); box-shadow: var(--ring-focus); }

.book-success {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
  background: var(--sky-50);
  border: 1px solid var(--sky-200);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}
.book-success__icon { font-size: 2rem; }
.book-success__title { margin: 0; font-family: var(--font-display); font-weight: var(--fw-semibold); font-size: var(--text-h3); color: var(--ink-900); }
.book-success__lead { margin: 0; color: var(--text-body); font-size: var(--text-base); line-height: var(--lh-normal); }

.book-details-panel {
  position: relative;
  padding: clamp(1.75rem, 4vw, 3rem);
  background: var(--ink-900);
  color: var(--cream-100);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-6);
}
.book-details-panel a { display: inline-block; padding-block: 0.25rem; /* ≥24px target; no negative margin — stacked links' tap boxes must not overlap */ }

.book-details-panel__label {
  display: block;
  color: var(--sky-300);
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}
.book-details-panel__address {
  margin: 0.5rem 0 0;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--text-h3);
  line-height: var(--lh-snug);
  color: var(--white);
}
.hours-list { margin-top: 0.6rem; display: flex; flex-direction: column; gap: 0.4rem; font-size: var(--text-base); color: var(--cream-100); }
.hours-list__row { display: flex; justify-content: space-between; gap: 1rem; }
.hours-list__row strong { color: var(--white); font-weight: var(--fw-bold); }
.hours-list__row--closed span:last-child { color: var(--ink-300); }
.contact-lines { margin: 0.5rem 0 0; font-size: var(--text-base); }
.contact-lines a { color: var(--white); }

/* ===========================================================
   Footer
   =========================================================== */
.site-footer { background: var(--ink-900); color: var(--cream-100); padding: var(--space-8) clamp(1.25rem, 5vw, 3rem); }
.site-footer__row {
  max-width: var(--container-lg);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
}
.site-footer__logo {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: 1.35rem;
  color: var(--white);
}
.site-footer__nav { display: flex; align-items: center; gap: 1.5rem; font-family: var(--font-display); font-weight: var(--fw-semibold); }
.site-footer__nav a { color: var(--sky-300); padding-block: 0.25rem; /* ≥24px target */ }
.site-footer__copy { max-width: var(--container-lg); margin: var(--space-6) auto 0; color: var(--ink-400); font-size: var(--text-sm); }

/* Explicit override: our flex/grid display rules share specificity with the
   UA [hidden] rule, and author styles win ties — so [hidden] needs its own say. */
[hidden] { display: none !important; }

/* ===========================================================
   Slice cursor (signature interaction)

   Replaced the earlier dot-and-ring cursor. The canvas and dot are created by
   the React component in lightswind/src/components/lightswind-pro/slice-cursor.jsx
   (the real Lightswind Pro component — Tailwind stripped, so these rules are
   its styling) and only ever exist on capable, fine-pointer, motion-OK
   devices — see the gate in lightswind/src/gate.js. Nothing here applies
   unless the component itself adds .slice-cursor-active to <html>.
   =========================================================== */
.slice-cursor {
  position: fixed;
  inset: 0;
  z-index: 999;
  pointer-events: none;
}

/* The core dot rides the raw pointer position (set inline per frame by the
   component); the difference blend keeps it visible on any background. Its
   width/height come from the component's dotSize prop. */
.slice-cursor-dot {
  position: fixed;
  z-index: 1000;
  pointer-events: none;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference;
}

/* Hide the native pointer only once the slice is actually tracking it, so a
   failed or slow island never leaves the page with no cursor at all. */
html.slice-cursor-revealed,
html.slice-cursor-revealed a,
html.slice-cursor-revealed button,
html.slice-cursor-revealed [role="button"] {
  cursor: none;
}

/* Text fields keep the native I-beam: the slice shows where the pointer is,
   but only a caret shows where the *text* will land. */
html.slice-cursor-revealed input,
html.slice-cursor-revealed textarea {
  cursor: text;
}

/* Fade in on -active, not -revealed: the Pro component plays an intro
   animation from the moment it mounts, before the mouse ever moves. The
   native cursor stays visible until -revealed regardless. */
.slice-cursor { opacity: 0; transition: opacity var(--dur-base) var(--ease-soft); }
.slice-cursor-dot {
  opacity: 0;
  /* transform is animated too: the component scales the dot up over
     interactive elements (hoverScale), and this is what eases it. */
  transition: opacity var(--dur-base) var(--ease-soft), transform 0.3s var(--ease-soft);
}
html.slice-cursor-active .slice-cursor,
html.slice-cursor-active .slice-cursor-dot { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  /* Belt-and-suspenders: the gate already refuses to load the island when
     this is set, but if it somehow mounted, force the native cursor back. */
  .slice-cursor, .slice-cursor-dot { display: none !important; }
  html.slice-cursor-active, html.slice-cursor-active * { cursor: auto !important; }
}

/* ===========================================================
   Responsive — mobile-first breakpoints
   =========================================================== */
@media (max-width: 860px) {
  .services-grid { grid-template-columns: 1fr; }
  .gallery-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .gallery-tile--main { grid-row: auto; min-height: 260px; }
  .book-card { grid-template-columns: 1fr; }
  /* No hovering pointer down here, so the 3x3 becomes a plain 2-up grid of
     fixed-height tiles rather than a squeezed square. */
  .bento {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
    grid-auto-rows: minmax(160px, 1fr);
    height: auto;
  }
}

@media (max-width: 560px) {
  .site-header__nav { gap: 0.75rem; }
  .hero__content { padding-top: 6.5rem; }
  .stats-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .bento { grid-template-columns: 1fr; grid-auto-rows: minmax(200px, 1fr); }
}

/* ===========================================================
   Level 4 — 3D bone hero
   Added when the hero gained a WebGL island. Kept as an
   append-only block so the original hero rules above are
   untouched and this can be lifted out wholesale if ever
   unwanted (delete this block + the two script tags).
   =========================================================== */

.hero__content {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: 2rem;
}

.hero__copy {
  /* The copy column owns the stacking context so the canvas can never
     sit above the CTAs no matter what z-index three.js sets. */
  position: relative;
  z-index: 2;
}

.hero__figure {
  position: relative;
  /* Hidden below 960px. The hero already carries a full-bleed video; a WebGL
     canvas on top of that on a mid-range Android is exactly how a 90
     Lighthouse score becomes a 60. This is the same judgement as not shipping
     a 4K hero image to a 390px screen. */
  display: none;
  /* A DEFINITE height, not min-height. React Three Fiber's wrapper is
     height:100%, and 100% of an auto-height parent resolves to auto — which
     silently collapses the canvas to its 150px HTML default. Cost me a
     confusing screenshot. */
  height: clamp(380px, 54vh, 580px);
  /* Never intercept a tap meant for a CTA. */
  pointer-events: none;
}

@media (min-width: 960px) {
  .hero__content {
    /* Copy keeps the wider column — the bone is the garnish, not the meal. */
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
    gap: clamp(1.5rem, 4vw, 3.5rem);
  }
  .hero__figure {
    display: block;
  }
}

/* The canvas fades in only once the GLB has actually parsed and the first
   frame is on screen, so you never see a hard pop-in of a half-lit bone. */
.hero__figure canvas {
  opacity: 0;
  transition: opacity 700ms var(--ease-soft);
}
.hero__figure[data-hero3d-ready="true"] canvas {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hero__figure canvas {
    transition: none;
  }
}

/* Perf readout badge — only ever visible with ?perf=1 in the URL. */
.hero-perf {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 9999;
  background: rgba(31, 44, 70, 0.92);
  color: var(--cream-100);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-md, 12px);
  padding: 0.75rem 0.9rem;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre;
  pointer-events: none;
}

/* ===========================================================
   Level 5 — draw-on-scroll signature
   The artwork is a single closed path with pathLength="1", so
   the dash maths below is length-independent: edit the drawing
   and nothing here needs re-tuning.
   =========================================================== */

.signature {
  display: flex;
  justify-content: center;
  color: var(--sky-600);
  margin-bottom: 0.25rem;
}

.signature__svg {
  width: clamp(104px, 15vw, 176px);
  height: auto;
  overflow: visible;          /* stroke-linecap can sit a hair outside the box */
}

.signature__svg path {
  /* Overrides the stroke-width attribute in the markup. At ~130px rendered,
     3.4 units in a 240-wide viewBox lands under 2 real pixels and the mark
     goes weedy — it needs more weight to hold its own next to a 3rem heading. */
  stroke-width: 4.6;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

.signature.is-drawn .signature__svg path {
  stroke-dashoffset: 0;
  /* 1.5s is the sweet spot. Under ~1s it reads as a flicker rather than an
     act of drawing; over ~2s the visitor has started scrolling again and
     never sees it land. The ease is the site's own --ease-spring curve
     without the overshoot — a pen accelerates away and settles. */
  transition: stroke-dashoffset 1500ms cubic-bezier(0.32, 0.72, 0, 1);
}

@media (prefers-reduced-motion: reduce) {
  .signature__svg path { stroke-dashoffset: 0; }
  .signature.is-drawn .signature__svg path { transition: none; }
}

/* ---------- First-visit intro overlay (optional, see js/intro.js) ---------- */

.intro {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: grid;
  place-items: center;
  background: var(--cream-100);
  color: var(--sky-600);
  /* No transition on the way IN — the overlay is injected already-visible so
     there is never a frame where the hero flashes before it covers. */
  transition: opacity 520ms var(--ease-soft), visibility 0s linear 520ms;
}

.intro.is-leaving {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.intro__mark {
  width: clamp(140px, 22vw, 260px);
  height: auto;
  overflow: visible;
}

.intro__mark path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1100ms cubic-bezier(0.32, 0.72, 0, 1);
}

.intro.is-drawing .intro__mark path { stroke-dashoffset: 0; }

.intro__word {
  margin-top: 1rem;
  font-family: var(--font-display, inherit);
  font-size: var(--text-lg);
  letter-spacing: 0.02em;
  color: var(--ink-700);
  opacity: 0;
  transition: opacity 420ms var(--ease-soft) 620ms;
  text-align: center;
}

.intro.is-drawing .intro__word { opacity: 1; }

/* ===========================================================
   Level 5 silver — the one-flourish experiment
   Three sections, three treatments. See 3d/FLOURISH.md.
   =========================================================== */

/* Shared: a canvas scoped to its section, never to the viewport.
   The Weblove particle templates ship as position:fixed;inset:0 with
   overflow:hidden on <body> — pasted in raw that covers the page and
   kills scrolling. This is the re-scoped version. */
.flourish-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;   /* the booking form must stay usable through it */
  z-index: 0;
}

/* ---------- A · Lottie only — "How a groom works" ---------- */

.how-section {
  max-width: var(--container-lg, 1120px);
  margin: 0 auto;
  padding: var(--section-y) clamp(1.25rem, 5vw, 3rem);
}

.how-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
  margin-top: clamp(2rem, 5vw, 3rem);
}

.how-step {
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg, 20px);
  padding: clamp(1.25rem, 3vw, 2rem);
  text-align: center;
  /* Spotlight card: a soft sky glow follows the pointer (main.js writes
     --mx/--my on pointermove), and the card lifts like the service cards.
     Without JS the glow just sits at its default position on hover —
     still a working effect, not a broken one. */
  position: relative;
  overflow: hidden;
  /* opacity 0.7s MUST stay in this list: .how-step carries [data-reveal]
     itself (not on a wrapper like the service cards), and this transition
     replaces the [data-reveal] one in the cascade — dropping opacity here
     would turn the scroll-reveal fade into a snap. */
  transition: opacity 0.7s var(--ease-soft),
              transform var(--dur-slow) var(--ease-spring),
              box-shadow var(--dur-base) var(--ease-soft),
              border-color var(--dur-base) var(--ease-soft);
}
.how-step::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 30%),
              rgba(78, 151, 206, 0.16), transparent 65%);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-soft);
  pointer-events: none;
}
.how-step:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); border-color: var(--sky-200); }
.how-step:hover::before { opacity: 1; }

.how-step__mark {
  /* 84px was too small: at that size an animated mark is indistinguishable
     from a static icon, so the Lottie paid 46 KB for nothing. A flourish
     needs enough area for the eye to register that it MOVED. */
  width: 112px;
  height: 112px;
  margin: 0 auto 0.5rem;
  /* Same gentle idle bob as the service icons (staggered below), and the
     same happy shake when the card is hovered. The bob rides the Lottie
     container too — a slow drift under a drawing loop reads as one motion,
     unlike three competing Lotties would. */
  animation: icon-float 6s ease-in-out infinite;
}
.how-grid > .how-step:nth-child(2) .how-step__mark { animation-delay: -2s; }
.how-grid > .how-step:nth-child(3) .how-step__mark { animation-delay: -4s; }
.how-step:hover .how-step__mark { animation: icon-wiggle 0.7s var(--ease-spring); }

/* The other two steps keep emoji on purpose. Three Lotties in one row would
   be three competing loops in one viewport — the exact thing this quest is
   testing. One animated mark leads the eye; three fight for it. */
.how-step__mark--plain {
  display: grid;
  place-items: center;
  font-size: 2.6rem;
}

.how-step__title {
  font-family: var(--font-display, inherit);
  font-size: var(--text-lg);
  color: var(--text-heading);
  margin: 0 0 0.5rem;
}

.how-step__desc {
  margin: 0;
  color: var(--text-body);
  font-size: var(--text-sm);
  line-height: var(--lh-relaxed);
}

/* ---------- C · Lottie + particles — "Happy tails" ---------- */

.tails-section {
  position: relative;               /* the canvas anchors to this */
  overflow: hidden;
  background: var(--surface-sunken);
  padding: var(--section-y) clamp(1.25rem, 5vw, 3rem);
}

.tails-inner {
  position: relative;
  z-index: 1;                       /* copy sits above the bubbles */
  max-width: var(--container-lg, 1120px);
  margin: 0 auto;
}

.tails-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
  margin-top: clamp(2rem, 5vw, 3rem);
}

.tails-card {
  margin: 0;
  background: var(--surface-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg, 20px);
  padding: clamp(1.25rem, 3vw, 1.75rem);
}

.tails-card blockquote {
  margin: 0 0 1rem;
  font-size: var(--text-base);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.tails-card figcaption {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.tails-card__mark {
  width: 84px;
  height: 84px;
  margin: 0 0 0.5rem;
}

/* ---------- B · particles only — booking ---------- */

.book-section {
  position: relative;
  overflow: hidden;
}

.book-section > *:not(.flourish-canvas) {
  position: relative;
  z-index: 1;
}

/* ---------- reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .flourish-canvas { display: none; }
  .service-card__icon, .how-step__mark { animation: none !important; }
  .how-step::before { display: none; }
  .how-step:hover { transform: none; }
}

@media (max-width: 860px) {
  .how-grid,
  .tails-grid { grid-template-columns: 1fr; }
  .flourish-canvas { display: none; }   /* gated in JS too — belt and braces */
}

/* ===========================================================
   Awwwards pass (Aug 2026) — appended as one block, like the
   Level 4/5 blocks above, so it can be reviewed or lifted out
   wholesale. Five ideas:
     1. one drawn language — every mark on the page is now a
        single-line drawing in the signature dog's style
     2. the walking trail — paw prints stamp down the page
        toward the booking form
     3. a dark mid-page moment — the testimonials band
     4. count-up stats
     5. the gallery image-trail island host
   =========================================================== */

/* ---------- 1 · the drawn-icon system ---------- */

.logo-paw { display: block; flex: none; }

.icon-line { width: 34px; height: 34px; display: block; }
.icon-line--lg { width: 56px; height: 56px; }

/* Same pathLength="1" trick as the signature dog, generalised: every shape in
   a [data-draw] svg draws itself when signature.js adds .is-drawn, each shape
   starting a beat after the one before, so multi-part icons assemble like pen
   strokes rather than fading in as a block. */
[data-draw] path,
[data-draw] circle,
[data-draw] ellipse {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
[data-draw].is-drawn path,
[data-draw].is-drawn circle,
[data-draw].is-drawn ellipse {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 900ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-draw].is-drawn > *:nth-child(2) { transition-delay: 0.12s; }
[data-draw].is-drawn > *:nth-child(3) { transition-delay: 0.24s; }
[data-draw].is-drawn > *:nth-child(4) { transition-delay: 0.36s; }
[data-draw].is-drawn > *:nth-child(5) { transition-delay: 0.48s; }
[data-draw].is-drawn > *:nth-child(6) { transition-delay: 0.58s; }
[data-draw].is-drawn > *:nth-child(7) { transition-delay: 0.68s; }

@media (prefers-reduced-motion: reduce) {
  [data-draw] path,
  [data-draw] circle,
  [data-draw] ellipse { stroke-dashoffset: 0; }
  [data-draw].is-drawn path,
  [data-draw].is-drawn circle,
  [data-draw].is-drawn ellipse { transition: none; }
}

/* ---------- 2 · the walking trail ---------- */

/* Sections that host a trail cluster anchor it themselves. */
.section, .why-section { position: relative; }
.gallery-section .container { position: relative; }

.paw-trail {
  position: absolute;
  width: 96px;
  pointer-events: none;
  /* Negative z-index: prints show on the page cream but walk BEHIND the white
     cards — the dog passed this way before the content arrived. The anchor
     sections are position:relative WITHOUT z-index on purpose, so this
     doesn't trap the prints above their own background. */
  z-index: -1;
  color: var(--sky-300);
  opacity: 0.55;
  display: none;                 /* the whitespace it lives in only exists on desktop */
}
@media (min-width: 960px) { .paw-trail { display: block; } }
.paw-trail__svg { display: block; width: 100%; height: auto; overflow: visible; }

/* Zigzag down the page: right, left, right — toward the booking form. */
.paw-trail--services { top: 4px; right: 3%; }
.paw-trail--gallery  { top: -6px; left: 2%; }
/* The why cluster lives in the true page margin beside the narrow column, so
   it needs room that only exists on wide screens. */
.paw-trail--why { top: 10px; right: -84px; display: none; }
@media (min-width: 1200px) { .paw-trail--why { display: block; } }

/* Each print stamps in — quick spring press, like a paw landing. The inner
   .pt group carries the CSS transform so the outer group's translate/rotate
   attribute (its position on the trail) is never overwritten. */
.paw-trail .pt {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}
.paw-trail.is-stamped .pt { animation: pawStamp 0.5s var(--ease-spring) both; }
.paw-trail.is-stamped g:nth-of-type(2) .pt { animation-delay: 0.22s; }
.paw-trail.is-stamped g:nth-of-type(3) .pt { animation-delay: 0.44s; }
.paw-trail.is-stamped g:nth-of-type(4) .pt { animation-delay: 0.66s; }

@keyframes pawStamp {
  0%   { opacity: 0; transform: scale(0.45); }
  62%  { opacity: 1; transform: scale(1.14); }
  100% { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .paw-trail .pt { opacity: 1; animation: none !important; }
}

/* ---------- 3 · the dark moment — testimonials ---------- */

.tails-section { background: var(--ink-900); }
.tails-section .section-head__title { color: var(--white); }
.tails-card {
  background: var(--ink-800);
  border-color: rgba(255, 255, 255, 0.08);
}
.tails-card blockquote { color: var(--cream-100); }
.tails-card figcaption { color: var(--ink-400); }

/* ---------- 4 · count-up stats ---------- */
/* No styles needed — main.js animates textContent in place. */

/* ---------- 5 · gallery image-trail island ---------- */

.gallery-head { position: relative; }

/* The island appends this overlay into .gallery-head; images inside it chase
   the pointer. pointer-events: none throughout — it's spectacle, not UI. */
.trail-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  overflow: visible;
}
.trail-img {
  position: absolute;
  width: 124px;
  height: 148px;
  object-fit: cover;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(var(--s, 0));
  opacity: 0;
  transition: transform 0.5s var(--ease-spring), opacity 0.3s var(--ease-soft);
  will-change: transform, opacity;
}
.trail-img[data-status="active"] {
  --s: 1;
  opacity: 1;
}

/* ---------- booking form failure state ---------- */

.book-error {
  background: var(--color-danger-soft);
  border: 1px solid var(--blush-300);
  border-radius: var(--radius-md);
  color: var(--ink-800);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  padding: 0.85rem 1rem;
  margin-bottom: var(--space-4);
}
.book-error a { font-weight: var(--fw-bold); }
