/* ============================================================
   Positive Animal Wellbeing — styles.css
   Brand: Woofington warmth + Designed by Women editorial layout
   ============================================================ */

/* ============ FONTS — self-hosted ============
   Was a render-blocking <link> to fonts.googleapis.com on all twelve pages.
   That request alone cost 791ms of blocking time, and because the homepage's
   LCP element is the hero H1 — text, not an image — the largest paint could
   not happen until a third-party font had been fetched from a second origin.
   Two round trips (CSS, then the font it names) in front of the headline.

   Both families are VARIABLE fonts, so one file covers every weight the site
   uses: League Spartan 400/500/600/700 and Inter 400/500/600. Four files
   total, and the unicode-range gating means a normal page pulls only the two
   'latin' ones (~71KB); latin-ext is fetched only if a character needs it.

   font-display: swap keeps text visible in the fallback during the swap
   period rather than blocking on the webfont. The fallbacks in --font-head
   and --font-body below are what render in that window.

   Files came from Google's own CDN (fonts.gstatic.com) via the same CSS the
   <link> used to request, so these are byte-identical to what was being
   served before — self-hosting changes where they load from, not what loads.
   Licence: both are SIL Open Font License 1.1, which permits self-hosting.
   To refresh, re-request the css2 URL with a modern browser UA and pull the
   woff2 files it names. */

@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('assets/fonts/league-spartan-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: 'League Spartan';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('assets/fonts/league-spartan-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('assets/fonts/inter-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: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Brand palette — Christine's greens, taken from her logo (#106333 forest,
   #9FCE72 sage) rather than picked by eye. Approved site-wide 2026-08-04.

   Rule that governs it: FOREST ON LIGHT, SAGE ON DARK. Forest is 6.92:1 on
   cream but only 2.46:1 on ink; sage is the reverse (1.71:1 on cream,
   9.95:1 on ink). Never swap them.

   --accent (amber) is deliberately UNCHANGED. Green is the brand colour,
   amber is the functional "look here" colour — which is why the joint rings
   and the diagram markers needed no rework when the palette changed. */
:root {
  --clay: #106333;           /* 6.92:1 on paper — up from 4.24:1 */
  --clay-rgb: 16, 99, 51;
  --sand: #2D7840;           /* 5.10:1 — --sand carries small text in 10 places
                                and was a failing 2.83:1, so it CANNOT take the
                                logo's sage (1.71:1). Fixes a pre-existing
                                accessibility problem rather than adding one. */
  --leaf: #9FCE72;           /* sage — dark backgrounds and decoration only */
  --cream: #E6DDB5;
  --ink: #161616;
  --paper: #FBF8EE;
  --paper-2: #F2ECD6;
  --accent: #E08A3C;         /* DECORATIVE ONLY — 2.51:1 on paper. Fine for
                                markers, rules and glyphs; never for text. */
  --accent-rgb: 224, 138, 60;
  --accent-text: #9A5B21;    /* the same amber, darkened until it passes AA as
                                text: 5.08:1 on paper, 4.56:1 on paper-2. The
                                band matters — .link-arrow sits on paper-2 on
                                five pages, so that is the binding constraint. */
  --muted: #6B6553;          /* body copy stays neutral — long text set in
                                green reads heavy, and this already passes */

  /* ---------- Fluid type scale ----------
     One ramp, replacing 16 per-component clamps that had drifted into
     near-duplicates (1.9 vs 1.95rem; three variants of ~1.2/2.2vw/1.5rem).
     h1 and h2 are also brought down — 7rem was shouting. */
  --fs-display: clamp(2.5rem, 6vw, 5.25rem);    /* h1 */
  --fs-xxl:     clamp(2.2rem, 5vw, 3.75rem);    /* stat numbers */
  --fs-xl:      clamp(1.9rem, 4vw, 3.15rem);    /* h2 */
  --fs-lg:      clamp(1.4rem, 3vw, 2.3rem);     /* section leads, marquee */
  --fs-md:      clamp(1.28rem, 2.5vw, 1.9rem);  /* pull quotes, testimonials */
  --fs-sm:      clamp(1.15rem, 2.1vw, 1.55rem); /* h3, small display */
  --fs-body-lg: clamp(1.05rem, 1.8vw, 1.28rem); /* hero sub, page sub */
  --fs-body-sm: clamp(0.98rem, 1.5vw, 1.12rem); /* long-form testimonial */

  /* ---------- Fluid spacing scale ----------
     Same story as the type: 31 per-component clamps that had drifted into
     near-duplicates — 3rem vs 3.5rem mins on an identical ramp, four card
     paddings within 0.6rem of each other. One ramp, six steps. */
  --sp-xs:  clamp(1.5rem, 3vw, 2.4rem);     /* tight card padding, small rhythm */
  --sp-s:   clamp(2rem, 4vw, 3.1rem);       /* card padding, standard rhythm */
  --sp-m:   clamp(2.4rem, 5.5vw, 4.5rem);   /* panels, grid gaps, section heads */
  --sp-l:   clamp(3.25rem, 7vw, 5.5rem);    /* large blocks */
  --sp-xl:  clamp(4rem, 9vw, 7rem);         /* standard section padding */
  --sp-2xl: clamp(5rem, 12vw, 9rem);        /* the biggest blocks */

  --font-head: 'League Spartan', Arial, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, sans-serif;

  --maxw: 1200px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --radius: 18px;
  --radius-pill: 110px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--ink);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
section { scroll-margin-top: 90px; }

/* ---------- Typography ---------- */
h1, h2, h3, h4 { font-family: var(--font-head); font-weight: 600; line-height: 1.02; letter-spacing: -0.01em; }
h1 { font-size: var(--fs-display); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-sm); letter-spacing: 0; }

.eyebrow {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 0.78rem;
  color: var(--sand);
  margin-bottom: 1.1rem;
}

.section-head { max-width: 760px; margin: 0 auto var(--sp-m); text-align: center; }
.section-note { color: var(--muted); margin-top: 1rem; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5em;
  font-family: var(--font-head); font-weight: 600; font-size: 1rem;
  padding: 0.95em 2em; border-radius: var(--radius-pill);
  border: 1.5px solid transparent; cursor: pointer;
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease), background 0.3s, color 0.3s;
}
.btn-primary { background: var(--ink); color: var(--paper); box-shadow: 0 6px 22px rgba(22,22,22,0.12); }
.btn-primary:hover { transform: translateY(-3px); box-shadow: 0 12px 34px rgba(22,22,22,0.22); }
.btn-ghost { background: transparent; border-color: rgba(22,22,22,0.25); color: var(--ink); }
.btn-ghost:hover { border-color: var(--ink); transform: translateY(-3px); }
.btn-soft { background: var(--paper); border-color: var(--clay); color: var(--clay); }
.btn-soft:hover { background: var(--clay); color: var(--paper); transform: translateY(-3px); }
.btn-block { width: 100%; }

.link-arrow {
  display: inline-flex; align-items: center; gap: 0.5em;
  font-family: var(--font-head); font-weight: 600; color: var(--accent-text);
  border-bottom: 1.5px solid transparent; padding-bottom: 2px;
  transition: gap 0.3s var(--ease), border-color 0.3s;
}
.link-arrow:hover { gap: 0.9em; border-color: var(--accent-text); }

/* ---------- Scroll progress ---------- */
#scroll-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 0;
  background: var(--accent); z-index: 10000; transition: width 0.1s linear;
}

