@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600&display=swap');

:root {
  /* Brand palette */
  --color-sand: #C8B49A;
  --color-burgundy: #7A1A1A;
  --color-black: #1A1A1A;
  --color-offwhite: #E8E8EC;

  /* Semantic (light theme) */
  --bg: var(--color-sand);
  --surface: var(--color-offwhite);
  --card: #ffffff;
  --text: var(--color-black);
  --accent: var(--color-burgundy);
  --border: color-mix(in srgb, var(--color-black) 8%, transparent);
  --nav-bg: color-mix(in srgb, var(--color-sand) 92%, transparent);
  --contact-bg: var(--color-sand);
  /* "In Training" status badge — deep teal, distinct from the burgundy "For
     Sale" badge. White badge text on this color clears WCAG AA (≈5.6:1). */
  --badge-training: #1f5d5b;
  /* "Best Seller" shop badge — warm gold, distinct from the burgundy/teal horse
     badges. Pairs with near-black text (--badge-bestseller-text) which clears
     WCAG AA on this gold (≈8.4:1). */
  --badge-bestseller: #e0a92e;
  --badge-bestseller-text: #1A1A1A;

  --font-display: 'Cormorant Garamond', Georgia, serif;
  --font-body: 'Inter', system-ui, sans-serif;

  --maxw: 1120px;
  --space: clamp(1rem, 2vw, 2rem);
  --radius: 10px;

  /* Sticky-navbar height. Fallback ≈ logo 64px + 0.5rem*2 padding + 1px border;
     nav.js measures the real value and overwrites this so the hero subtracts
     the exact bar height (nav + hero = one viewport). */
  --nav-h: 81px;

  /* Strong custom easings (built-in CSS easings lack punch). */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  color-scheme: light;
}

:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #1b1917;
  --surface: #232020;
  --card: #2a2624;
  --text: #E8E8EC;
  --accent: #b9423f;
  --border: rgba(232, 232, 236, 0.14);
  --nav-bg: rgba(27, 25, 23, 0.86);
  --contact-bg: #232020;
  /* Slightly lighter teal so white badge text still clears AA on the darker UI. */
  --badge-training: #2a7a77;
  /* Brighter gold on the darker UI; dark text keeps strong contrast. */
  --badge-bestseller: #f0bf52;
  /* --badge-bestseller-text is intentionally NOT redefined here: the light-theme
     near-black (#1A1A1A) keeps >10:1 contrast on this brighter gold, so the dark
     theme reuses it as-is. */
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.6;
  /* Ease the whole page between light/dark instead of snapping. */
  transition: background-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
}

h1, h2, h3 {
  font-family: var(--font-display);
  line-height: 1.1;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.5rem, 6vw, 4.5rem); }
h2 { font-size: clamp(2rem, 4vw, 3rem); }

p { text-wrap: pretty; }

a { color: var(--accent); }

/* Visible keyboard focus for every interactive element (mouse clicks stay clean). */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Skip link: hidden off-screen until focused, lets keyboard users jump past the
   navbar straight to <main id="main-content"> (WCAG 2.4.1). */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  z-index: 200;
  background: var(--accent);
  color: #fff;
  padding: 0.5rem 1rem;
  border-radius: 0 0 var(--radius) 0;
  text-decoration: none;
}
.skip-link:focus { top: 0; }

.container {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--space);
}

.section { padding-block: clamp(3rem, 8vw, 6rem); }

.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--color-offwhite);
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius);
  text-decoration: none;
  /* Native <button> elements don't inherit font; the contact "Send Email"
     button needs this to match the <a class="btn"> link buttons. */
  font: inherit;
  font-weight: 600;
  transition: transform 0.16s var(--ease-out), opacity 0.16s var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-2px); opacity: 0.92; }
}
.btn:active { transform: scale(0.97); }

.btn--ghost {
  background: transparent;
  color: var(--accent);
  border: 2px solid var(--accent);
}

/* Scroll-reveal is enhancement-only: content is visible by default, and only
   hidden-then-revealed when JS is present (html.js). No JS, no blank sections. */
html.js .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out); }
html.js .reveal.is-visible { opacity: 1; transform: none; }

/* Hero entrance: each line rises in, staggered, on first load. JS-gated so
   no-JS users see content immediately; only runs when motion is allowed. */
@media (prefers-reduced-motion: no-preference) {
  html.js .hero__eyebrow,
  html.js .hero h1,
  html.js .hero__tagline,
  html.js .hero__cta {
    opacity: 0;
    animation: hero-rise 0.7s var(--ease-out) forwards;
  }
  html.js .hero__eyebrow { animation-delay: 0.10s; }
  html.js .hero h1       { animation-delay: 0.20s; }
  html.js .hero__tagline { animation-delay: 0.34s; }
  html.js .hero__cta     { animation-delay: 0.48s; }
}
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js .reveal { opacity: 1; transform: none; transition: none; }
}

