/* =========================================================================
   Adoptable animals (live ShelterLuv feed) - cards + skeleton loader
   ========================================================================= */
.animals { display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px; }
.animal {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm);
  color: var(--ink); transition: transform .18s ease, box-shadow .18s ease;
}
.animal:hover { transform: translateY(-5px); box-shadow: var(--shadow); color: var(--ink); }
.animal__photo {
  aspect-ratio: 4 / 3; background: var(--tan) center/cover no-repeat;
}
.animal__photo--none { display: grid; place-items: center; font-size: 2.6rem; }
.animal__body { padding: 17px 19px 21px; }
.animal__name { margin: 0 0 2px; font-size: 1.3rem; color: var(--teal-dark); }
.animal__meta { margin: 0 0 4px; color: var(--muted); font-size: .92rem; }
.animal__age { margin: 0; color: var(--ink); font-size: .9rem; font-weight: 700; }
.animal__chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 11px 0 13px; }
.chip {
  background: var(--teal-soft); color: var(--teal-dark); font-family: var(--font-body);
  font-size: .7rem; font-weight: 800; letter-spacing: .02em; padding: 4px 10px; border-radius: 999px;
}
.animal__cta { color: var(--amber-dark); font-weight: 800; font-size: .92rem; }

/* Featured "longest waiting" pet */
.featured {
  display: grid; grid-template-columns: minmax(0, 46%) 1fr; align-items: stretch;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow);
}
/* The featured photo is now the same swipeable strip the browse cards use.
   It used to be an empty div with a background-image, which is why it showed
   nothing on mobile: an empty element in a stretched grid row can resolve to
   zero height, and there was no intrinsic content to stop it. Real <img>
   slides cannot collapse. */
.featured__media { min-height: 360px; background: var(--tan); }
.featured__media .animal__strip { height: 100%; }
.featured__media .animal__slide img { height: 100%; min-height: 360px; aspect-ratio: auto; }
.featured__photo { min-height: 360px; background: var(--tan) center/cover no-repeat; }
.featured__body { padding: 40px 44px; align-self: center; }
.featured__badge {
  display: inline-block; background: var(--amber-soft); color: var(--amber-dark);
  font-weight: 800; text-transform: uppercase; letter-spacing: .06em; font-size: .74rem;
  padding: 5px 13px; border-radius: 999px; margin-bottom: 14px;
}
.featured__name { font-size: 2rem; margin: 0 0 4px; }
.featured__meta { color: var(--muted); font-weight: 700; margin: 0 0 6px; }
.featured__wait { color: var(--teal-dark); font-weight: 800; margin: 0 0 12px; }
.featured .btn { margin-top: 8px; }
@media (max-width: 760px) {
  .featured { grid-template-columns: 1fr; }
  .featured__photo { min-height: 0; aspect-ratio: 4 / 3; }
  /* Match the browse cards on narrow screens: 4:3, driven by the image itself
     rather than by a min-height that would crop a portrait photo badly. */
  .featured__media { min-height: 0; }
  .featured__media .animal__slide img { height: auto; min-height: 0; aspect-ratio: 4 / 3; }
  .featured__body { padding: 30px 26px; }
}

/* Tough's photo strip. Three shots, equal height, no cropping surprises. */
.tough__gallery {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 28px;
}
.tough__shot { margin: 0; }
.tough__shot img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block;
  border-radius: var(--radius); background: var(--tan);
}
@media (max-width: 640px) {
  .tough__gallery { grid-template-columns: repeat(2, 1fr); }
  .tough__gallery .tough__shot:last-child { grid-column: 1 / -1; }
  .tough__gallery .tough__shot:last-child img { aspect-ratio: 16 / 9; }
}

/* Event cards */
.event-date {
  display: inline-block; background: var(--amber-soft); color: var(--amber-dark);
  font-family: var(--font-body); font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  font-size: .74rem; padding: 5px 12px; border-radius: 999px;
}
.event-meta { color: var(--muted); font-size: .92rem; }
.animals__empty { grid-column: 1 / -1; text-align: center; padding: 30px 10px; color: var(--muted); }
.animals__empty p { font-family: var(--font-accent); font-size: 1.1rem; margin-bottom: 16px; }
.animal--sk { pointer-events: none; }
.animal--sk .animal__photo { animation: pulse 1.3s ease-in-out infinite; }
.sk-line { height: 13px; border-radius: 6px; background: var(--line); margin: 10px 0; animation: pulse 1.3s ease-in-out infinite; }
.sk-line--sm { width: 55%; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .5; } }
@media (max-width: 920px) { .animals { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .animals { grid-template-columns: 1fr; } }

/* =========================================================================
   LAWS website demo - shared design system
   One stylesheet drives every page. Colors, type, and spacing are tokens at
   the top so the whole site can be re-skinned by editing a handful of values.
   ========================================================================= */

/* Brand fonts, all self-hosted so the site needs NO external font CDN.
   Headline: KG HAPPY Solid (Kimberly Geswein, LAWS holds a commercial license).
   Body: Gilroy (Radomir Tinkov free weights - Light + ExtraBold; LAWS-licensed).
   Accent serif: Lora (SIL Open Font License). */
@font-face {
  font-family: "KG HAPPY Solid";
  src: url("/assets/fonts/kghappy-solid.woff2") format("woff2"),
       url("/assets/fonts/kghappy-solid.woff") format("woff");
  font-weight: 400 700; font-style: normal; font-display: swap;
}
/* Full Gilroy family, self-hosted (LAWS-licensed): one face per weight. */
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-300.woff2") format("woff2"); font-weight:300; font-style:normal; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-400.woff2") format("woff2"); font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-400i.woff2") format("woff2"); font-weight:400; font-style:italic; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-500.woff2") format("woff2"); font-weight:500; font-style:normal; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-600.woff2") format("woff2"); font-weight:600; font-style:normal; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-700.woff2") format("woff2"); font-weight:700; font-style:normal; font-display:swap; }
@font-face { font-family:"Gilroy"; src:url("/assets/fonts/gilroy-800.woff2") format("woff2"); font-weight:800; font-style:normal; font-display:swap; }
@font-face { font-family: "Lora"; src: url("/assets/fonts/lora-500.woff2") format("woff2"); font-weight: 400 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Lora"; src: url("/assets/fonts/lora-600.woff2") format("woff2"); font-weight: 600 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Lora"; src: url("/assets/fonts/lora-500i.woff2") format("woff2"); font-weight: 400 500; font-style: italic; font-display: swap; }