/* ---------- Navbar ---------- */
#navbar { position: fixed; top: 14px; left: 0; right: 0; z-index: 1000; transition: top 0.4s var(--ease); }
.nav-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 0.6rem 1.5rem;
  display: flex; align-items: center; justify-content: space-between;
  border-radius: var(--radius-pill);
  transition: all 0.45s var(--ease);
}
/* width:auto so the logo derives its width from the height. A raster logo has
   a large intrinsic width, which as a flex item sets a huge basis, shrinks to
   the space available and then gets pinned there by max-width:100% - stretching
   it. The old SVG never hit this. flex:0 0 auto keeps the link from shrinking. */
.nav-logo { flex: 0 0 auto; }
.nav-logo img { height: 42px; width: auto; transition: height 0.4s var(--ease); }
.nav-links { display: flex; align-items: center; gap: 2rem; position: relative; }
.nav-highlight { position: absolute; top: 50%; left: 0; height: 38px; width: 0; transform: translateY(-50%); background: rgba(var(--clay-rgb),0.14); border-radius: var(--radius-pill); opacity: 0; transition: transform 0.35s var(--ease), width 0.35s var(--ease), opacity 0.25s; pointer-events: none; z-index: 0; }
.nav-links a { font-family: var(--font-head); font-weight: 500; font-size: 1rem; transition: color 0.25s; position: relative; z-index: 1; }
.nav-links a:not(.nav-cta):hover { color: var(--accent-text); }
.nav-cta {
  background: var(--ink); color: var(--paper); padding: 0.55em 1.4em; border-radius: var(--radius-pill);
  transition: transform 0.3s var(--ease), background 0.3s;
}
.nav-cta:hover { transform: translateY(-2px); background: var(--accent); color: var(--ink); }

#navbar.scrolled .nav-inner {
  max-width: 880px;
  background: rgba(251, 248, 238, 0.8);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border: 1px solid rgba(var(--clay-rgb), 0.18);
  box-shadow: 0 10px 40px rgba(22,22,22,0.08);
  padding: 0.5rem 0.7rem 0.5rem 1.5rem;
}
#navbar.scrolled .nav-logo img { height: 34px; }

.nav-toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 8px; }
.nav-toggle span { width: 26px; height: 2px; background: var(--ink); transition: 0.3s var(--ease); }

.mobile-menu {
  position: fixed; inset: 0; z-index: 999;
  background: var(--paper); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1.5rem;
  transform: translateY(-100%); transition: transform 0.5s var(--ease);
}
.mobile-menu.open { transform: translateY(0); }

/* Screen-reader-only text. Used to give links their full spoken context
   without an aria-label — an aria-label REPLACES the accessible name, and if
   it does not contain the visible text verbatim, a speech-input user saying
   what they can see cannot activate the control. That is the
   label-content-name-mismatch audit. Words in a hidden span append to the
   visible name instead of overwriting it. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.mobile-menu a { font-family: var(--font-head); font-size: 1.8rem; font-weight: 600; }
.mobile-menu .nav-cta { font-size: 1.3rem; }
.mobile-contact { margin-top: 1rem; color: var(--sand); font-family: var(--font-head); }

/* ---------- Hero ---------- */
.hero {
  position: relative; min-height: 100vh; display: flex; flex-direction: column;
  justify-content: center; padding: 8rem 1.5rem 4rem; overflow: hidden;
  background: radial-gradient(120% 120% at 80% 0%, var(--cream) 0%, var(--paper) 55%);
}
.hero-bg { position: absolute; inset: 0; pointer-events: none; }
.orb { position: absolute; border-radius: 50%; filter: blur(90px); opacity: 0.5; }
.orb-1 { width: 540px; height: 540px; background: var(--sand); top: -180px; right: -120px; animation: drift 18s ease-in-out infinite; }
.orb-2 { width: 460px; height: 460px; background: var(--accent); bottom: -200px; left: -120px; opacity: 0.22; animation: drift 22s ease-in-out infinite reverse; }
@keyframes drift { 0%,100% { transform: translate(0,0); } 50% { transform: translate(40px, 30px); } }
/* elegant drifting capsules (Shape Landing Hero pattern, 21st.dev) */
.shape { position: absolute; border-radius: 999px; opacity: 0; border: 1px solid rgba(255,255,255,0.35); box-shadow: 0 8px 32px rgba(var(--clay-rgb),0.12); animation: shapeIn 1.4s var(--ease) forwards, shapeFloat 14s ease-in-out infinite; }
.shape-1 { width: 320px; height: 88px; left: -36px; top: 22%; --r: -12deg; --o: 0.55; background: linear-gradient(90deg, rgba(156,149,118,0.55), transparent); animation-delay: 0.2s, 0.2s; }
.shape-2 { width: 230px; height: 66px; right: 5%; top: 15%; --r: 14deg; --o: 0.5; background: linear-gradient(90deg, rgba(224,138,60,0.45), transparent); animation-delay: 0.45s, 0.45s; }
.shape-3 { width: 170px; height: 52px; right: 24%; bottom: 16%; --r: -20deg; --o: 0.45; background: linear-gradient(90deg, rgba(230,221,181,0.65), transparent); animation-delay: 0.65s, 0.65s; }
@keyframes shapeIn { from { opacity: 0; } to { opacity: var(--o, 0.5); } }
@keyframes shapeFloat { 0%, 100% { transform: rotate(var(--r)) translateY(0); } 50% { transform: rotate(var(--r)) translateY(22px); } }
.grid-overlay {
  position: absolute; inset: 0;
  background-image: linear-gradient(rgba(var(--clay-rgb),0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(var(--clay-rgb),0.05) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(120% 80% at 50% 20%, #000 0%, transparent 75%);
  mask-image: radial-gradient(120% 80% at 50% 20%, #000 0%, transparent 75%);
}
.hero-content { position: relative; max-width: var(--maxw); margin: 0 auto; width: 100%; }
.hero-title { max-width: 16ch; margin-bottom: 1.6rem; }
.hero-sub { max-width: 52ch; font-size: var(--fs-body-lg); color: var(--muted); margin-bottom: 2.2rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 2.6rem; }
.hero-trust { list-style: none; display: flex; flex-wrap: wrap; gap: 0.6rem 1.6rem; font-family: var(--font-head); font-weight: 500; color: var(--clay); }
.hero-trust li { position: relative; padding-left: 1.3rem; }
.hero-trust li::before { content: "✦"; position: absolute; left: 0; color: var(--accent); }

.scroll-hint {
  position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 0.6rem;
  font-family: var(--font-head); font-size: 0.78rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--sand);
}
.scroll-arrow { width: 1px; height: 36px; background: var(--sand); position: relative; overflow: hidden; }
.scroll-arrow::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 50%; background: var(--accent); animation: scrolldn 1.8s ease-in-out infinite; }
@keyframes scrolldn { 0% { transform: translateY(-100%); } 100% { transform: translateY(200%); } }

/* ---------- Intro ---------- */
.intro { max-width: 1000px; margin: 0 auto; padding: var(--sp-2xl) 1.5rem 3rem; text-align: center; }
.intro-lead { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-lg); line-height: 1.3; letter-spacing: -0.01em; }
.intro-lead strong { color: var(--accent-text); font-weight: 600; }

/* ---------- Marquee ---------- */
.marquee { position: relative; overflow: hidden; padding: 1.6rem 0; border-top: 1px solid rgba(var(--clay-rgb),0.18); border-bottom: 1px solid rgba(var(--clay-rgb),0.18); margin: 2rem 0 0; -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }
.marquee-track { display: flex; align-items: center; gap: 1.6rem; width: max-content; animation: marquee 36s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-track span { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-lg); color: var(--clay); white-space: nowrap; }
.marquee-track span[aria-hidden] { color: var(--accent); }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ---------- Services ---------- */
.services { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-xl) 1.5rem; display: flex; flex-direction: column; gap: var(--sp-xl); }
.service { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-m); align-items: center; }
.service-body h2 { margin: 0.4rem 0 1.2rem; }
.service-body > p { color: var(--muted); margin-bottom: 1.4rem; }
.ticks { list-style: none; display: grid; gap: 0.7rem; margin-bottom: 1.6rem; }
.ticks li { position: relative; padding-left: 1.9rem; font-weight: 500; }
.ticks li::before { content: ""; position: absolute; left: 0; top: 0.5em; width: 11px; height: 11px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18); }
.pact { background: rgba(var(--clay-rgb),0.1); border-left: 3px solid var(--accent); padding: 1rem 1.2rem; border-radius: 0 12px 12px 0; font-size: 0.96rem; margin-bottom: 1.6rem; }
.muted-note { font-size: 0.92rem; color: var(--muted); margin-bottom: 1.6rem; }