/* === Navbar === */
  .nav {
    position: sticky; top: 0; z-index: 50;
    background: var(--nav-bg);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--border);
    transition: background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
  }
  .nav__inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-block: 0.5rem; }
  .nav__brand { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
  .nav__logo { width: 64px; height: 64px; border-radius: 10px; object-fit: contain; display: block; }
  .nav__right { display: flex; align-items: center; gap: 0.5rem; }
  .nav__links { position: relative; display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; justify-content: flex-end; }

  /* One shared burgundy box (the indicator) glides between tabs instead of each
     tab toggling its own background. */
  .nav__indicator {
    position: absolute; top: 0; left: 0; z-index: 0;
    width: 0; height: 0; border-radius: 8px;
    background: var(--accent); opacity: 0; pointer-events: none;
    /* Smooth, evenly-eased glide (no front-loaded snap). */
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.2s ease;
  }

  .nav__link {
    position: relative; z-index: 1;
    color: var(--text); text-decoration: none; font-weight: 500; white-space: nowrap;
    padding: 0.4rem 0.7rem; border-radius: 8px; line-height: 1;
    transition: background 0.18s ease, color 0.2s ease;
  }
  .nav__link:hover { color: var(--accent); }

  /* No-JS fallback: the active tab carries its own burgundy fill. */
  .nav__link.is-active { background: var(--accent); color: #fff; font-weight: 600; }
  .nav__link.is-active:hover { color: #fff; }

  /* With JS, the sliding box is the fill, so the active tab is transparent
     (the box sits behind it) and keeps its white text. */
  html.js .nav__link.is-active { background: transparent; }

  @media (prefers-reduced-motion: reduce) {
    .nav__indicator { transition: opacity 0.2s ease; }
  }

  .nav__theme {
    flex: none; display: grid; place-items: center;
    /* 44px = WCAG 2.5.5 minimum touch target. */
    width: 44px; height: 44px; border-radius: 50%;
    background: transparent; border: 1px solid var(--border); color: var(--text);
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.16s var(--ease-out);
  }
  .nav__theme:hover { color: var(--accent); border-color: var(--accent); }
  .nav__theme:active { transform: scale(0.92); }

  /* Both icons share one grid cell and crossfade with a rotate+scale so the
     sun/moon swap feels like a single object turning, not two toggling. */
  .nav__theme svg {
    grid-area: 1 / 1; width: 20px; height: 20px;
    transition: opacity 0.25s var(--ease-out), transform 0.45s var(--ease-out);
  }
  .nav__theme .icon-moon { opacity: 1; transform: rotate(0) scale(1); }
  .nav__theme .icon-sun  { opacity: 0; transform: rotate(-90deg) scale(0.4); }
  :root[data-theme='dark'] .nav__theme .icon-moon { opacity: 0; transform: rotate(90deg) scale(0.4); }
  :root[data-theme='dark'] .nav__theme .icon-sun  { opacity: 1; transform: rotate(0) scale(1); }

  @media (prefers-reduced-motion: reduce) {
    .nav__theme svg { transition: opacity 0.2s ease; transform: none !important; }
  }

  .nav__toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 0.25rem; }
  .nav__toggle span { display: block; width: 26px; height: 2px; background: var(--text); transition: transform 0.2s var(--ease-in-out), opacity 0.2s var(--ease-in-out); }
  .nav[data-open] .nav__toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav[data-open] .nav__toggle span:nth-child(2) { opacity: 0; }
  .nav[data-open] .nav__toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  @media (max-width: 760px) {
    .nav__toggle { display: flex; }
    .nav__links {
      display: none;
      position: absolute; left: 0; right: 0; top: 100%;
      flex-direction: column; align-items: stretch; gap: 0.25rem;
      background: var(--bg);
      border-bottom: 1px solid var(--border);
      padding: 0.6rem var(--space) 1rem;
    }
    .nav[data-open] .nav__links { display: flex; }
    .nav__link { padding: 0.7rem 0.8rem; }
    /* Dropdown menu: no sliding box; the active item just gets its own fill. */
    .nav__indicator { display: none; }
    html.js .nav__link.is-active { background: var(--accent); }
  }

/* === Footer === */
  .footer { background: var(--color-black); color: var(--color-offwhite); padding-block: 2.5rem; }
  .footer__inner { display: flex; flex-direction: column; align-items: center; gap: 0.75rem; text-align: center; }
  .footer__brand { font-family: var(--font-display); font-size: 1.3rem; }
  .footer__loc { margin: 0; opacity: 0.85; display: inline-flex; align-items: center; gap: 0.4rem; }
  .footer__locicon { width: 16px; height: 16px; flex: none; }
  .footer__social { display: flex; gap: 1rem; margin-top: 0.25rem; }
  .footer__icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; border-radius: 50%;
    background: color-mix(in srgb, var(--color-offwhite) 12%, transparent);
    color: var(--color-offwhite);
    transition: background 0.2s ease, color 0.2s ease, transform 0.16s var(--ease-out);
  }
  .footer__icon svg { width: 20px; height: 20px; fill: currentColor; }
  @media (hover: hover) and (pointer: fine) {
    .footer__icon:hover { background: var(--color-burgundy); color: var(--color-offwhite); transform: translateY(-2px); }
  }
  .footer__icon:active { transform: scale(0.92); }
  .footer small { opacity: 0.75; }

/* === Hero === */
  .hero {
    background: linear-gradient(180deg, color-mix(in srgb, var(--color-black) 35%, transparent), color-mix(in srgb, var(--color-black) 60%, transparent)),
                url('/assets/images/hero-roundpen.jpg');
    background-size: cover; background-position: center 20%;
    /* Nav + hero = exactly one screen. First line is the fallback for browsers
       without dvh; the calc line wins where supported and uses the live --nav-h
       (measured in nav.js) so the bar height is subtracted exactly. dvh tracks
       mobile URL-bar collapse so there's no gap/overflow on phones. */
    min-height: 100vh;
    min-height: calc(100dvh - var(--nav-h));
    display: flex; align-items: center; color: var(--color-offwhite);
    text-align: center;
  }
  .hero__inner { width: 100%; }
  .hero__eyebrow { letter-spacing: 0.3em; text-transform: uppercase; font-size: 0.8rem; margin: 0 0 0.5rem; opacity: 0.9; }
  .hero h1 { color: var(--color-offwhite); }
  .hero__tagline { font-family: var(--font-display); font-size: clamp(1.25rem, 2.5vw, 1.75rem); margin-bottom: 2rem; }
  .hero__cta { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }
  .hero .btn--ghost { color: var(--color-offwhite); border-color: var(--color-offwhite); }

  /* Pin&stack for the Home hero (pin-only — no rubberband). The full-screen
     hero pins to the top so the About panel (.pin-content) scrolls up and stacks
     over it. z-index keeps it beneath .pin-content.
     Two gates make the fallbacks safe:
       - .is-pinstack: JS adds it only once pin-stack.js has taken over, so with
         no JS the hero stays a normal full-screen block (page scrolls normally).
       - prefers-reduced-motion: no-preference: under reduced motion the pin is
         dropped entirely, so the Home page scrolls normally as the spec requires.
     (This is intentionally stricter than the Contact .image-hero, whose CSS
     sticky pin stays under reduced motion — that #6 behaviour is untouched.) */
  @media (prefers-reduced-motion: no-preference) {
    .hero.is-pinstack {
      position: sticky;
      top: 0;
      z-index: 0;
    }
  }

/* === Image hero + pin&stack (reusable: Contact, Gallery, Shop) ===
   Component contract (markup in the page, behaviour in js/pin-stack.js):

     <section class="image-hero image-hero--shop" data-pinstack>
       <h1 class="image-hero__title">…</h1>
     </section>
     <main class="pin-content">…page content…</main>

   The photo URL lives on the per-page modifier class (.image-hero--shop, etc.)
   below — never inline — so this honours the no-inline-style rule.

   - .image-hero is a 50vh image banner with a dark veil so a light title clears
     WCAG AA on both light and dark themes (veil + photo are dark either way).
   - It pins (position: sticky) so the .pin-content panel scrolls up and stacks
     over it. The veil/photo are equally dark in both themes, so legibility holds.
   - JS adds .is-pinstack once it has taken over; CSS only ever provides the
     static 50vh banner + sticky pin. Without JS the banner is a normal 50vh
     block and the page scrolls normally.
   - The sticky pin is gated on prefers-reduced-motion: no-preference (see the
     rule right after this block). Under reduced motion the banner stays a plain
     static 50vh block (position: relative) and the page scrolls normally,
     honouring the motion standard — mirrors how .hero.is-pinstack is gated. */
  .image-hero {
    /* 50vh banner. svh/dvh track mobile URL-bar collapse; vh is the fallback. */
    height: 50vh;
    height: 50svh;
    /* The dark veil (gradient) sits over the photo so the title stays legible
       regardless of the image content underneath. */
    background-image:
      linear-gradient(180deg,
        color-mix(in srgb, var(--color-black) 45%, transparent),
        color-mix(in srgb, var(--color-black) 60%, transparent)),
      var(--image-hero-bg);
    background-size: cover;
    background-position: center;
    display: grid;
    place-items: center;
    text-align: center;
    /* Non-sticky fallback: relative keeps the z-index stacking context so
       .pin-content (z-index: 1) still rides above. The sticky pin is added
       only under prefers-reduced-motion: no-preference, below. */
    position: relative;
    z-index: 0;
    /* Safari compositing promotion: forces the sticky banner onto its own GPU
       layer so Safari caches it as a texture instead of repainting the two-layer
       cover background every frame while .pin-content scrolls over it. Does not
       break position: sticky nor the sibling stacking with .pin-content. */
    transform: translateZ(0);
  }
  /* Pin & stack: the banner sticks to the top so the content panel below can
     scroll up and cover it. Gated on no-preference so under reduced motion the
     banner stays static (relative) and the page scrolls normally. */
  @media (prefers-reduced-motion: no-preference) {
    .image-hero {
      position: sticky;
      top: 0;
    }
  }
  /* Light title on the dark veil — high contrast in both themes. A soft shadow
     guards the few light pixels of sky behind the veil. */
  .image-hero__title {
    margin: 0;
    color: var(--color-offwhite);
    padding-inline: var(--space);
    text-shadow: 0 2px 12px color-mix(in srgb, var(--color-black) 70%, transparent);
  }

  /* Per-page photo modifiers. Keeping the image URL on a class (not inline)
     honours the no-inline-style rule; #8/#11 add --gallery/--shop the same way. */
  .image-hero--contact { --image-hero-bg: url('/assets/images/contact.jpg'); }
  .image-hero--gallery { --image-hero-bg: url('/assets/images/gallery.jpg'); }
  .image-hero--shop { --image-hero-bg: url('/assets/images/shop.jpg'); }
  .image-hero--horses { --image-hero-bg: url('/assets/images/our-horses.jpg'); }

  /* The content panel rides above the pinned hero with its own opaque
     background, so it visibly stacks over the image as the user scrolls. */
  .pin-content {
    position: relative;
    z-index: 1;
    background: var(--bg);
  }

  /* Safari: promote the content panel to its own compositor layer so it reliably
     stacks ABOVE the sticky image hero while scrolling. Without this, WebKit can
     paint the composited sticky hero over the (non-composited) content and then
     snap it to the front — the "content goes behind the hero, then jumps in
     front" glitch. Scoped to main.pin-content so Home section panels are untouched. */
  main.pin-content { transform: translateZ(0); }

  @media (prefers-reduced-motion: reduce) {
    /* Static 50vh banner: no sticky pin (gated above), no transitions. */
    .image-hero { transition: none; }
  }

/* === About === */
  .about { display: grid; grid-template-columns: 1.22fr 3fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
  .about__media img { width: 100%; height: auto; aspect-ratio: 3 / 4; object-fit: cover; object-position: center 25%; border-radius: var(--radius); display: block; }
  @media (max-width: 760px) {
    .about { grid-template-columns: 1fr; }
    .about__media img { max-width: 420px; margin-inline: auto; }
  }

  /* About as a full-screen panel in the Home stack (Hero → About → Services).
     100dvh tracks mobile URL-bar collapse; 100vh is the fallback for browsers
     without dvh. Flex centers the .about grid vertically and horizontally
     inside the panel. The .section padding still applies so content never
     touches the edges on short viewports (the grid just centers within it). */
  .about-panel {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .about-panel > .about { width: 100%; }

  /* Pin & stack for About: it sticks to the top so the Services panel scrolls
     up and covers it — the same pin-only pattern as the Home hero. About keeps
     its opaque .pin-content background (z-index: 1) so it cleanly hides the
     pinned hero beneath it. Two gates keep the no-JS / reduced-motion fallbacks
     safe (identical reasoning to .hero.is-pinstack):
       - .is-pinstack: JS (pin-stack.js, pin-only mode) adds it only once it has
         taken over, so with no JS About stays a normal full-height block.
       - prefers-reduced-motion: no-preference: under reduced motion the pin is
         dropped, so the Home page scrolls normally. */
  @media (prefers-reduced-motion: no-preference) {
    .about-panel.is-pinstack {
      position: sticky;
      top: 0;
      /* Above the hero (z:0), below the Services panel (z:2). */
      z-index: 1;
    }
  }

/* === Services === */
  .services { background: var(--surface); transition: background-color 0.3s var(--ease-out); }
  .services__eyebrow {
    text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.8rem; font-weight: 700;
    color: var(--accent); margin: 0 0 0.5rem;
  }
  .services__intro { max-width: 60ch; margin: 0 0 0.5rem; font-size: 1.05rem; opacity: 0.9; }

  /* Services as a full-screen image panel — top of the Home stack.
     The photo carries a DARK veil (same gradient pattern as .image-hero) so the
     eyebrow/title/intro can switch to light text that clears WCAG AA in BOTH
     themes: the veil + photo are dark either way, so legibility never depends on
     the active theme. The white service cards then pop against the dark field.
     The URL lives on a class (no inline styles). */
  .services--panel {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Dark veil over the photo (overrides the flat var(--surface) background). */
    background-image:
      linear-gradient(180deg,
        color-mix(in srgb, var(--color-black) 65%, transparent),
        color-mix(in srgb, var(--color-black) 72%, transparent)),
      url('/assets/images/services.jpg');
    background-size: cover;
    background-position: center;
    /* Light copy on the dark veil. Cards keep their own --card background. */
    color: var(--color-offwhite);
  }
  .services--panel > .container { width: 100%; }
  /* Eyebrow/title/intro go light against the veil; a soft shadow guards any
     lighter pixels of the photo that bleed through the gradient. */
  .services--panel .services__eyebrow,
  .services--panel h2,
  .services--panel .services__intro {
    color: var(--color-offwhite);
    text-shadow: 0 2px 12px color-mix(in srgb, var(--color-black) 70%, transparent);
  }
  .services--panel .services__intro { opacity: 1; }
  .services--panel .services__intro strong { color: var(--color-offwhite); }

  /* Stack over the pinned About panel. Opaque is provided by the cover photo
     (no transparent gaps), and z-index: 2 sits above About (z:1) and the hero
     (z:0), so Services visibly rises and covers About as the user scrolls. */
  .services--panel.pin-content { z-index: 2; }

  /* Service cards as a responsive grid that spreads evenly across the full
     container width. The cards already use display:flex; height:100%, so the
     grid keeps every card the same height regardless of summary length.
     Mobile-first: scale columns up at wider breakpoints. */
  .services__grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: clamp(1rem, 2vw, 1.5rem);
    margin-top: 2rem;
  }
  @media (max-width: 900px) {
    .services__grid { grid-template-columns: repeat(2, 1fr); }
  }
  @media (max-width: 560px) {
    .services__grid { grid-template-columns: 1fr; }
  }

/* === Floating CTA (reusable) ===
   Fixed bottom-right call-to-action. Built as a reusable component because the
   Shop page (Activity #4) reuses the same pattern. It is a real <a> so it works
   with no JS (direct link to /contact.html); reserve.js toggles .is-visible as a
   scroll-driven panel enters/leaves the viewport and upgrades the click to a modal.

   Default state without JS = visible (so the link is never a dead end). When JS is
   present (html.js) the button starts hidden and only .is-visible reveals it. */
  .float-cta {
    position: fixed;
    right: clamp(1rem, 3vw, 2rem);
    bottom: clamp(1rem, 3vw, 2rem);
    z-index: 90;
    display: inline-flex; align-items: center; gap: 0.5rem;
    background: var(--accent); color: var(--color-offwhite);
    padding: 0.85rem 1.4rem; border-radius: 999px;
    font-family: var(--font-body); font-weight: 600; font-size: 1rem;
    text-decoration: none; border: 0; cursor: pointer;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--color-black) 28%, transparent);
    transition: transform 0.2s var(--ease-out), opacity 0.2s var(--ease-out);
  }
  .float-cta__icon { width: 20px; height: 20px; flex: 0 0 auto; }
  @media (hover: hover) and (pointer: fine) {
    .float-cta:hover { transform: translateY(-3px); box-shadow: 0 12px 30px color-mix(in srgb, var(--color-black) 36%, transparent); }
  }
  .float-cta:active { transform: scale(0.96); }

  /* JS present: hide until the observed panel scrolls into view. The hidden state
     slides/fades out toward the bottom-right; .is-visible animates it back in. */
  html.js .float-cta {
    opacity: 0;
    transform: translateY(24px) scale(0.9);
    pointer-events: none;
    visibility: hidden;
  }
  html.js .float-cta.is-visible {
    opacity: 1;
    transform: none;
    pointer-events: auto;
    visibility: visible;
  }
  /* Static variant (Shop / Activity #4): a plain direct link, not scroll-triggered.
     Opt out of the JS hide-until-visible behavior above so it stays visible at all
     times, with or without JS. */
  html.js .float-cta--static,
  .float-cta--static {
    opacity: 1;
    transform: none;
    pointer-events: auto;
    visibility: visible;
  }
  /* Grow-on-hover variant (Shop / Activity #4): the button scales up smoothly on
     hover. Scoped to its own modifier so the home "Reserve Your Spot" button (#10)
     keeps its translateY lift unchanged. Hover-only + reduced-motion safe (below). */
  @media (hover: hover) and (pointer: fine) {
    .float-cta--grow:hover {
      transform: scale(1.08);
      box-shadow: 0 12px 30px color-mix(in srgb, var(--color-black) 36%, transparent);
    }
    .float-cta--grow:focus-visible {
      transform: scale(1.08);
    }
  }
  /* Keep the press feedback consistent with the scaled-up resting hover state. */
  .float-cta--grow:active { transform: scale(1.04); }

  /* The fixed .float-cta is anchored bottom-right with a clamp(1rem,3vw,2rem)
     offset. On mobile it can overlap the last product row or the footer when the
     page is scrolled to the bottom. This reserves clearance below the content
     (button offset + ~button height) so nothing is ever hidden behind it. Opt-in
     via the modifier so only pages that carry the static float-cta pay the cost. */
  .has-float-cta { padding-bottom: calc(clamp(1rem, 3vw, 2rem) + 4rem); }