:root {
  /* Palette from the LAWS Brand Manual (Prairie Sage Creative) */
  --teal:        #179c9b;   /* Main color, R23 G156 B155, pulled from the logo */
  --teal-dark:   #127e7d;   /* derived deeper teal for hover / dark sections */
  --teal-soft:   #e2f1f0;
  /* terracotta = brand "supporting warmer" accent (kept under --amber* token names) */
  --amber:       #d75f3f;
  --amber-dark:  #bc4e30;
  --amber-soft:  #f6e2d7;
  --cream:       #f7f4ee;   /* brand off-white page background */
  --tan:         #ebe4d6;   /* brand neutral tan, alternating sections */
  --brown:       #3a352e;   /* brand supporting dark brown, footer */
  --gray:        #595950;   /* Secondary color, Pantone 425C */
  --surface:     #ffffff;
  --ink:         #33302b;   /* warm near-black body text */
  --muted:       #6b6459;
  --line:        #e6ddcd;

  --maxw: 1140px;
  --radius: 16px;
  --radius-sm: 10px;
  --shadow: 0 14px 40px rgba(58, 53, 46, 0.12);
  --shadow-sm: 0 6px 18px rgba(58, 53, 46, 0.09);

  /* Headline: KG HAPPY Solid (brand) -> Fredoka, closest free rounded match.
     Body: Gilroy (brand) -> Montserrat.  Accent: Kefa serif (brand) -> Lora. */
  --font-display: "KG HAPPY Solid", "Fredoka", "Trebuchet MS", sans-serif;
  --font-body: "Gilroy", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-accent: "Lora", Georgia, serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--cream);
  line-height: 1.65;
  font-size: 18px;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

a { color: var(--teal); text-decoration: none; }
a:hover { color: var(--teal-dark); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.14;
  color: var(--teal-dark);
  margin: 0 0 0.5em;
  letter-spacing: 0;
}
h1 { font-size: clamp(2.3rem, 5vw, 3.6rem); }
h2 { font-size: clamp(1.7rem, 3.4vw, 2.5rem); }
h3 { font-size: 1.3rem; color: var(--teal-dark); }

p { margin: 0 0 1.1em; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }

.section { padding: 84px 0; }
.section--tight { padding: 56px 0; }
.section--cream { background: var(--tan); }
.section--surface { background: var(--surface); }
.section--teal { background: var(--teal); color: #eaf4f2; }
.section--teal h2, .section--teal h3 { color: #fff; }

.eyebrow {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.78rem;
  color: var(--amber-dark);
  margin-bottom: 14px;
}
.section--teal .eyebrow { color: var(--amber-soft); }

.lead { font-family: var(--font-accent); font-size: 1.22rem; line-height: 1.55; color: var(--muted); max-width: 62ch; }
.center { text-align: center; }
.center .lead { margin-left: auto; margin-right: auto; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 1rem;
  padding: 14px 26px;
  border-radius: 999px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn--amber { background: var(--amber); color: #fff; box-shadow: var(--shadow-sm); }
.btn--amber:hover { background: var(--amber-dark); color: #fff; }
.btn--teal { background: var(--teal-dark); color: #fff; }
.btn--teal:hover { background: #0e6b6a; color: #fff; }
.btn--ghost { background: transparent; border-color: currentColor; color: var(--teal-dark); }
.btn--ghost:hover { background: var(--teal); color: #fff; border-color: var(--teal); }
.btn--white { background: #fff; color: var(--teal-dark); }
.btn--white:hover { background: var(--amber-soft); color: var(--teal-dark); }
.btn--lg { padding: 17px 34px; font-size: 1.08rem; }

/* ---------- Top utility bar ---------- */
.topbar {
  background: var(--teal-dark);
  color: #d9ebe8;
  font-size: 0.85rem;
  font-weight: 600;
}
.topbar .wrap {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; padding-top: 8px; padding-bottom: 8px; flex-wrap: wrap;
}
.topbar a { color: #fff; }
.topbar__hours { opacity: .85; }
.topbar__social a { margin-left: 12px; font-weight: 800; letter-spacing: .03em; }

/* ---------- Header / nav ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(255,255,255,0.96);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.site-header .wrap {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 14px; padding-bottom: 14px; gap: 18px;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand img { height: 46px; width: auto; }
.brand__name { display: none; }

.nav { display: flex; align-items: center; gap: 4px; }
.nav a {
  font-weight: 700; color: var(--ink); padding: 10px 14px; border-radius: 10px;
  font-size: 0.98rem;
}
.nav a:hover { background: var(--teal-soft); color: var(--teal-dark); }
.nav a.is-active { color: var(--teal); }

.nav__group { position: relative; }
.nav__group > span {
  font-weight: 700; padding: 10px 14px; border-radius: 10px; cursor: default;
  display: inline-block;
}
.nav__group:hover > span { background: var(--teal-soft); color: var(--teal-dark); }
.nav__menu {
  position: absolute; top: 100%; left: 0; min-width: 220px;
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  box-shadow: var(--shadow); padding: 8px; display: none;
}
.nav__group:hover .nav__menu { display: block; }
.nav__menu a { display: block; padding: 9px 12px; border-radius: 8px; }

.header__cta { display: flex; align-items: center; gap: 10px; }

/* mobile nav toggle (CSS-only) */
.nav-toggle, .nav-toggle-label { display: none; }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  color: #fff;
  background-color: #179c9b;
  background-image: linear-gradient(rgba(34,31,27,0.52), rgba(34,31,27,0.66)), var(--hero-img, none);
  background-size: cover, cover;
  /* image layer honors --hero-pos so a subject (e.g. a dog's face) isn't cropped out */
  background-position: center center, var(--hero-pos, center center);
  background-repeat: no-repeat, no-repeat;
}
.hero .wrap { padding-top: 96px; padding-bottom: 104px; }
.hero h1 { color: #fff; max-width: 16ch; }
.hero p { font-size: 1.25rem; max-width: 46ch; color: #f1f7f5; }
.hero__actions { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 26px; }
.hero--page .wrap { padding-top: 70px; padding-bottom: 70px; }
.hero--page h1 { max-width: 22ch; }

/* Solid brand-color hero for text/utility pages (no photo needed) */
.hero--solid {
  background: linear-gradient(135deg, var(--teal) 0%, var(--teal-dark) 100%);
}
.hero--solid::after {
  content: ""; position: absolute; inset: 0; opacity: .5;
  background: radial-gradient(circle at 82% 18%, rgba(255,255,255,.14), transparent 45%);
}
.hero--solid .wrap { position: relative; z-index: 1; }

/* =========================================================================
   Rotating homepage hero
   Art-directed per breakpoint. Two crops per photo (16:9 desktop, 4:5 phone)
   swapped by <picture>, so the phone downloads a picture that was actually
   composed for a phone instead of a landscape shot squeezed into a portrait
   hole. See HERO_SLIDES in build.py for how the crops are made.
   ========================================================================= */
.hero--home {
  position: relative; overflow: hidden; background: var(--teal-dark);
  display: flex; align-items: center; min-height: min(72svh, 620px);
}
.hero--home > .wrap { position: relative; z-index: 2; width: 100%; }
.hero__slides { position: absolute; inset: 0; z-index: 0; }
.hero__slide {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .85s ease;
  will-change: opacity;
}
.hero__slide.is-active { opacity: 1; }
.hero__slide picture, .hero__slide img {
  display: block; width: 100%; height: 100%;
}
.hero__slide img {
  object-fit: cover;
  /* The hero box is usually wider than 16:9, so cover trims height. Biasing
     the window slightly above center keeps heads in frame. */
  object-position: center 42%;
}
/* Slow drift on the slide being shown. Restarts every time the class lands,
   which is what we want: each photo gets the same move from the same start. */
.hero__slide.is-active img { animation: heroDrift 8.6s ease-out both; }
@keyframes heroDrift { from { transform: scale(1.005); } to { transform: scale(1.055); } }

/* Readability scrim. On desktop the copy is a left-aligned column, so the
   gradient runs left to right and leaves the right side of the photo bright.
   On phones the copy is pinned to the bottom, so it runs top to bottom. */
.hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Only as dark as the type needs. The earlier version stacked a flat
     vertical wash on top of this one, which took the whole photograph down
     with it; the copy sits left, so the darkness should too. */
  background: linear-gradient(96deg,
    rgba(26,23,20,.84) 0%, rgba(26,23,20,.72) 30%, rgba(26,23,20,.42) 56%,
    rgba(26,23,20,.16) 80%, rgba(26,23,20,.08) 100%);
}

/* ---- Controls: one pill holding the pause toggle and the photo dots ----
   WCAG 2.2.2 needs a real pause mechanism and the W3C carousel note is
   explicit that dots alone do not count, because picking a slide is not the
   same as stopping playback. Rather than park a lone circle over the corner
   of the photo, both controls live together in the content column where they
   read as part of the layout. Built by script, so a no-JS browser is never
   shown a control that cannot do anything; the space is reserved either way
   to keep the hero from jumping on load. */
.hero__controls {
  display: flex; align-items: center; gap: 12px;
  margin-top: 30px; min-height: 40px; visibility: hidden;
}
.hero__controls.is-ready { visibility: visible; }
.hero__toggle {
  flex: none; width: 40px; height: 40px; padding: 0; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer; color: #fff;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.45);
  backdrop-filter: blur(3px); transition: background .15s ease, border-color .15s ease;
}
.hero__toggle svg { width: 14px; height: 14px; fill: currentColor; }
.hero__toggle:hover { background: rgba(255,255,255,.28); border-color: #fff; }
.hero__toggle:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.hero__dots { display: flex; align-items: center; gap: 4px; }
/* 40px tall hit area with a small visible bar inside it: a thumb gets a
   target it can actually hit without the design turning into big buttons. */
.hero__dot {
  width: 26px; height: 40px; padding: 0; border: 0; background: none;
  cursor: pointer; display: grid; place-items: center;
}
.hero__dot::before {
  content: ""; display: block; width: 20px; height: 4px; border-radius: 999px;
  background: rgba(255,255,255,.42); transition: background .2s ease, width .2s ease;
}
.hero__dot:hover::before { background: rgba(255,255,255,.75); }
.hero__dot.is-on::before { background: #fff; width: 26px; }
.hero__dot:focus-visible { outline: 3px solid #fff; outline-offset: -4px; border-radius: 6px; }

/* Pausing should stop everything that moves, drift included. */
.hero--home.is-paused .hero__slide img { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .hero__slide { transition: none; }
  .hero__slide.is-active img { animation: none; }
}

/* ---- Mid widths: keep the hero box near 16:9 so cover trims almost nothing.
   A tall hero on an 850px screen is a 1.3-aspect box holding a 1.78-aspect
   photo, which throws away a fifth of the width off each side and takes the
   animal with it. Shortening the box is the fix, not a different crop. ---- */
@media (min-width: 761px) and (max-width: 1100px) {
  .hero--home { min-height: min(64svh, 520px); }
  .hero--home > .wrap { padding-top: 62px; padding-bottom: 68px; }
  .hero--home h1 { font-size: clamp(2.1rem, 4.4vw, 2.9rem); }
}

/* ---- Phones: the photo gets its own block, the copy gets its own panel ----
   See the note above HERO_SLIDES in build.py for why overlaying does not work
   on these particular photographs. The square crop is shown whole: no text
   over it, no cropping by the layout, nothing to compose around. */
@media (max-width: 760px) {
  .hero--home {
    display: block; min-height: 0; overflow: hidden;
    background: linear-gradient(165deg, var(--teal) 0%, var(--teal-dark) 100%);
    background-image: linear-gradient(165deg, var(--teal) 0%, var(--teal-dark) 100%);
  }
  .hero__slides {
    position: relative; inset: auto; width: 100%;
    aspect-ratio: 1 / 1; max-height: 54svh; overflow: hidden;
  }
  /* If a short viewport caps the square, trim from the bottom: in all eight
     photos the animal sits above center and the floor is what is expendable. */
  .hero--home .hero__slide img { object-position: center 38%; }
  /* No drift on a phone: the square is shown at exactly its own aspect ratio,
     so any zoom would start cropping the photo the layout just protected. */
  .hero--home .hero__slide.is-active img { animation: none; transform: none; }
  .hero__scrim { display: none; }

  /* Sized against a measurement, not a guess: on a 390x844 phone the topbar
     and header take 136px and the square photo takes 390, which leaves 318
     for the copy if "Meet adoptable pets" is to sit above the fold. Stacked
     full-width buttons alone cost 163px of that, so they go side by side. */
  .hero--home > .wrap { padding: 22px 24px 26px; }
  .hero--home .eyebrow { margin-bottom: 10px; }
  .hero--home h1 { font-size: clamp(1.6rem, 6.7vw, 2.1rem); max-width: 20ch; margin-bottom: 10px; }
  .hero--home p { font-size: .96rem; line-height: 1.5; margin-bottom: 0; }
  .hero__actions { margin-top: 16px; gap: 10px; flex-wrap: nowrap; }
  .hero__actions .btn {
    flex: 1 1 0; min-width: 0; margin: 0; text-align: center;
    /* Measured: at 14.7px the label is 148px wide inside a 146px content box,
       so it wrapped to two lines and cost 30px of height. Scale the label with
       the viewport instead of picking one size and hoping. */
    padding: 13px 8px; font-size: clamp(.8rem, 3.6vw, .95rem);
  }

  /* Riding on the photo, bottom left, on a pill dark enough to hold up over
     grass, snow or a sunlit window. Costs no vertical space, which is the
     whole point: it keeps the buttons above the fold on a 390x844 phone. */
  .hero__slides .hero__controls {
    position: absolute; z-index: 3; left: 14px; bottom: 12px; margin: 0;
    min-height: 0; gap: 2px; padding: 3px 8px 3px 3px; border-radius: 999px;
    background: rgba(20,18,16,.46);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  }
  /* Before the script runs the mount is still in the copy column. Take it out
     of flow there so nothing jumps when it moves. */
  .hero--home > .wrap .hero__controls { position: absolute; min-height: 0; }
  .hero__toggle { width: 30px; height: 30px; border-color: rgba(255,255,255,.35); }
  .hero__toggle svg { width: 11px; height: 11px; }
  .hero__dot { width: 18px; height: 30px; }
  .hero__dot::before { width: 12px; height: 3px; }
  .hero__dot.is-on::before { width: 18px; }
}
/* ---- Phones: center the short-form blocks ----
   Ian, 19 Aug 2026: "some buttons and cards that need text centered on mobile."
   On a phone every section heading is centered while the hero copy and every
   card body were flush left, which reads as an accident rather than a choice.
   Scoped deliberately to SHORT blocks. Multi-paragraph prose (the .split
   bodies, article text, the mission statement) stays left aligned, because a
   centered six-line paragraph gives the eye no reliable left edge to return
   to and measurably slows reading. */
@media (max-width: 760px) {
  .hero--home > .wrap { text-align: center; }
  .hero--home h1, .hero--home p { margin-left: auto; margin-right: auto; }
  .hero--home .hero__actions { justify-content: center; }

  .card { text-align: center; }
  .card__icon { margin-left: auto; margin-right: auto; }
  .card .btn { margin-top: 4px; }
  /* the five-up row goes single-column here, so its bottom-pinned button has to
     re-centre with the rest of the card body */
  .grid--5 .card .btn { align-self: center; }

  /* Two opt-outs, both found by looking rather than guessing. A board bio runs
     about a dozen lines, and the emergency directory is a table of names,
     phone numbers, hours and addresses wearing a list costume. Centering
     either makes it harder to use, which is the opposite of the point. */
  .card--person .card__body p { text-align: left; }
  .card:has(.reslist) { text-align: left; }
  .reslist, .reslist li, .emerg__top, .emerg__row, .emerg__note { text-align: left; }
  /* A card holding a list is a card holding structure. Bulleted requirements
     with bold lead-ins turn to soup when every line starts somewhere different. */
  .card ul, .card ol, .card dl, .card li { text-align: left; }
}

@media (max-width: 380px) {
  .hero--home > .wrap { padding: 20px 18px 24px; }
  .hero--home p { font-size: .93rem; }
  .hero__actions .btn { padding: 12px 7px; }
}

/* ---------- Grid + cards ---------- */
.grid { display: grid; gap: 26px; }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
/* Five-up "ways to change a life" row. At 1140px a fifth column leaves each
   card ~200px wide, so the row gets its own tighter gap, padding and icon
   rather than squeezing the four-up card into a space it was not drawn for. */
.grid--5 { grid-template-columns: repeat(5, 1fr); gap: 20px; }
.grid--5 .card { padding: 26px 20px; display: flex; flex-direction: column; }
.grid--5 .card__icon { width: 46px; height: 46px; font-size: 1.35rem; margin-bottom: 13px; }
.grid--5 .card h3 { font-size: 1.15rem; }
.grid--5 .card p { font-size: .93rem; margin-bottom: 18px; }
.grid--5 .card .btn { margin-top: auto; padding: 9px 14px; font-size: .78rem; align-self: flex-start; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 30px; box-shadow: var(--shadow-sm);
  transition: transform .18s ease, box-shadow .18s ease;
}
.card--hover:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.card h3 { margin-top: 0; }
.card__icon {
  width: 54px; height: 54px; border-radius: 14px; background: var(--teal-soft);
  display: grid; place-items: center; font-size: 1.6rem; margin-bottom: 16px;
}
.card--media { padding: 0; overflow: hidden; }
/* A FIXED height was the bug: on the wide two-up homepage cards a 210px-tall
   image is close to a 3:1 crop of a photo that is natively 3:2, so the top of
   the animal's head got sliced off. An aspect-ratio scales the frame with the
   card instead, so the crop stays the same shape whether the card is a quarter
   of a four-up grid or half of a two-up one. 3/2 matches the source photos. */
.card--media img {
  width: 100%; aspect-ratio: 3 / 2; height: auto;
  object-fit: cover; object-position: center;
  background: var(--tan); display: block;
}
.card--media .card__body { padding: 24px 26px 28px; }

.split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center;
}
/* Split images fill a consistent frame so portrait/landscape sources don't
   create lopsided, over-tall rows. object-position keeps subjects in view. */
.split img, .split .figframe {
  border-radius: var(--radius); box-shadow: var(--shadow);
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; object-position: center;
}
.split .figframe { background: var(--tan) center/cover no-repeat; }

/* ---------- Framed image (any source ratio, clean crop) ---------- */
.figframe {
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
  background: var(--tan) center/cover no-repeat;
}
.figframe--portrait { aspect-ratio: 4 / 5; }
.figframe--wide     { aspect-ratio: 3 / 2; }
.figframe--square   { aspect-ratio: 1 / 1; }

/* Animal sponsor profile: framed photo + story side by side */
.profile { display: grid; grid-template-columns: minmax(0,420px) 1fr; gap: 48px; align-items: center; }
.profile .figframe { max-width: 420px; }
@media (max-width: 860px) { .profile { grid-template-columns: 1fr; gap: 30px; } .profile .figframe { max-width: 100%; } }

/* ---------- Board / people cards with headshots ---------- */
.card--person { padding: 0; overflow: hidden; }
.person__photo { aspect-ratio: 4 / 5; background: var(--tan) center top/cover no-repeat; }
.card--person .card__body { padding: 22px 24px 26px; }
.person__role { color: var(--amber-dark); font-weight: 800; text-transform: uppercase; letter-spacing: .08em; font-size: .78rem; margin: 2px 0 12px; }

/* ---------- Adoption-story "Happy Tails" caption ---------- */
.tail .card__body { padding: 16px 18px 20px; }
.tail__name { font-family: var(--font-display); color: var(--teal-dark); font-size: 1.15rem; margin: 0 0 4px; }
.tail__quote { color: var(--muted); font-size: .95rem; margin: 0; font-style: italic; }

/* ---------- Resource list ---------- */
.reslist { list-style: none; padding: 0; margin: 14px 0 0; }
.reslist li { padding: 12px 0; border-bottom: 1px dashed var(--line); }
.reslist li:last-child { border-bottom: none; }
.reslist a { font-weight: 700; }
.reslist span { display: block; color: var(--muted); font-size: .95rem; margin-top: 2px; }

/* Emergency directory. The phone number is the reason the page exists, so it
   gets its own tap target rather than being buried in the description text.
   44px min-height is the accessibility floor for a touch target, and someone
   using this is panicking and probably one-handed. */
.reslist--emerg li { padding: 16px 0; }
/* Fields, not prose. The base .reslist rule paints every span muted and block,
   which is right for a one-line description and wrong here, so the value spans
   opt back out. */
.reslist--emerg .emerg__top {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px;
}
.reslist--emerg .emerg__name { font-weight: 800; font-size: 1.03rem; display: inline; }
.reslist--emerg a.emerg__name { color: var(--teal-dark); }
.reslist--emerg .emerg__row {
  display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 4px 10px;
  margin: 7px 0 0; font-size: .93rem;
}
.reslist--emerg .emerg__row > span { display: block; color: var(--ink); margin: 0; }
.reslist--emerg .emerg__k {
  color: var(--muted); font-weight: 700; font-size: .74rem;
  text-transform: uppercase; letter-spacing: .05em; padding-top: 3px;
}
.reslist--emerg .emerg__note {
  color: var(--muted); font-size: .9rem; margin: 9px 0 0; max-width: 62ch;
}
@media (max-width: 560px) {
  /* Stack the label above the value rather than squeezing a 76px column. */
  .reslist--emerg .emerg__row { grid-template-columns: 1fr; gap: 0; }
  .reslist--emerg .emerg__k { padding-top: 6px; }
}
.reslist__tel {
  display: inline-flex; align-items: center; min-height: 44px;
  margin-left: 10px; font-weight: 800; color: var(--amber-dark);
  font-family: var(--font-body); white-space: nowrap;
}
.reslist__tel::before { content: "\260E"; margin-right: 6px; font-weight: 400; }
@media (max-width: 560px) {
  .reslist__tel { display: flex; margin-left: 0; }
}

/* ---------- Stat band ---------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; text-align: center; }
/* Digits in the display font misread (its 1 looks like I, its 9 like q), so
   number-heavy elements use the body font at heavy weight instead. */
.stat__num { font-family: var(--font-body); font-size: 2.6rem; color: #fff; font-weight: 800; }
.stat__label { color: #cfe5e1; font-weight: 600; font-size: 0.98rem; }

/* ---------- Timeline ---------- */
.timeline { border-left: 3px solid var(--amber); margin-left: 8px; padding-left: 30px; }
.timeline__item { position: relative; padding-bottom: 36px; }
.timeline__item::before {
  content: ""; position: absolute; left: -39px; top: 4px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--amber);
  border: 3px solid var(--cream);
}
.timeline__year { font-family: var(--font-body); font-weight: 800; font-size: 1.4rem; color: var(--teal); margin-bottom: 6px; }

/* ---------- Lists ---------- */
.ticks { list-style: none; padding: 0; margin: 0; }
.ticks li { position: relative; padding-left: 32px; margin-bottom: 12px; }
.ticks li::before {
  content: "🐾"; position: absolute; left: 0; top: 0; font-size: 1rem;
}
.fees { list-style: none; padding: 0; margin: 0; }
.fees li { display: flex; justify-content: space-between; padding: 11px 0; border-bottom: 1px dashed var(--line); }
.fees li span:last-child { font-weight: 800; color: var(--teal); }

/* ---------- Progress ---------- */
.progress { background: #fff; border: 1px solid var(--line); border-radius: 999px; height: 22px; overflow: hidden; }
.progress__bar { height: 100%; background: var(--amber); width: 60%; }

/* ---------- Newsletter / CTA band ---------- */
.cta-band { background: var(--amber-soft); border-radius: var(--radius); padding: 46px; text-align: center; }

/* ---------- Partners / sponsors (linked logos) ---------- */
.partners { display: flex; flex-wrap: wrap; gap: 20px; align-items: center; justify-content: center; margin-top: 28px; }
.partner {
  display: flex; align-items: center; justify-content: center;
  width: 170px; height: 96px; padding: 14px 18px;
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  box-shadow: var(--shadow-sm); transition: transform .15s ease, box-shadow .15s ease;
}
.partner:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.partner img { max-width: 100%; max-height: 100%; object-fit: contain; }
@media (max-width: 560px) { .partner { width: 132px; height: 84px; } }

/* ---------- Footer ---------- */
.site-footer { background: var(--brown); color: #d8d2c6; padding: 64px 0 28px; }
.site-footer a { color: #fff; }
.site-footer .grid { gap: 36px; }
/* Brand rule: use the white logo on dark backgrounds. The color PNG is rendered
   monochrome-white via filter so we don't need a separate asset. */
.site-footer img { height: 58px; margin-bottom: 16px; filter: brightness(0) invert(1); opacity: .95; }
.site-footer h2 { color: #fff; font-family: var(--font-body); font-weight: 800; text-transform: uppercase; letter-spacing: .08em; font-size: .82rem; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
/* Footer list items: wrapped lines must read as ONE entry. The old rule had
   9px between items but inherited ~1.7 line-height, so a wrapped address looked
   like two separate entries. Tighter lines + bigger item gap fixes the grouping. */
.site-footer li { margin-bottom: 15px; line-height: 1.45; }
/* Social links repeat in the footer as well as the topbar (2026-08 website
   review): the topbar scrolls away, the footer is where people land. */
/* Footer social: the brand marks themselves, not their names.
   An icon link has NO accessible name of its own, so every <a> carries an
   aria-label and the <svg> is aria-hidden; a screen reader reads "LAWS on
   Facebook, link" rather than announcing a decorative path. The 44px circle is
   the WCAG 2.5.8 minimum target, which the old text links did not meet either. */
.footer__social { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px !important; }
.footer__social li { margin-bottom: 0; }
.footer__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.10); color: #fff;
  transition: background .18s ease, transform .18s ease;
}
.footer__social a:hover, .footer__social a:focus-visible {
  background: var(--teal); transform: translateY(-2px);
}
.footer__social svg { width: 20px; height: 20px; fill: currentColor; display: block; }
@media (prefers-reduced-motion: reduce) {
  .footer__social a { transition: none; }
  .footer__social a:hover, .footer__social a:focus-visible { transform: none; }
}
.site-footer li:last-child { margin-bottom: 0; }
.flabel { display: block; font-size: .74rem; text-transform: uppercase;
  letter-spacing: .07em; opacity: .6; margin-bottom: 3px; }
.footer__bottom { border-top: 1px solid rgba(255,255,255,0.15); margin-top: 40px; padding-top: 20px; font-size: .85rem; opacity: .8; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
/* "SMS Terms" was breaking across two lines on phones, so the link read as
   "SMS" then a stray "Terms". Link labels here are short; never split them. */
.footer__bottom a { white-space: nowrap; }

/* demo ribbon */
.demo-ribbon {
  background: #2b2b2b; color: #ffd9a8; text-align: center; font-size: .82rem;
  font-weight: 700; padding: 7px 16px; letter-spacing: .02em;
}
.demo-ribbon span { color: #fff; }

/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
  .grid--5 { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 920px) {
  .split { grid-template-columns: 1fr; gap: 30px; }
  .grid--3, .grid--4, .grid--5 { grid-template-columns: repeat(2, 1fr); }
  .stats { grid-template-columns: repeat(2, 1fr); gap: 32px; }

  .nav-toggle-label {
    display: inline-grid; place-items: center; width: 46px; height: 46px;
    border-radius: 10px; border: 1px solid var(--line); cursor: pointer;
    font-size: 1.4rem; color: var(--teal-dark);
  }
  .nav { display: none; }
  .nav-toggle:checked ~ .nav {
    display: flex; flex-direction: column; align-items: stretch;
    position: absolute; top: 100%; left: 0; right: 0; background: #fff;
    border-bottom: 1px solid var(--line); padding: 14px 24px 22px; gap: 2px;
    box-shadow: var(--shadow);
    /* The full menu is ~1200px tall and rides the sticky header, so page
       scrolling can never reveal its lower half (Give, Pet Help, Contact) on
       a phone. Cap it to the viewport and let the MENU itself scroll.
       Reported by Ellen 2026-08-28: Fundraisers / Willow's Fund unreachable. */
    max-height: calc(100vh - 140px);
    max-height: calc(100dvh - 140px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  .nav__group > span { display: block; color: var(--amber-dark); font-size: .8rem; text-transform: uppercase; letter-spacing: .1em; padding: 14px 12px 4px; }
  .nav__menu { position: static; display: block; border: none; box-shadow: none; padding: 0 0 0 10px; min-width: 0; }
  .site-header .wrap { position: relative; flex-wrap: wrap; }
}
@media (max-width: 600px) {
  .grid--2, .grid--3, .grid--4, .grid--5 { grid-template-columns: 1fr; }
  body { font-size: 17px; }
  .section { padding: 60px 0; }
  .topbar__hours { display: none; }
}

/* Visually hidden but read by screen readers; used for section h2s on
   pages whose first visible headings are card h3s (fixes h1->h3 jumps). */
.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}

/* Digit runs inside display-font headings (see .stat__num note). */
.nums { font-family: var(--font-body); font-weight: 800; }
/* Pills used to fuse when a button row wrapped on small screens. */
.btn { margin-block: 5px; }
/* Demo-only annotations, styled so they read as notes rather than site copy. */
.demo-note { font-size: .92rem; color: var(--muted); font-style: italic; }

/* ============ ADOPTABLE ANIMAL DETAIL PAGES ============ */
.animalhero__back { margin: 0 0 18px; font-size: .95rem; }
.animalhero__grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 44px; align-items: start; }
.animalhero__info h1 { margin: 6px 0 0; }
.gal__main { border-radius: var(--radius); overflow: hidden; background: var(--tan); aspect-ratio: 4/3;
  box-shadow: var(--shadow); }
.gal__main img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Auto slideshow: every photo is a stacked slide, the active one fades in.
   The thin bar along the bottom fills once per interval so the motion reads
   as deliberate rather than as a glitch. (Ian, 2026-09-05) */
.gal__stage { position: absolute; inset: 0; }
/* ShelterLuv bakes a 2.5% coloured frame into every cover photo; a 4:3 crop of
   that square leaves bars down both sides. A 6% zoom crops the frame away. */
.gal__slide { position: absolute; inset: 0; opacity: 0; transition: opacity .9s ease; transform: scale(1.06); }
.gal__thumb img { transform: scale(1.08); }
.gal__slide.is-on { opacity: 1; }
.gal__bar { position: absolute; left: 0; bottom: 0; height: 4px; width: 0; z-index: 2;
  background: rgba(255, 255, 255, .85); border-radius: 0 2px 2px 0; }
.gal__bar.is-run { animation: galbar 4.2s linear forwards; }
@keyframes galbar { from { width: 0; } to { width: 100%; } }
@media (prefers-reduced-motion: reduce) { .gal__slide { transition: none; } .gal__bar { display: none; } }
/* Slideshow controls: prev/next on the sides, pause/play bottom-right. Always
   visible (touch has no hover), brighter on hover, hidden while a video plays. */
.gal__nav, .gal__pause { position: absolute; z-index: 3; border: 0; padding: 0; cursor: pointer;
  border-radius: 50%; background: rgba(255, 255, 255, .86); color: var(--teal-dark);
  display: grid; place-items: center; box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
  transition: background .15s ease, transform .15s ease, opacity .15s ease; }
.gal__nav { top: 50%; width: 46px; height: 46px; transform: translateY(-50%); }
.gal__nav--prev { left: 14px; } .gal__nav--next { right: 14px; }
.gal__nav:hover { background: #fff; transform: translateY(-50%) scale(1.06); }
.gal__nav svg { width: 24px; height: 24px; }
.gal__pause { right: 14px; bottom: 16px; width: 40px; height: 40px; }
.gal__pause:hover { background: #fff; transform: scale(1.06); }
.gal__pause svg { width: 18px; height: 18px; }
.gal__pause .ic-play { display: none; }
.gal__pause.is-paused .ic-play { display: block; }
.gal__pause.is-paused .ic-pause { display: none; }
.gal__nav:focus-visible, .gal__pause:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }
.gal__main:has(iframe.gal__video) .gal__nav, .gal__main:has(iframe.gal__video) .gal__pause { display: none; }
@media (max-width: 860px) { .gal__nav { width: 40px; height: 40px; } .gal__nav--prev { left: 10px; } .gal__nav--next { right: 10px; } }
.gal__thumbs { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.gal__thumb { width: 82px; height: 66px; padding: 0; border: 2px solid transparent; border-radius: 12px;
  overflow: hidden; background: none; cursor: pointer; transition: border-color .15s ease, transform .15s ease; }
.gal__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal__thumb:hover { transform: translateY(-2px); }
.gal__thumb.is-on { border-color: var(--teal); }
.gal__thumb:focus-visible { outline: 3px solid var(--teal); outline-offset: 2px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
/* Full-width fact band + one-column story (2026-09-05, Ian: details woven
   into the page, no floating sidebar, no dead space, story on WHITE not tan). */
.factband { background: var(--teal-soft); border-top: 1px solid rgba(23, 156, 155, .16);
  border-bottom: 1px solid rgba(23, 156, 155, .16); padding: 28px 0; }
.factband__list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 18px 44px; justify-content: space-between; }
.factband__list li { display: flex; flex-direction: column; gap: 2px; min-width: 90px; }
.factband__k { font-size: .76rem; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--teal-dark); }
.factband__v { font-weight: 700; color: var(--ink); font-size: 1.15rem; line-height: 1.3; }
/* ONE full-width column, big readable type (Ian, 2026-09-05: no columns, ever). */
.story { font-size: 1.22rem; line-height: 1.75; }
.story p { margin: 0 0 1.1em; }
.story__rule { border: 0; border-top: 3px solid var(--teal-soft); width: 96px; margin: 30px auto; }
.story__list { padding-left: 1.25em; margin: 0 0 1.1em; }
.story__list li { margin-bottom: .45em; }
.storynote { text-align: center; color: var(--muted); font-size: .92rem; margin: 34px 0 0; }
/* Hero extras: waiting line, share row, phone line. */
.waiting { margin: 12px 0 0; color: var(--amber-dark); font-weight: 700; font-size: 1rem; }
.waiting--new { color: var(--teal-dark); }
.animalhero__ask { margin: 16px 0 0; color: var(--muted); font-size: .95rem; }
.share { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 26px;
  padding-top: 20px; border-top: 1px solid var(--line); }
.share__label { font-size: .78rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-right: 2px; }
.share__btn { background: var(--surface); border: 1.5px solid var(--line); color: var(--teal-dark);
  font: inherit; font-weight: 700; font-size: .9rem; line-height: 1.2; padding: 8px 14px;
  border-radius: 999px; cursor: pointer; transition: border-color .15s ease, background .15s ease; }
.share__btn:hover { border-color: var(--teal); background: var(--teal-soft); color: var(--teal-dark); }
.share__btn.is-done { border-color: var(--teal); background: var(--teal); color: #fff; }
/* "More friends waiting": static cards in the .animal skin, NOT .animals (that
   class is what adoptables.js replaces with the live grid). */
.friends { display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px; }
.friends .animal__photo { background-size: 107% auto; }
@media (max-width: 920px) { .friends { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .friends { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .friends .animal__body { padding: 12px 13px 15px; } .friends .animal__name { font-size: 1.05rem; } }
/* Sticky apply bar, phones only, slides up once the hero button scrolls away. */
.stickycta { display: none; }
@media (max-width: 860px) {
  .stickycta { display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    align-items: center; justify-content: space-between; gap: 12px;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    background: var(--surface); border-top: 1px solid var(--line);
    box-shadow: 0 -8px 24px rgba(58, 53, 46, .14);
    transform: translateY(110%); transition: transform .25s ease; }
  .stickycta.is-show { transform: none; }
  .stickycta__name { font-family: var(--font-display); color: var(--teal-dark); font-size: 1.2rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .stickycta .btn { white-space: nowrap; padding: 12px 22px; font-size: .95rem; }
  body:has(.stickycta.is-show) { padding-bottom: 72px; }
}

/* video: LAWS-hosted clips and YouTube facades */
.videogrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 22px; }
.animalvideo { margin: 0; }
.animalvideo video { width: 100%; border-radius: var(--radius); background: #000; display: block; }
.animalvideo figcaption { margin-top: 10px; font-size: .92rem; color: var(--muted); }
.videofacade { position: relative; padding: 0; border: 0; background: #000; cursor: pointer;
  border-radius: var(--radius); overflow: hidden; display: block; width: 100%; aspect-ratio: 16/9; }
.videofacade img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: .85; transition: opacity .15s ease; }
.videofacade:hover img { opacity: 1; }
.videofacade__play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 2.4rem; color: #fff; text-shadow: 0 2px 12px rgba(0,0,0,.6); }
.videofacade iframe { width: 100%; height: 100%; border: 0; }

/* card loop: silent looping clip standing in for the cover photo in a grid */
.animal__media { position: relative; }
.animal__loop { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .35s ease; }
.animal__loop.is-playing { opacity: 1; }
/* WCAG 2.2.2: an indefinitely looping clip needs a stop mechanism, and a
   reduced-motion preference must win outright. */
@media (prefers-reduced-motion: reduce) { .animal__loop { display: none !important; } }
body.motion-paused .animal__loop { display: none !important; }

@media (max-width: 860px) {
  .animalhero__grid { grid-template-columns: 1fr; gap: 28px; }
  .story { font-size: 1.1rem; }
  .factband__list { justify-content: flex-start; gap: 16px 36px; }
}

/* Embedded ShelterLuv application forms, hosted on our own pages. */
.formembed { background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden; }
.formembed iframe { display: block; width: 100%; height: 1500px; border: 0; }
@media (max-width: 700px) { .formembed iframe { height: 1900px; } }

/* Adopted animals keep their page; it becomes the success story. */
.adoptedbox { background: var(--teal-soft, #e6f4f3); border: 1px solid var(--teal);
  border-radius: var(--radius); padding: 18px 20px; margin-top: 18px; }
.adoptedbox__tag { display: inline-block; background: var(--teal); color: #fff;
  font-weight: 800; font-size: .78rem; letter-spacing: .08em; text-transform: uppercase;
  padding: 4px 12px; border-radius: 999px; }

/* =========================================================================
   Share a video (/share-a-video)
   The only real form we host ourselves; every other form is a ShelterLuv
   iframe. Kept deliberately plain: it is filled in one-handed, on a phone,
   usually by a foster standing in a room with a puppy.
   ========================================================================= */
.vpanel { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-sm); padding: 32px 32px 36px; }
.vform { display: grid; gap: 20px; }
.vform__row { display: grid; gap: 7px; }
.vform label { font-weight: 700; font-size: .95rem; color: var(--ink); }
.vform__opt { font-weight: 500; color: var(--muted); font-size: .86rem; }
.vform input[type="text"], .vform input[type="email"], .vform input[type="tel"],
.vform select, .vform textarea {
  font: inherit; font-size: 1rem; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 13px 14px; width: 100%; }
.vform textarea { resize: vertical; min-height: 92px; }
.vform input:focus-visible, .vform select:focus-visible, .vform textarea:focus-visible {
  outline: 3px solid var(--teal); outline-offset: 1px; border-color: var(--teal); }
/* Needs to out-specify `.vform label` above, or the consent line renders bold
   and reads like a heading instead of the sentence it is. */
.vform label.vform__check { display: flex; gap: 11px; align-items: flex-start;
  font-weight: 500; color: var(--ink); font-size: .93rem; line-height: 1.5; }
.vform__check input { flex: none; width: 20px; height: 20px; margin-top: 2px; accent-color: var(--teal); }
.vform__hint { color: var(--muted); font-size: .92rem; margin: -4px 0 0; }
.vform button { justify-self: start; }
/* The manual fallback, revealed when the phone has no mail app wired up. */
.vfallback { background: var(--tan); border-radius: var(--radius-sm);
  padding: 18px 20px; margin-top: 4px; font-size: .95rem; }
.vfallback[hidden] { display: none; }
.vfallback dt { font-weight: 800; font-size: .74rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted); margin-top: 12px; }
.vfallback dt:first-of-type { margin-top: 0; }
.vfallback dd { margin: 3px 0 0; word-break: break-word; }
.vfallback code { font-size: .95em; background: rgba(255,255,255,.7);
  padding: 2px 6px; border-radius: 5px; }
.vfallback p { margin: 0 0 8px; }
.vfallback p:last-child { margin-bottom: 0; }

/* Upload progress. A phone clip over rural Wyoming DSL can take minutes, so
   the bar is not decoration: without it people close the tab and lose the
   upload. Percentage in text as well as width, because a bar alone reads as
   "stuck" when a big file crawls. */
.vprogress { margin-top: 4px; }
.vprogress[hidden] { display: none; }
.vprogress__track { height: 10px; border-radius: 999px; background: var(--tan);
  overflow: hidden; }
.vprogress__bar { height: 100%; width: 0; border-radius: 999px;
  background: var(--teal); transition: width .25s ease; }
.vprogress__stat { margin: 8px 0 0; color: var(--muted); font-size: .92rem; }

/* The finished state. The link is a real input rather than text so it can be
   selected and copied on a phone, where "long press to copy" on plain text is
   a coin flip. */
.vdone { background: var(--tan); border-radius: var(--radius-sm);
  padding: 22px 22px 24px; margin-top: 4px; }
.vdone[hidden] { display: none; }
.vdone h3 { margin: 0 0 .4em; }
.vdone p { margin: 0 0 12px; font-size: .95rem; }
.vdone__row { display: flex; gap: 10px; align-items: stretch; margin-bottom: 12px; }
.vdone__row input { flex: 1; min-width: 0; font-family: inherit; font-size: .95rem;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff; color: var(--ink); }
.vdone__row .btn { flex: none; }
.vdone a[hidden] { display: none; }

@media (max-width: 560px) {
  .vpanel { padding: 24px 20px 28px; }
  .vform button { justify-self: stretch; width: 100%; }
  .vdone__row { flex-direction: column; }
  .vdone__row .btn { width: 100%; }
}

/* =========================================================================
   Almost Home project page — the ledger and the correction log.
   Both exist because this campaign's strongest argument is specificity:
   real permit numbers, real cubic feet, real dollars.
   ========================================================================= */
.ledger { width: 100%; border-collapse: collapse; margin-top: 8px; }
.ledger th, .ledger td { text-align: left; padding: 13px 12px; border-bottom: 1px solid var(--line); }
.ledger th { font-size: .76rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 800; }
.ledger td:last-child, .ledger th:last-child { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ledger tr:last-child td { border-bottom: none; }
.ledger tfoot td { font-weight: 800; border-top: 2px solid var(--ink); border-bottom: none; padding-top: 15px; }
.ledger .is-funded { color: var(--teal-dark); font-weight: 700; }
@media (max-width: 560px) { .ledger th, .ledger td { padding: 11px 6px; font-size: .93rem; } }

/* "They asked / we answered" pairs. A dl reads correctly to a screen reader
   and collapses to a single column on a phone without any extra markup. */
.reqs { display: grid; gap: 0; margin: 0; }
.reqs > div { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.15fr); gap: 26px;
  padding: 20px 0; border-bottom: 1px solid var(--line); }
.reqs > div:last-child { border-bottom: none; }
.reqs dt { font-weight: 800; margin: 0; }
.reqs dd { margin: 0; color: var(--muted); }
@media (max-width: 720px) {
  .reqs > div { grid-template-columns: 1fr; gap: 6px; }
  .reqs dd { padding-left: 0; }
}
/* Status strip: the four facts a donor or a board member wants in three seconds. */
.statusstrip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.statusstrip > div { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px 20px; }
.statusstrip strong { display: block; font-family: var(--font-body); font-weight: 800;
  font-size: 1.35rem; color: var(--teal-dark); line-height: 1.25; margin-bottom: 4px; }
.statusstrip span { color: var(--muted); font-size: .93rem; }
@media (max-width: 900px) { .statusstrip { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .statusstrip { grid-template-columns: 1fr; } }
/* Pull-out box for the "we found it ourselves" beat on the Almost Home page. */
.callout { background: var(--teal-soft); border-left: 5px solid var(--teal);
  border-radius: var(--radius-sm); padding: 24px 26px; }
.callout h3 { color: var(--teal-dark); }
.callout p:last-child { margin-bottom: 0; }
/* An escape hatch from the 4:3 split frame, for images where the SHAPE is the
   subject. The Giving Tree is a tall sculpture: cropping it to landscape cuts
   the crown off the top and the base off the bottom, which is the whole photo. */
.split img.split__tall { aspect-ratio: auto; object-fit: contain;
  max-height: 620px; width: auto; max-width: 100%; justify-self: center;
  background: transparent; box-shadow: var(--shadow); }
@media (max-width: 760px) { .split img.split__tall { max-height: 460px; } }

/* Video in the photo strip. The clip is the single strongest thing on an
   animal's page, so it sits FIRST in the thumbnails and plays in the main
   frame rather than in a section below the fold. */
.gal__thumb--video { position: relative; }
.gal__play { position: absolute; inset: 0; display: grid; place-items: center;
  color: #fff; font-size: 1.1rem; text-shadow: 0 1px 6px rgba(0,0,0,.6);
  background: rgba(0,0,0,.28); border-radius: inherit; pointer-events: none; }
.gal__main { position: relative; }
.gal__main iframe.gal__video { position: absolute; inset: 0;
  width: 100%; height: 100%; border: 0; border-radius: inherit; background: #000; }

/* =========================================================================
   Browse-grid photo strips. Seeing a second photo used to cost a page load;
   now every card swipes. Native CSS scroll-snap does the work, so there is no
   carousel library and swiping costs zero JS on touch.
   ========================================================================= */
.animal__media { position: relative; overflow: hidden; }
.animal__strip {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; -ms-overflow-style: none;
}
/* NOTE: deliberately NO `scroll-behavior: smooth` here. Combined with
   scroll-snap-type: x mandatory it wedges programmatic scrolling: setting
   scrollLeft = 252 lands at 3px and stays there, so the arrows silently do
   nothing. Smoothness is requested per-call in JS instead, which works. */
.animal__strip::-webkit-scrollbar { display: none; }
.animal__slide { flex: 0 0 100%; scroll-snap-align: center; position: relative; }
.animal__slide img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  background: var(--tan); display: block;
}
.animal__slide--video .animal__playbadge {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: #fff; font-size: 1.5rem; text-shadow: 0 1px 8px rgba(0,0,0,.65);
  background: rgba(0,0,0,.3);
}

/* Arrows are pointer-only. On touch the finger IS the control, and two
   permanent buttons over a small card just cover the animal. */
.animal__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: 50%; border: 0;
  background: rgba(255,255,255,.9); color: var(--ink);
  font-size: 1.25rem; line-height: 1; cursor: pointer;
  display: grid; place-items: center; padding: 0;
  box-shadow: 0 2px 8px rgba(0,0,0,.18);
  opacity: 0; transition: opacity .16s ease; z-index: 2;
}
.animal__nav--prev { left: 8px; }
.animal__nav--next { right: 8px; }
/* Reveal on hovering the MEDIA, not just `.animal`. The featured card is
   `.featured`, never `.animal`, so the old selector meant its arrows existed
   in the DOM at opacity 0 and could never be shown. With no arrows and a
   mouse, a scroll-snap strip needs shift+wheel to move, i.e. it looked
   completely static. */
.animal:hover .animal__nav,
.animal__media:hover .animal__nav,
.animal__nav:focus-visible { opacity: 1; }

/* The featured pet is ONE large showcase, not a dense grid, so its arrows stay
   visible. Nobody should have to hover a photo to discover it has seven more
   behind it. Slightly bigger too, to suit a 500px frame. */
/* No opacity override: the featured arrows now hide and reveal on hover exactly
   like the browse cards, which is only possible because the reveal rule above
   matches `.animal__media` rather than `.animal`. They are just sized up, since
   34px buttons look lost on a 500px frame. */
.featured__media .animal__nav { width: 40px; height: 40px; font-size: 1.4rem; }
.featured__media .animal__nav--prev { left: 12px; }
.featured__media .animal__nav--next { right: 12px; }
@media (hover: none) { .animal__nav { display: none; } }

.animal__dots {
  position: absolute; left: 0; right: 0; bottom: 8px; z-index: 2;
  display: flex; justify-content: center; gap: 5px; pointer-events: none;
}
.animal__dots i {
  width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.55);
  box-shadow: 0 1px 3px rgba(0,0,0,.4); transition: background .16s ease;
}
.animal__dots i.is-on { background: #fff; }

@media (prefers-reduced-motion: reduce) {
  .animal__strip { scroll-behavior: auto; }
}

/* A browse card's video slide plays in place once clicked. */
.animal__slide--video iframe {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border: 0; background: #000;
}