.service-media .ph, .ph { border-radius: var(--radius); aspect-ratio: 4 / 3.2; position: relative; overflow: hidden; }
.ph::after {
  content: ""; position: absolute; inset: 0;
  background: url('assets/logo/paw-icon.webp') center / 38% no-repeat;
  opacity: 0.16;
}
.ph-clay { background: linear-gradient(140deg, var(--cream), var(--sand)); }
.ph-sand { background: linear-gradient(140deg, var(--paper-2), var(--clay)); }
.ph-laser { background: linear-gradient(140deg, #2a2722, var(--clay)); }
.ph-portrait { aspect-ratio: 3 / 3.6; background: linear-gradient(160deg, var(--cream), var(--sand)); }

/* real photo frames (replace placeholder panels) */
.media-frame { border-radius: var(--radius); aspect-ratio: 4 / 3.2; overflow: hidden; position: relative; }
.media-frame picture { display: block; width: 100%; height: 100%; }
.media-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-frame--portrait { aspect-ratio: 4 / 4.7; }

/* Dark laser service */
.service-dark { position: relative; background: var(--ink); color: var(--paper); border-radius: clamp(20px, 4vw, 36px); padding: var(--sp-m); overflow: hidden; }
.service-dark .eyebrow { color: var(--accent); }
.service-dark .service-body > p { color: rgba(251,248,238,0.7); }
.service-dark .ticks li::before { background: var(--accent); }
.service-dark .ph-laser::after { opacity: 0.22; }
.service-bg { position: absolute; inset: 0; pointer-events: none; }
.laser-streak { position: absolute; width: 140%; height: 2px; background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb),0.7), transparent); top: 30%; left: -20%; transform: rotate(-12deg); filter: blur(1px); }
.laser-streak.streak-2 { top: 68%; opacity: 0.5; }

/* layered laser image stack (Section With Mockup pattern, 21st.dev) */
.laser-stack { position: relative; }
.laser-stack .ph { aspect-ratio: 4 / 3.2; }
/* The offset panel behind the photo is NOT an image slot. It used to carry the
   .ph placeholder class, which gave it a flat fill and the logo watermark, so
   it read as an empty frame waiting for a picture — and once --clay went green
   it turned into a conspicuous green box.
   It is now the laser's light: amber, soft, falling away to nothing. Same
   depth the layered stack was always after, but it looks intended.
   If Christine ever sends a second laser photo, this is the slot for it. */
.laser-back {
  position: absolute; inset: 0;
  transform: translate(20px, calc(22px + var(--py, 0px))) rotate(2.5deg);
  border-radius: var(--radius);
  background:
    radial-gradient(120% 90% at 78% 18%, rgba(var(--accent-rgb), 0.55), transparent 62%),
    linear-gradient(150deg, rgba(var(--accent-rgb), 0.22), rgba(var(--accent-rgb), 0.03) 70%, transparent);
  opacity: 0.9;
}
.laser-front { position: relative; box-shadow: 0 30px 60px rgba(0,0,0,0.45); }
.laser-front::before { content: ""; position: absolute; inset: 0; border-radius: var(--radius); box-shadow: inset 0 0 0 1px rgba(224,138,60,0.45); pointer-events: none; z-index: 1; }

/* ---------- How it works ---------- */
.how { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-xl) 1.5rem; }
.steps { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; counter-reset: s; position: relative; }
.steps::before { content: ""; position: absolute; top: 57px; left: 12%; right: 12%; height: 2px; background: rgba(var(--clay-rgb),0.28); z-index: 0; }
.step { background: var(--paper-2); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: 1.8rem 1.5rem; transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease); position: relative; z-index: 1; }
.step:hover { transform: translateY(-6px); box-shadow: 0 18px 40px rgba(22,22,22,0.08); }
.step-num { width: 56px; height: 56px; border-radius: 50%; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.28); color: var(--accent-text); font-family: var(--font-head); font-weight: 700; font-size: 1.5rem; display: flex; align-items: center; justify-content: center; margin-bottom: 1.1rem; }
.step h3 { margin-bottom: 0.5rem; }
.step p { font-size: 0.95rem; color: var(--muted); }

/* ---------- About ---------- */
.about { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-xl) 1.5rem; display: grid; grid-template-columns: 0.85fr 1fr; gap: var(--sp-m); align-items: center; }
.about-body h2 { margin: 0.4rem 0 1.4rem; }
.about-body p { color: var(--muted); margin-bottom: 1.1rem; }
.pullquote { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-md); line-height: 1.25; color: var(--clay); border-top: 1px solid rgba(var(--clay-rgb),0.25); margin-top: 1.6rem; padding-top: 1.4rem; }
/* About collage (About 3 pattern, 21st.dev) */
.about-collage { position: relative; }
.about-collage .ph-portrait { width: 100%; }
/* main portrait photo */
.about-portrait { aspect-ratio: 3 / 3.6; border-radius: var(--radius); overflow: hidden; margin: 0; }
.about-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* inset sits BOTTOM-right (over the dog/carpet), badge TOP-left (over the cabinet)
   — both deliberately clear of Christine's face in the portrait */
.about-collage-sec { position: absolute; right: -16px; bottom: -16px; width: 40%; aspect-ratio: 1 / 1; border: 6px solid var(--paper); border-radius: var(--radius); overflow: hidden; margin: 0; }
.about-collage-sec img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Bottom-left, NOT top-left. In the current photo Christine is on the left of
   frame and Alfie on the right, with both faces in the upper two-thirds — a
   top-pinned badge lands squarely on her head. Bottom-left sits over her
   jumper. Re-check this if the photo is ever swapped again. */
.about-breakout { position: absolute; left: -16px; bottom: 18px; display: flex; align-items: center; gap: 0.7rem; max-width: 210px; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.2); border-radius: 14px; padding: 0.8rem 1rem; box-shadow: 0 18px 40px rgba(22,22,22,0.12); }
/* height:auto, not a matching 36px. The source is 187x220 (0.85), so forcing a
   square squashed the paw and tripped Lighthouse's image-aspect-ratio audit. */