/* === Reserve modal === */
  /* Centered dialog, same overlay mechanics as the gallery lightbox: a fixed
     full-screen layer, dark backdrop, scale/fade entrance. role/aria + focus-trap
     are wired in reserve.js. [hidden] keeps it out of the layout until opened. */
  .reserve-modal {
    position: fixed; inset: 0; z-index: 150;
    display: flex; align-items: center; justify-content: center;
    padding: 1.5rem;
  }
  .reserve-modal[hidden] { display: none; }
  .reserve-modal__backdrop {
    position: absolute; inset: 0;
    background: rgba(20, 18, 16, 0.78);
    animation: reserveFade 0.25s ease both;
  }
  .reserve-modal__dialog {
    position: relative; z-index: 1;
    width: min(420px, 100%);
    background: var(--card); color: var(--text);
    border-radius: var(--radius);
    padding: 2.25rem 1.75rem 1.75rem;
    text-align: center;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
    animation: reserveZoom 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
  }
  .reserve-modal__msg {
    margin: 0 0 1.5rem; font-size: 1.15rem; line-height: 1.5;
  }
  .reserve-modal__msg strong { color: var(--accent); }
  .reserve-modal__close {
    position: absolute; top: 0.5rem; right: 0.6rem;
    width: 40px; height: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 0; cursor: pointer;
    color: var(--text); font-size: 1.75rem; line-height: 0;
    border-radius: 50%; padding-bottom: 4px;
    transition: background 0.18s ease, color 0.18s ease;
  }
  @media (hover: hover) and (pointer: fine) {
    .reserve-modal__close:hover { background: var(--accent); color: #fff; }
  }

  @keyframes reserveFade { from { opacity: 0; } to { opacity: 1; } }
  @keyframes reserveZoom { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }

  @media (prefers-reduced-motion: reduce) {
    /* No entrance/exit motion: the button snaps between hidden/visible and the
       modal opens without scale/fade. The button still shows by default w/o JS. */
    .float-cta { transition: none; }
    html.js .float-cta { transform: none; }
    html.js .float-cta.is-visible { transform: none; }
    /* The Shop grow-on-hover (#4) drops its scale when reduced motion is requested. */
    .float-cta--grow:hover,
    .float-cta--grow:focus-visible,
    .float-cta--grow:active { transform: none; }
    .reserve-modal__backdrop, .reserve-modal__dialog { animation: none; }
  }

/* === ServiceCard === */
  .card {
    background: var(--card);
    /* Pin the card's text to --text so it stays dark on the white card even when
       the card sits inside a light-on-dark panel (e.g. .services--panel), where
       it would otherwise inherit the panel's light --color-offwhite text. */
    color: var(--text);
    border: 1px solid var(--border);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--color-black) 8%, transparent);
    border-radius: var(--radius);
    padding: 1.75rem 1.5rem;
    display: flex; flex-direction: column; height: 100%;
    border-top: 4px solid var(--accent);
    transition: transform 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out),
                background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
  }
  @media (hover: hover) and (pointer: fine) {
    .card:hover { transform: translateY(-4px); box-shadow: 0 12px 28px color-mix(in srgb, var(--color-black) 14%, transparent); }
  }
  .card__icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; border-radius: 50%;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent); margin-bottom: 1rem;
  }
  .card__icon svg { width: 28px; height: 28px; }
  .card__title { margin: 0 0 0.35rem; font-size: 1.5rem; }
  .card__price { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
  .card__amount { font-family: var(--font-display); font-size: 1.9rem; font-weight: 700; color: var(--accent); line-height: 1; }
  .card__unit { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.7; }
  .card__summary { margin: 0; flex: 1; opacity: 0.9; }

/* === Filterable cards (card-filter.js) === */
  /* card-filter.js hides cards by setting `el.hidden = true`. The UA rule
     `[hidden] { display: none }` has near-zero specificity, so `.horse` and
     `.product` (both `display: flex`) win and the card stays visible — the
     search box and chips appear to do nothing. !important here is the
     intended escape hatch: a hidden filter item must never render, regardless
     of the card's own display value. Scoped to [data-filter-item] so it only
     affects filterable cards and stays reusable across Our Horses and Shop. */
  [data-filter-item][hidden] { display: none !important; }

/* === HorseCard === */
  .horse {
    background: var(--card); border-radius: var(--radius); overflow: hidden;
    display: flex; flex-direction: column; height: 100%;
    border: 1px solid var(--border);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--color-black) 8%, transparent);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
  }
  .horse:hover { transform: translateY(-4px); box-shadow: 0 12px 28px color-mix(in srgb, var(--color-black) 14%, transparent); }
  .horse__media { position: relative; }
  .horse__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
  .horse__badge {
    position: absolute; top: 0.75rem; left: 0.75rem;
    background: var(--accent); color: #fff;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
    padding: 0.3rem 0.6rem; border-radius: 999px;
    box-shadow: 0 1px 4px color-mix(in srgb, var(--color-black) 25%, transparent);
  }
  /* For Sale keeps the burgundy accent; In Training uses a distinct teal token. */
  .horse__badge--sale { background: var(--accent); }
  .horse__badge--training { background: var(--badge-training); }
  .horse__body { padding: 1.1rem 1.25rem 1.25rem; display: flex; flex-direction: column; gap: 0.6rem; flex: 1; }
  .horse__head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
  .horse__name { margin: 0; font-size: 1.6rem; }
  .horse__price { font-family: var(--font-display); font-size: 1.35rem; font-weight: 700; color: var(--accent); }
  .horse__desc { margin: 0; flex: 1; opacity: 0.9; font-size: 0.95rem; }
  .horse__cta { margin-top: 0.5rem; align-self: stretch; text-align: center; }
  /* For Sale "Inquire About" buttons rendered 5% smaller. */
  .horse__cta--sm { transform: scale(0.95); transform-origin: center; }
  .horse__cta--sm:hover { transform: scale(0.95) translateY(-2px); }