.about-breakout img { width: 36px; height: auto; }
.about-breakout strong { display: block; font-family: var(--font-head); font-size: 0.95rem; color: var(--ink); }
.about-breakout span { font-size: 0.78rem; color: var(--muted); }

/* ---------- Stats ---------- */
.stats { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-l) 1.5rem; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; text-align: center; }
.stat-num { font-family: var(--font-head); font-weight: 700; font-size: var(--fs-xxl); color: var(--ink); display: block; line-height: 1; }
.stat-text { color: var(--accent-text); }
.stat-label { font-family: var(--font-head); font-weight: 500; color: var(--sand); margin-top: 0.6rem; display: block; }

/* ---------- Testimonials ---------- */
.testimonials { background: var(--paper-2); padding: var(--sp-xl) 1.5rem; }
.testi-viewport { max-width: var(--maxw); margin: 0 auto; overflow: hidden; padding: 4px 0 20px; }
/* NO gap. script.js pages the track with translateX(-index * 100%), which is a
   percentage of the track's own width — i.e. exactly one slide. Any flex gap
   makes the slide pitch (width + gap) larger than the step, so every slide
   after the first lands short by 24px, 48px, 72px… The gap was never visible
   anyway: .testi-viewport is overflow:hidden and shows one slide at a time.
   align-items:center — the track is as tall as its tallest slide, and Chance's
   183-word back review is ~110px taller than the rest. Centring splits that
   difference above and below the shorter cards. */
.testi-track { display: flex; align-items: center; transition: transform 0.6s var(--ease); }
.testi-card { flex: 0 0 100%; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: var(--sp-s); }
.testi-card blockquote { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-md); line-height: 1.32; letter-spacing: -0.01em; position: relative; }
.testi-card blockquote::before { content: "\201C"; font-size: 4rem; color: var(--accent); line-height: 0; vertical-align: -0.3em; margin-right: 0.1em; }
.testi-card figcaption { margin-top: 1.6rem; color: var(--sand); font-family: var(--font-head); display: flex; align-items: center; gap: 0.9rem; }
.testi-avatar { flex: 0 0 auto; width: 46px; height: 46px; border-radius: 50%; background: var(--clay); color: var(--paper); font-family: var(--font-head); font-weight: 600; font-size: 1.1rem; display: flex; align-items: center; justify-content: center; }
.testi-meta span { color: var(--ink); font-weight: 600; }
.testi-dots { display: flex; justify-content: center; gap: 0; margin-top: 1.4rem; }

/* The BUTTON is the 24x24 touch target; the visible dot is drawn inside it.
   It was a 9px square button, which is a genuinely hard thing to hit with a
   thumb and fails the WCAG 2.2 minimum. A transparent overlay does not fix
   it — the audit measures the element's own box, so the element has to grow.
   Dropping the flex gap to 0 keeps the row visually tight despite the bigger
   boxes: 24px of button leaves ~15px between adjacent dots. */
.testi-dot {
  width: 24px; height: 24px; border: 0; padding: 0; background: none;
  cursor: pointer; position: relative;
  transition: width 0.3s var(--ease);
}
.testi-dot::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 9px; height: 9px; border-radius: 50%;
  background: rgba(var(--clay-rgb),0.3);
  transition: width 0.3s var(--ease), background 0.3s, border-radius 0.3s;
}
.testi-dot.active { width: 40px; }
.testi-dot.active::before { width: 26px; border-radius: 6px; background: var(--accent); }
.testi-controls { max-width: var(--maxw); margin: 1.8rem auto 0; display: flex; gap: 0.8rem; justify-content: center; }
.testi-btn { width: 52px; height: 52px; border-radius: 50%; border: 1.5px solid var(--clay); background: transparent; color: var(--clay); font-size: 1.3rem; cursor: pointer; transition: 0.3s var(--ease); }
.testi-btn:hover { background: var(--clay); color: var(--paper); transform: translateY(-2px); }

/* Featured single review (photo + quote) */
.testi-featured { max-width: 1000px; margin: 0 auto; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); overflow: hidden; display: grid; grid-template-columns: 0.82fr 1.18fr; align-items: stretch; box-shadow: 0 18px 44px rgba(22,22,22,0.06); }
.testi-featured-media { position: relative; }
.testi-featured-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 30%; display: block; }
/* Chance's photo is the only landscape source (640x480); the shared `center 30%`
   lands on his shoulder and clips the muzzle. His head sits at ~x440 of 640, so
   85% keeps it in frame. Two classes to clear the rule above at (0,1,1). */
.testi-featured-media.testi-media-chance img { object-position: 85% center; }
.testi-featured-body { padding: var(--sp-s); display: flex; flex-direction: column; justify-content: center; }
.testi-featured-body blockquote { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-md); line-height: 1.32; letter-spacing: -0.01em; }
.testi-featured-body blockquote::before { content: "\201C"; font-family: var(--font-head); font-size: 4rem; color: var(--accent); line-height: 0; vertical-align: -0.35em; margin-right: 0.08em; }
.testi-featured-body figcaption { margin-top: 1.6rem; font-family: var(--font-head); }
.testi-name { display: block; font-weight: 600; font-size: 1.15rem; color: var(--ink); }
.testi-pet { display: block; color: var(--sand); font-weight: 500; font-size: 0.95rem; margin-top: 0.15rem; }
.testi-flip-hint { display: inline-flex; align-items: center; gap: 0.4em; margin-top: 1.3rem; font-family: var(--font-head); font-weight: 600; font-size: 0.82rem; letter-spacing: 0.03em; text-transform: uppercase; color: var(--accent); }
.testi-flip-hint span { font-size: 1.1em; }

/* Flip card (click/tap to reveal the full review + second photo) */
.testi-slide { flex: 0 0 100%; min-width: 0; }
.testi-featured.no-media { grid-template-columns: 1fr; }
.testi-featured.no-media .testi-featured-body { padding: var(--sp-s); }
.testi-flip { max-width: 1000px; margin: 0 auto; perspective: 1800px; cursor: pointer; border-radius: var(--radius); }
/* align-items:start so each face keeps its own height instead of both being
   stretched to the taller one — script.js then sets the card to the face on
   show. The height transition makes that change read as the card opening
   rather than the page jumping. */
.testi-flip-inner { display: grid; align-items: start; transition: transform 0.7s var(--ease), height 0.45s var(--ease); transform-style: preserve-3d; }
.testi-flip.is-flipped .testi-flip-inner { transform: rotateY(180deg); }
.testi-flip .testi-face { grid-area: 1 / 1; max-width: none; margin: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.testi-flip .testi-back { transform: rotateY(180deg); }
.testi-flip:focus-visible { outline: 3px solid rgba(var(--accent-rgb), 0.6); outline-offset: 5px; }
/* long-form review on the back: smaller, no giant quote mark */
.testi-featured-body blockquote.testi-full { font-family: var(--font-body); font-weight: 500; font-size: var(--fs-body-sm); line-height: 1.55; letter-spacing: 0; color: var(--ink); }
.testi-featured-body blockquote.testi-full::before { content: none; }
/* Chance's review is the only multi-paragraph one; 200 words as a single block
   is a wall. Two classes to clear `.testi-featured-body blockquote` at (0,2,1). */
.testi-featured-body .testi-full p + p { margin-top: 0.75em; }

@media (max-width: 760px) {
  /* align-content:start — the front is forced to the back's height (both faces
     share one grid cell), so there is always slack on the shorter face. Without
     this it lands between the photo and the quote as a dead gap; centred, you
     scroll past ~550px of empty card before reaching the photo. Starting the
     rows puts photo and quote first and pushes the slack to the bottom, where
     it reads as padding above the dots. */
  .testi-featured { grid-template-columns: 1fr; align-content: start; }
  /* align-self:start is load-bearing, not tidying. The flip stacks both faces in
     one grid cell, so the front is as tall as the back. A stretched row plus
     aspect-ratio makes the browser derive WIDTH from that height — Chance's
     948px-wide media box inside a 375px card, clipped by overflow:hidden into a
     random zoomed crop. Starting the row keeps width definite and lets height
     follow the ratio. */
  .testi-featured-media { aspect-ratio: 4 / 3; align-self: start; width: 100%; }
}

/* ---------- Pricing ---------- */
.pricing { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-xl) 1.5rem; }
/* Pricing. Two cards carry the figures; the note underneath carries the
   caveat and the CTA. The card selectors below are deliberately two-class —
   `.feature-card p` is (0,1,1) and beats any lone class, which is the same
   trap that once rendered .price-caveat as green display type. */
.price-grid { max-width: 860px; margin-inline: auto; }
.price-card .price-figure {
  font-family: var(--font-head); font-weight: 700; font-size: var(--fs-md);
  line-height: 1; letter-spacing: -0.02em; color: var(--clay);
  margin-bottom: 0.6rem;
}
/* Flex column + margin-top:auto so both cards' rules sit on the same baseline,
   however unequal the body copy above them is. */
.price-card { display: flex; flex-direction: column; }
/* Space above the rule. margin-top:auto alone gives none once the cards stack
   at mobile and there is no free space left to absorb. */
.price-card h3 + p { margin-bottom: 1.2rem; }
.price-card .price-fine {
  font-size: 0.85rem; color: var(--muted);
  margin-top: auto; padding-top: 1.1rem;
  border-top: 1px solid rgba(var(--clay-rgb), 0.14);
}
.price-note { max-width: 720px; margin: var(--sp-s) auto 0; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.2); border-radius: var(--radius); padding: var(--sp-m); text-align: center; }
.price-note > p { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-sm); line-height: 1.35; letter-spacing: -0.01em; color: var(--clay); margin-bottom: var(--sp-xs); }
/* included list: the .ticks component, re-centred for this panel */
.price-includes { display: grid; gap: 0.7rem; text-align: left; max-width: 420px; margin: 0 auto var(--sp-xs); }
/* needs the element in the selector: `.price-note > p` is (0,1,1) and would
   otherwise beat a lone `.price-caveat` at (0,1,0), leaving the footnote set
   as green display type. */
.price-note > p.price-caveat {
  font-family: var(--font-body); font-weight: 400;
  font-size: 0.95rem; line-height: 1.55; letter-spacing: 0;
  color: var(--muted); max-width: 48ch; margin: 0 auto var(--sp-xs);
}

/* ---------- Contact ---------- */
.contact { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-xl) 1.5rem; display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-m); }
.contact-intro h2 { margin: 0.4rem 0 1.2rem; }
.contact-intro > p { color: var(--muted); margin-bottom: 2rem; }
.contact-details { list-style: none; display: grid; gap: 1rem; margin-bottom: 2rem; }
/* 96px, not 80: "WHATSAPP" is the longest label and overran the old column. */
.contact-details li { display: grid; grid-template-columns: 96px 1fr; align-items: baseline; }
.contact-details span { font-family: var(--font-head); font-weight: 600; color: var(--sand); text-transform: uppercase; font-size: 0.78rem; letter-spacing: 0.1em; }
.contact-details a:hover { color: var(--accent); }
.hours { width: 100%; border-collapse: collapse; }
.hours th, .hours td { text-align: left; padding: 0.6rem 0; border-bottom: 1px solid rgba(var(--clay-rgb),0.2); }
.hours th { font-family: var(--font-head); font-weight: 600; }
.hours td { color: var(--muted); }
/* Two classes deliberately: `.contact-intro > p` is (0,1,1) and would otherwise
   win the margin. Same trap that turned .price-caveat into a heading. */
.contact-intro .hours-note { margin-top: 0.9rem; margin-bottom: 0; }

.contact-form { background: var(--paper-2); border: 1px solid rgba(var(--clay-rgb),0.18); border-radius: var(--radius); padding: var(--sp-xs); }
.field { margin-bottom: 1.2rem; }
.field label { display: block; font-family: var(--font-head); font-weight: 500; margin-bottom: 0.4rem; font-size: 0.92rem; }
.field input, .field textarea { width: 100%; padding: 0.85rem 1rem; border: 1px solid rgba(var(--clay-rgb),0.35); border-radius: 10px; background: var(--paper); font-family: var(--font-body); font-size: 1rem; color: var(--ink); transition: border-color 0.25s, box-shadow 0.25s; }
.field input:focus, .field textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18); }
.form-note { font-size: 0.82rem; color: var(--sand); margin-top: 1rem; }
.form-success { margin-top: 1rem; padding: 0.9rem 1.1rem; border-radius: 12px; background: rgba(var(--clay-rgb),0.12); border-left: 3px solid var(--accent); color: var(--ink); font-size: 0.95rem; }
/* Netlify honeypot. Has to be in the DOM for the spam score, must never be seen
   or reachable — display:none here, aria-hidden + tabindex="-1" in the markup. */
.hp-field { display: none; }
/* Error panel sits on --paper, not an amber tint: --accent-text is 5.08:1 on
   paper but drops to ~4.1:1 once you tint the background amber, which fails AA
   for the recovery links that are the whole point of the message. */
.form-error { margin-top: 1rem; padding: 0.9rem 1.1rem; border-radius: 12px; background: var(--paper); border-left: 3px solid var(--accent-text); color: var(--ink); font-size: 0.95rem; line-height: 1.5; }
.form-error a { color: var(--accent-text); text-decoration: underline; }

/* ---------- Footer ---------- */
.footer { background: var(--ink); color: var(--paper); padding: var(--sp-l) 1.5rem 2rem; }
.footer-inner { max-width: var(--maxw); margin: 0 auto; display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 2.5rem; }
.footer-brand img { height: 46px; width: auto; margin-bottom: 1rem; }
.footer-brand p { color: rgba(251,248,238,0.6); max-width: 28ch; }
.footer-col h4 { font-size: 1rem; margin-bottom: 1rem; color: var(--cream); }
.footer-col a, .footer-col span { display: block; color: rgba(251,248,238,0.7); margin-bottom: 0.6rem; transition: color 0.25s; }
.footer-col a:hover { color: var(--accent); }
.footer-base { max-width: var(--maxw); margin: 3rem auto 0; padding-top: 1.6rem; border-top: 1px solid rgba(251,248,238,0.12); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; font-size: 0.88rem; color: rgba(251,248,238,0.55); }
.to-top:hover { color: var(--accent); }
.footer-social { display: flex; gap: 0.7rem; margin-top: 1.2rem; }
.footer-social a { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 50%; border: 1px solid rgba(251,248,238,0.25); color: rgba(251,248,238,0.8); margin: 0; transition: all 0.3s var(--ease); }
.footer-social a:hover { background: var(--accent); border-color: var(--accent); color: var(--ink); transform: translateY(-3px); }
.footer-social svg { width: 18px; height: 18px; }
.footer-legal { display: flex; gap: 1.4rem; }
.footer-legal a { color: rgba(251,248,238,0.55); }
.footer-legal a:hover { color: var(--accent); }