/* === ProductCard === */
  .product { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; }
  /* Positioning context for the corner "Best Seller" badge; keeps the badge
     over the image without affecting the card's flex layout. */
  .product__media { position: relative; }
  .product__media img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; display: block; }
  .product__badge {
    position: absolute; top: 0.75rem; left: 0.75rem;
    background: var(--badge-bestseller); color: var(--badge-bestseller-text);
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
    padding: 0.3rem 0.6rem; border-radius: 999px;
    box-shadow: 0 1px 4px color-mix(in srgb, var(--color-black) 25%, transparent);
  }
  /* flex: 1 is intentional: it lets the body stretch to fill its card so that
     cards in the same grid row share equal heights regardless of how much copy
     each product has. */
  .product__body { padding: 1.1rem 1.25rem 1.25rem; display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
  .product__name { margin: 0; font-size: 1.3rem; }
  .product__price { font-family: var(--font-display); font-size: 1.25rem; color: var(--accent); font-weight: 700; }
  .product__desc { white-space: pre-line; margin: 0; font-size: 0.9rem; line-height: 1.5; opacity: 0.9; }

/* === Contact === */
  .contact { background: var(--contact-bg); transition: background-color 0.3s var(--ease-out); }
  .contact__grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: clamp(1.5rem, 4vw, 3rem); margin-top: 2rem; align-items: start; }
  .contact__formcard { padding: clamp(1.25rem, 3vw, 1.75rem); }
  .contact__form { display: grid; gap: 1rem; }
  .contact__form label { display: grid; gap: 0.35rem; font-weight: 600; }
  .contact__form input, .contact__form textarea { padding: 0.65rem; border: 1px solid color-mix(in srgb, var(--text) 25%, transparent); border-radius: var(--radius); font: inherit; background: var(--surface); color: var(--text); transition: background-color 0.3s var(--ease-out), border-color 0.18s var(--ease-out), color 0.3s var(--ease-out); }

  .contact__side { display: grid; gap: 1rem; align-content: start; }
  .contact__cta { display: flex; align-items: center; justify-content: center; gap: 0.6rem; text-align: center; }
  /* Phone icon "rings" (shakes from its top) on hover to reinforce the call action. */
  .contact__cta-icon { width: 20px; height: 20px; fill: currentColor; flex: none; transform-origin: 50% 0; }
  @media (hover: hover) and (pointer: fine) {
    .contact__cta:hover .contact__cta-icon { animation: cta-ring 0.5s var(--ease-out); }
  }
  @keyframes cta-ring {
    0%   { transform: rotate(0); }
    15%  { transform: rotate(-12deg); }
    30%  { transform: rotate(10deg); }
    45%  { transform: rotate(-8deg); }
    60%  { transform: rotate(6deg); }
    75%  { transform: rotate(-4deg); }
    100% { transform: rotate(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .contact__cta:hover .contact__cta-icon { animation: none; }
  }

  .ccard {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.1rem 1.25rem;
    box-shadow: 0 1px 3px color-mix(in srgb, var(--color-black) 8%, transparent);
    display: grid; gap: 0.7rem;
    transition: background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
  }
  .ccard__title { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700; color: var(--accent); }
  .ccard__row { display: flex; align-items: center; gap: 0.7rem; text-decoration: none; color: var(--text); font-weight: 600; word-break: break-word; }
  .ccard__row:not(.ccard__row--static):hover { color: var(--accent); }
  .ccard__rowicon {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--accent); color: #fff;
  }
  .ccard__rowicon svg { width: 18px; height: 18px; fill: currentColor; }
  .ccard__rowicon--line svg { fill: none; stroke: currentColor; stroke-width: 2; }

  .ccard--link { text-decoration: none; transition: transform 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out), border-color 0.18s var(--ease-out), background-color 0.3s var(--ease-out); }
  @media (hover: hover) and (pointer: fine) {
    .ccard--link:hover { transform: translateY(-3px); box-shadow: 0 10px 24px color-mix(in srgb, var(--color-black) 14%, transparent); border-color: color-mix(in srgb, var(--color-burgundy) 40%, transparent); }
  }
  .ccard--link:active { transform: translateY(-1px) scale(0.99); }

  @media (max-width: 760px) { .contact__grid { grid-template-columns: 1fr; } }