/* Qualifications & accreditations strip */
.footer-creds { max-width: var(--maxw); margin: 3rem auto 0; padding-top: 1.6rem; border-top: 1px solid rgba(251,248,238,0.12); }
.footer-creds h4 { font-size: 1rem; margin-bottom: 1.1rem; color: var(--cream); }
.cred-list { list-style: none; display: flex; flex-wrap: wrap; gap: 0.8rem; }
.cred-list a { display: inline-flex; align-items: center; gap: 0.6rem; padding: 0.7rem 1.1rem; border-radius: var(--radius-pill); border: 1px solid rgba(251,248,238,0.2); color: rgba(251,248,238,0.85); font-family: var(--font-head); font-weight: 500; font-size: 0.92rem; transition: transform 0.3s var(--ease), border-color 0.3s, color 0.3s; }
.cred-list a:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }
.cred-list svg { width: 18px; height: 18px; flex: 0 0 auto; color: var(--accent); }
.cred-tag { font-size: 0.64rem; font-weight: 600; letter-spacing: 0.06em; padding: 0.14em 0.5em; border-radius: 5px; background: rgba(251,248,238,0.12); color: rgba(251,248,238,0.7); }
.cred-list a:hover .cred-tag { background: rgba(var(--accent-rgb), 0.18); color: var(--accent); }

/* ---------- Reveal animations ---------- */
.reveal { opacity: 0; transform: translateY(28px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

/* kinetic word-rise (hero + section headings) */
.split .w, .split-line .w { display: inline-block; overflow: hidden; vertical-align: top; }
.split .w > i, .split-line .w > i { display: inline-block; font-style: normal; transform: translateY(110%); transition: transform 0.9s var(--ease); transition-delay: calc(var(--i, 0) * 0.05s); }
.split.in .w > i, .split-line.in .w > i { transform: none; }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  /* the About photo is much smaller here, so the badge would sit over
     Christine's face — drop it below the image instead, and hide the inset */
  .about-breakout { position: static; max-width: none; margin-top: 1.1rem; box-shadow: none; }
  .about-collage-sec { display: none; }
  .nav-links { display: none; }
  .nav-toggle { display: flex; }
  .shape { display: none; }
  .service, .about, .contact { grid-template-columns: 1fr; }
  .service-dark .service-media { order: -1; }
  .steps { grid-template-columns: repeat(2, 1fr); }
  .steps::before { display: none; }
  .stats { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 1rem; }
  .price-grid { grid-template-columns: 1fr; max-width: 460px; margin: 0 auto; }
  .footer-inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 540px) {
  .steps { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; }
  .hero { padding-top: 7rem; }
  .nav-inner { padding: 0.5rem 1rem; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; scroll-behavior: auto !important; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .split .w > i, .split-line .w > i { transform: none; transition: none; }
  .marquee-track { animation: none; }
  .testi-flip-inner { transition: none; }
}

/* ============================================================
   Learn / condition subpages (Mobility Therapy, Osteoarthritis)
   ============================================================ */
.crumb { max-width: var(--maxw); margin: 0 auto; padding: 6.5rem 1.5rem 0; }
.crumb a { color: var(--sand); font-family: var(--font-head); font-weight: 500; font-size: 0.9rem; transition: color 0.25s; }
.crumb a:hover { color: var(--accent); }
.crumb span { color: rgba(107,101,83,0.5); margin: 0 0.5rem; }

.page-hero { position: relative; overflow: hidden; padding: var(--sp-m) 1.5rem clamp(3rem, 7vw, 5rem); text-align: center; }
.page-hero::before, .page-hero::after { content: ""; position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.5; z-index: 0; pointer-events: none; }
.page-hero::before { width: 380px; height: 380px; background: radial-gradient(circle, rgba(var(--accent-rgb),0.35), transparent 70%); top: -140px; left: -60px; }
.page-hero::after { width: 340px; height: 340px; background: radial-gradient(circle, rgba(var(--clay-rgb),0.30), transparent 70%); top: 20px; right: -90px; }
.page-hero > * { position: relative; z-index: 1; }
.page-hero h1 { max-width: 16ch; margin: 0.4rem auto 1.3rem; }
.page-sub { max-width: 56ch; margin: 0 auto; color: var(--muted); font-size: var(--fs-body-lg); }
.page-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; margin-top: 2.2rem; }

/* Byline. These are health guides for anxious owners, and until now nothing on
   them said who wrote them or when — the expertise was real and documented in
   the footer certificates, but never attached to the content it was backing.
   The date is the honest one from git, not a rolling "freshness" fake. */
.byline {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 0.5rem 0.75rem; margin: 1.6rem auto 0; max-width: 56ch;
  font-size: 0.9rem; color: var(--muted);
}
.byline a { color: var(--accent-text); font-weight: 500; }
.byline a:hover { text-decoration: underline; }
.byline-sep { opacity: 0.45; }

/* Service area. The location signal used to live only in the footer, so a
   reader (or a search engine) on a guide page had no idea where any of this
   was actually offered. */
.service-area { margin-top: 2.2rem; }
.service-area strong { font-family: var(--font-head); color: var(--ink); }

/* Footer NAP. Name, address and service area written out in the same words the
   LocalBusiness schema uses — a mismatch between the two is exactly what
   erodes local trust signals. <address> is the correct element and browsers
   italicise it by default, which this undoes. */
.footer-nap {
  font-style: normal; display: flex; flex-direction: column; gap: 0.15rem;
  margin-top: 0.5rem;
}
.footer-nap .nap-name { font-family: var(--font-head); font-weight: 600; }

.learn { max-width: 900px; margin: 0 auto; padding: var(--sp-l) 1.5rem; }
.learn-wide { max-width: var(--maxw); }
.learn-lead { font-family: var(--font-head); font-weight: 500; font-size: var(--fs-lg); line-height: 1.3; letter-spacing: -0.01em; margin-bottom: 1.4rem; }
.learn .body { color: var(--muted); font-size: 1.08rem; }
.learn .body p + p { margin-top: 1.1rem; }
.learn .section-head { margin-bottom: var(--sp-s); }

.band { background: var(--paper-2); }

/* pull-quote / goal callout */
.goal-quote { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-lg); line-height: 1.2; color: var(--clay); text-align: center; max-width: 20ch; margin: 0 auto; }
.goal-quote span { color: var(--accent); }

/* feature / benefit cards */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.2rem; margin-top: var(--sp-s); }
.feature-card { background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: 1.7rem; transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease); }
.feature-card:hover { transform: translateY(-5px); box-shadow: 0 18px 40px rgba(22,22,22,0.08); }
.feature-icon { width: 48px; height: 48px; border-radius: 13px; background: rgba(var(--accent-rgb),0.12); color: var(--accent); display: flex; align-items: center; justify-content: center; margin-bottom: 1.1rem; }
.feature-icon svg { width: 24px; height: 24px; }
.feature-card h3 { font-size: 1.15rem; margin-bottom: 0.45rem; }
.feature-card p { font-size: 0.95rem; color: var(--muted); }