/* === Gallery page === */
  /* Ported from Gallery.astro's scoped <style> (the intro paragraph + the
     empty state when no photos are present). */
  .gallery__intro { max-width: 60ch; opacity: 0.9; margin: 0 0 2rem; }
  .gallery__empty {
    max-width: 55ch; margin-top: 1rem; padding: 1.5rem 1.75rem;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--card);
  }

/* === GalleryLightbox === */
        .gallery__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0.75rem; }
        /* On phones the 180px min-track only fits one column (two would need
           ~372px + gap), leaving a single tall stack. Force exactly two columns
           at the project's 760px mobile breakpoint so the grid reads as a gallery.
           Each cell is then ~165px CSS (≈330px at 2x DPR); the 700px thumbnails
           cover that with room to spare, so they stay crisp. Desktop is untouched:
           above 760px the auto-fill track above keeps governing. */
        @media (max-width: 760px) {
          .gallery__grid { grid-template-columns: repeat(2, 1fr); }
        }
        .gallery__thumb { border: 0; padding: 0; cursor: pointer; background: none; border-radius: var(--radius); overflow: hidden; aspect-ratio: 1; }
        .gallery__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; }
        .gallery__thumb:hover img { transform: scale(1.06); }

        .gallery__overlay {
          position: fixed; inset: 0; z-index: 100;
          background: rgba(20, 18, 16, 0.92);
          display: flex; align-items: center; justify-content: center; padding: 1.5rem;
          animation: galleryFade 0.25s ease both;
        }
        .gallery__full {
          max-width: 92vw; max-height: 88vh; border-radius: var(--radius);
          box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
          animation: galleryZoom 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
        }

        .gallery__nav, .gallery__close {
          position: fixed; display: inline-flex; align-items: center; justify-content: center;
          color: #fff; background: rgba(0, 0, 0, 0.45); border: 0; cursor: pointer;
          border-radius: 50%; line-height: 0;
          transition: background 0.18s ease, transform 0.18s ease;
        }
        .gallery__nav { top: 50%; transform: translateY(-50%); width: 48px; height: 48px; font-size: 2rem; padding-bottom: 4px; }
        .gallery__nav:hover { background: var(--accent); }
        .gallery__nav--prev { left: clamp(0.5rem, 2vw, 1.5rem); }
        .gallery__nav--next { right: clamp(0.5rem, 2vw, 1.5rem); }
        .gallery__close { top: 1rem; right: 1.25rem; width: 44px; height: 44px; font-size: 2rem; padding-bottom: 4px; }
        .gallery__close:hover { background: var(--accent); }

        .gallery__count {
          position: fixed; bottom: 1.25rem; left: 50%; transform: translateX(-50%);
          color: #fff; font-size: 0.9rem; letter-spacing: 0.05em; opacity: 0.85;
        }

        @keyframes galleryFade { from { opacity: 0; } to { opacity: 1; } }
        @keyframes galleryZoom { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }

        @media (prefers-reduced-motion: reduce) {
          .gallery__overlay, .gallery__full { animation: none; }
          .gallery__thumb img { transition: none; }
          .gallery__thumb:hover img { transform: none; }
        }