/* Ailment lists — laser-general-ailments.html.
   Grid capped at 3 columns: six cards in the default 4-col auto-fit leaves a
   ragged 4+2 row. align-items:start stops the 5-item cards stretching to match
   the 12-item ones and carrying a block of dead space. */
.ailments-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); max-width: 880px; margin-inline: auto; align-items: start; }
/* Same dot language as .ticks but smaller — these run to a dozen items a card,
   where .ticks' 11px dots shout. */
.ailment-list { list-style: none; display: grid; gap: 0.42rem; margin-top: 0.9rem; }
.ailment-list li { position: relative; padding-left: 1.05rem; font-size: 0.95rem; color: var(--muted); line-height: 1.4; }
.ailment-list li::before { content: ""; position: absolute; left: 0; top: 0.58em; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }

/* comparison cards (mild vs severe) */
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem; margin-top: var(--sp-s); }
.compare-card { border-radius: var(--radius); padding: 1.9rem; border: 1px solid rgba(var(--clay-rgb),0.18); }
.compare-mild { background: var(--paper); }
.compare-severe { background: rgba(var(--accent-rgb),0.07); border-color: rgba(var(--accent-rgb),0.32); }
.compare-card h3 { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.9rem; }
.compare-tag { font-family: var(--font-head); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.12em; padding: 0.25em 0.8em; border-radius: 100px; }
.compare-mild .compare-tag { background: rgba(var(--clay-rgb),0.18); color: var(--clay); }
.compare-severe .compare-tag { background: var(--accent); color: var(--ink); }
.compare-card p { color: var(--muted); font-size: 1rem; }

/* numbered pain-pathway steps */
.flow { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1.2rem; margin-top: var(--sp-s); counter-reset: f; }
.flow li { list-style: none; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: 1.6rem; position: relative; }
.flow li::before { counter-increment: f; content: counter(f); font-family: var(--font-head); font-weight: 700; font-size: 1.1rem; color: var(--accent); background: rgba(var(--accent-rgb),0.12); width: 38px; height: 38px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 0.9rem; }
.flow li strong { font-family: var(--font-head); display: block; margin-bottom: 0.3rem; }
.flow li p { font-size: 0.92rem; color: var(--muted); }

/* choice-led ethos callout */
.ethos { max-width: 900px; margin: 0 auto; }
.ethos-card { background: var(--ink); color: var(--paper); border-radius: clamp(18px,3vw,28px); padding: var(--sp-s); }
.ethos-card .eyebrow { color: var(--accent); }
.ethos-card p { color: rgba(251,248,238,0.78); font-size: 1.1rem; }

/* CTA band */
.cta-band { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-m) 1.5rem clamp(4rem, 8vw, 6rem); }
.cta-inner { background: var(--ink); color: var(--paper); border-radius: clamp(20px, 4vw, 36px); padding: var(--sp-m); text-align: center; position: relative; overflow: hidden; }
.cta-inner .laser-streak { top: 40%; }
.cta-inner h2 { margin-bottom: 1rem; }
.cta-inner p { color: rgba(251,248,238,0.72); max-width: 50ch; margin: 0 auto 2rem; }
.cta-inner .btn-primary { background: var(--accent); color: var(--ink); }
.cta-inner .btn-primary:hover { background: var(--paper); color: var(--ink); }

/* related cross-links */
.related { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin-top: var(--sp-s); }
.related-card { display: block; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: 1.7rem 1.9rem; transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease); }
.related-card:hover { transform: translateY(-4px); box-shadow: 0 16px 36px rgba(22,22,22,0.08); }
.related-card .eyebrow { margin-bottom: 0.5rem; }
.related-card strong { font-family: var(--font-head); font-weight: 600; font-size: 1.25rem; color: var(--ink); display: block; margin-bottom: 0.5rem; }
.related-card .link-arrow { font-size: 0.95rem; }

/* Learn hub card grid (reuses .related-card) */
.guide-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(278px, 1fr)); gap: 1.2rem; margin-top: var(--sp-xs); align-items: stretch; }
.guide-grid .related-card { display: flex; flex-direction: column; }
.guide-grid .related-card p:not(.eyebrow) { color: var(--muted); font-size: 0.97rem; margin: 0 0 1.3rem; }
.guide-grid .related-card .link-arrow { margin-top: auto; align-self: flex-start; }

/* ---------- IVDD page extras ---------- */
/* two-column media block (text + illustration) */
.media-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-m); align-items: center; }
.media-frame--43 { aspect-ratio: 4 / 3; border: 1px solid rgba(var(--clay-rgb),0.14); }

/* SVG diagram figure */
.diagram { margin: 0 auto; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-radius: var(--radius); padding: var(--sp-xs); }
.diagram svg { width: 100%; height: auto; display: block; }
.diagram figcaption { text-align: center; color: var(--muted); font-size: 0.9rem; margin-top: 1rem; }

/* ---------- Diagram teaching motion (no library) ----------
   The healthy side of each diagram is simply there; the AMBER marks — the
   problem state — settle in a beat later, so the eye reads "normal, then
   what goes wrong" instead of taking the whole thing in at once.

   No JS: the figures are already `.diagram.reveal`, and the existing
   IntersectionObserver in script.js adds `.in`. This just hangs off that.

   Amber is targeted by attribute rather than by adding classes to ~20 SVG
   nodes across four pages. It is the same #E08A3C everywhere — checked.

   transform-box:fill-box is what makes scale() grow from each mark's own
   centre; without it SVG children scale from the viewBox origin and fly
   across the diagram. */
.diagram svg [fill="#E08A3C"],
.diagram svg [stroke="#E08A3C"] {
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0.55);
  transition: transform 0.55s var(--ease) 0.35s, opacity 0.45s ease 0.35s;
}
/* Only fade marks that DON'T carry an authored opacity — two of them are
   deliberately 0.75 and 0.9, and animating to opacity:1 would silently
   flatten that. Those get the scale only. */
.diagram svg [fill="#E08A3C"]:not([opacity]),
.diagram svg [stroke="#E08A3C"]:not([opacity]) { opacity: 0; }

.diagram.in svg [fill="#E08A3C"],
.diagram.in svg [stroke="#E08A3C"] { transform: scale(1); }
.diagram.in svg [fill="#E08A3C"]:not([opacity]),
.diagram.in svg [stroke="#E08A3C"]:not([opacity]) { opacity: 1; }

/* The panel headings are type, not marks — they shouldn't pop, just arrive. */
.diagram svg text[fill="#E08A3C"] { transform: none; }
.diagram.in svg text[fill="#E08A3C"] { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .diagram svg [fill="#E08A3C"],
  .diagram svg [stroke="#E08A3C"] { transform: none; opacity: 1; transition: none; }
}

/* signs to watch for */
.signs-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 0.9rem; margin-top: var(--sp-xs); }
.sign { display: flex; align-items: center; gap: 0.7rem; background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16); border-left: 3px solid var(--accent); border-radius: 10px; padding: 0.9rem 1.1rem; font-family: var(--font-head); font-weight: 500; font-size: 0.98rem; }
.sign svg { flex: 0 0 auto; width: 18px; height: 18px; color: var(--accent); }