/* === Our Horses page === */
  .page__eyebrow { text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.8rem; font-weight: 700; color: var(--accent); margin: 0 0 0.5rem; }
  .page__intro { max-width: 65ch; opacity: 0.9; }
  .horses__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 2rem; }
  .horses__empty { max-width: 55ch; margin-top: 2rem; padding: 1.5rem 1.75rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); }

/* === Card filter bar (search + chips) — shared by Our Horses & Shop === */
  /* Visually hidden but available to screen readers (labels for the search). */
  .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;
  }
  .cardbar {
    margin-top: 1.75rem;
    display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
    justify-content: space-between;
  }
  .cardbar__search { position: relative; flex: 1 1 18rem; min-width: 0; }
  .cardbar__searchicon {
    position: absolute; left: 0.85rem; top: 50%; transform: translateY(-50%);
    width: 18px; height: 18px; opacity: 0.55; pointer-events: none;
  }
  .cardbar__input {
    width: 100%; padding: 0.7rem 0.9rem 0.7rem 2.6rem;
    font: inherit; color: var(--text);
    background: var(--surface);
    border: 1px solid color-mix(in srgb, var(--text) 25%, transparent);
    border-radius: 999px;
    transition: background-color 0.3s var(--ease-out), border-color 0.18s var(--ease-out), color 0.3s var(--ease-out);
  }
  .cardbar__input::placeholder { color: color-mix(in srgb, var(--text) 55%, transparent); }
  .cardbar__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--accent); }

  .chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .chip {
    font: inherit; font-size: 0.85rem; font-weight: 600;
    padding: 0.5rem 1rem; border-radius: 999px; cursor: pointer;
    color: var(--text);
    background: var(--card);
    border: 1px solid color-mix(in srgb, var(--text) 22%, transparent);
    transition: background-color 0.18s var(--ease-out), border-color 0.18s var(--ease-out), color 0.18s var(--ease-out), transform 0.16s var(--ease-out);
  }
  @media (hover: hover) and (pointer: fine) {
    .chip:hover { border-color: var(--accent); color: var(--accent); }
  }
  .chip:active { transform: scale(0.97); }
  /* Active chip: solid accent fill, white text (clears AA in both themes). */
  .chip[aria-pressed='true'] {
    background: var(--accent); color: #fff; border-color: var(--accent);
  }
  @media (hover: hover) and (pointer: fine) {
    .chip[aria-pressed='true']:hover { color: #fff; }
  }
  @media (prefers-reduced-motion: reduce) {
    .chip { transition: background-color 0.18s var(--ease-out), border-color 0.18s var(--ease-out), color 0.18s var(--ease-out); }
    .chip:active { transform: none; }
  }

/* === Shop page === */
  .shop__intro { max-width: 65ch; opacity: 0.9; }
  .shop__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1.25rem; margin-top: 2rem; }
  .shop__empty { max-width: 55ch; margin-top: 2rem; padding: 1.5rem 1.75rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); }