/* educational note callout */
.callout-note { background: var(--paper-2); border: 1px solid rgba(var(--clay-rgb),0.16); border-left: 3px solid var(--accent); border-radius: 0 var(--radius) var(--radius) 0; padding: 1.5rem 1.7rem; margin-top: 2rem; }
.callout-note strong { font-family: var(--font-head); color: var(--ink); }
.callout-note p { color: var(--muted); margin: 0; }

/* ---------- Analytics consent banner ----------
   Required because GA4 sets cookies and this site operates in Spain, where
   prior consent is not optional. It is deliberately small and calm: this is a
   site about not forcing animals to do things, and a full-screen interstitial
   demanding a decision would be a poor first impression.

   [hidden] must be re-asserted — the display rule below would otherwise beat
   the attribute, and the banner would flash on every load before script.js
   decides whether it is needed. */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1000;
  background: var(--paper);
  border-top: 1px solid rgba(var(--clay-rgb),0.22);
  box-shadow: 0 -10px 34px rgba(22,22,22,0.10);
  padding: 1.1rem 1.5rem;

  /* NO entry animation, deliberately.
     The first version used `animation: cookie-rise .4s both`. CSS animations
     do not run on a display:none element, and the banner starts [hidden], so
     it stuck permanently on its `from` frame — parked one full height BELOW
     the viewport. It was in the DOM, reported itself visible to script, and
     could still be clicked programmatically, so every check short of
     measuring its rect against innerHeight passed. No human could see it.

     The obvious repair — start at translateY(100%), have JS add a class on the
     next animation frame — trades one invisible failure for another: rAF does
     not fire in a background or non-compositing tab, so the banner would again
     be present but off-screen, and again only detectable by measuring.

     This is a consent control that Spanish law requires to be shown. It does
     not get to depend on a frame callback for its visibility. It has no
     transform at all now: unhide it and it is simply there. */
}
.cookie-banner[hidden] { display: none; }

.cookie-inner {
  max-width: var(--maxw); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
}
.cookie-copy { flex: 1 1 380px; }
.cookie-title {
  font-family: var(--font-head); font-weight: 600; color: var(--ink);
  margin: 0 0 0.2rem; font-size: 1rem;
}
.cookie-text { margin: 0; color: var(--muted); font-size: 0.9rem; max-width: 70ch; }
.cookie-text a { color: var(--accent-text); text-decoration: underline; }
.cookie-actions { display: flex; gap: 0.7rem; flex: 0 0 auto; }
.cookie-actions .btn { padding: 0.7rem 1.4rem; font-size: 0.92rem; }

@media (max-width: 560px) {
  .cookie-inner { flex-direction: column; align-items: stretch; gap: 1rem; }
  .cookie-actions { justify-content: stretch; }
  .cookie-actions .btn { flex: 1; text-align: center; }
}

/* ---------- FAQ ----------
   Native <details>/<summary>: no JavaScript, keyboard-operable for free, and
   crucially the answer text is in the DOM whether or not the item is open —
   which is what makes it extractable by a crawler or an AI engine. An
   accordion built from divs and JS would hide the answers from both. */
.faq-list { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 2.2rem; }
.faq-item {
  background: var(--paper); border: 1px solid rgba(var(--clay-rgb),0.16);
  border-radius: var(--radius); overflow: hidden;
  transition: border-color 0.25s var(--ease);
}
.faq-item[open] { border-color: rgba(var(--clay-rgb),0.34); }
.faq-item summary {
  cursor: pointer; padding: 1.15rem 1.4rem; list-style: none;
  display: flex; align-items: center; justify-content: space-between; gap: 1.2rem;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.faq-q {
  font-family: var(--font-head); font-weight: 600; font-size: 1.05rem;
  color: var(--ink); margin: 0; line-height: 1.25;
}
/* Plus/minus drawn in CSS so there is no icon font and no inline SVG per item. */
.faq-item summary::after {
  content: ""; flex: 0 0 auto; width: 13px; height: 13px; position: relative;
  background:
    linear-gradient(var(--accent-text), var(--accent-text)) center/100% 2px no-repeat,
    linear-gradient(var(--accent-text), var(--accent-text)) center/2px 100% no-repeat;
  transition: transform 0.25s var(--ease);
}
.faq-item[open] summary::after {
  background: linear-gradient(var(--accent-text), var(--accent-text)) center/100% 2px no-repeat;
  transform: rotate(180deg);
}
.faq-a { padding: 0 1.4rem 1.35rem; margin: 0; color: var(--muted); max-width: 68ch; }

/* emergency alert callout */
.callout-alert { background: rgba(var(--accent-rgb),0.10); border: 1px solid rgba(var(--accent-rgb),0.34); border-left: 4px solid var(--accent); border-radius: 0 var(--radius) var(--radius) 0; padding: 1.5rem 1.7rem; margin-top: 1.8rem; display: flex; gap: 1rem; align-items: flex-start; }
.callout-alert svg { flex: 0 0 auto; width: 26px; height: 26px; color: var(--accent); margin-top: 2px; }
.callout-alert strong { font-family: var(--font-head); color: var(--ink); }
.callout-alert p { color: var(--muted); margin: 0; }

/* vet-essential banner (dark) */
.vet-banner { background: var(--ink); color: var(--paper); border-radius: var(--radius); padding: var(--sp-xs) clamp(1.6rem,3vw,2.2rem); display: flex; align-items: center; gap: 1.2rem; margin-top: 1.4rem; }
.vet-banner .vet-icon { flex: 0 0 auto; width: 52px; height: 52px; border-radius: 50%; background: rgba(var(--accent-rgb),0.18); color: var(--accent); display: flex; align-items: center; justify-content: center; }
.vet-banner .vet-icon svg { width: 26px; height: 26px; }
.vet-banner h3 { color: var(--paper); font-size: var(--fs-sm); margin-bottom: 0.2rem; }
.vet-banner p { color: rgba(251,248,238,0.72); font-size: 0.98rem; margin: 0; }

@media (max-width: 820px) {
  .media-split { grid-template-columns: 1fr; }
  .vet-banner { flex-direction: column; text-align: center; }
}

@media (max-width: 720px) {
  .compare, .related { grid-template-columns: 1fr; }
}

/* ============================================================
   Brand colour rules that fall out of the palette
   ============================================================ */

/* Headings carry the brand. Body copy stays neutral (--muted). */
h1, h2, h3 { color: var(--clay); }

/* Sage is only safe against dark, so the footer is where it earns its keep. */
.footer-col h4 { color: var(--leaf); }

/* Headings inside the dark footer must NOT inherit the forest rule above —
   forest on ink is 2.46:1 and would be unreadable. */
.footer h1,
.footer h2,
.footer h3,
/* .service-dark is the OTHER dark surface on the site and was missed when the
   green went site-wide — "Class IV Laser Therapy" was rendering forest on ink
   at 2.46:1, under the 3.0 a heading that size needs. Any new dark block must
   be added here too. */
.service-dark h1,
.service-dark h2,
.service-dark h3 { color: var(--leaf); }

/* Same rule, same miss: .link-arrow is --accent-text (#9A5B21), which is 5.08:1
   on paper but only 3.34:1 on ink — it fails AA on the one dark block that has
   links. Sage takes it to ~10:1. Forest on light, sage on dark. */
.service-dark .link-arrow { color: var(--leaf); }
.service-dark .link-arrow:hover { border-color: var(--leaf); }
/* two links now sit under the laser copy */
.service-links { display: flex; flex-wrap: wrap; gap: 0.9rem 1.6rem; margin-top: 0.2rem; }
