/* =============================================================
   Exotic Stars Cattery - site stylesheet.
   Hand edited. Replaced the previous site.css on 27 August 2026.
   theme.css is no longer loaded: the tokens live at the top of
   this file.

   Direction: warm domestic editorial. The photographs carry the
   page; the type is quiet and large; nothing floats in a card.

   Rules held throughout:
   - One accent (slate blue), used identically everywhere.
   - One radius system: sharp. Radius 0 on every surface, images
     and buttons included. The old build was rounded on every
     element, which is most of why it read as a template.
   - No tint bands alternating down the page. One paper colour,
     photographs and hairlines do the separating.
   - No cards. Elevation only where something genuinely floats
     (menu panel, modal).
   ============================================================= */

:root{
  /* Ground is NEUTRAL, deliberately. The first pass of this draft used a warm
     cream (#faf6ef) and Mark called it: warm cream plus a warm accent is the
     default palette every generated "premium / artisan" page reaches for, and
     it was warmer than the live build's own #fdfbf7, so it moved toward the
     tell rather than away from it. It was also working against the argument of
     the redesign, which is that the photographs carry the page: a warm ground
     tints every photograph on it.
     Two alternatives are one block of tokens away, see the foot of this file. */
  --paper:#fbfbfa;
  --paper-deep:#f0f0ee;
  /* translucent version of --paper, for the sticky header and the mobile bar.
     Keep it in step with --paper or those two strips keep the old ground. */
  --paper-veil:rgba(251,251,250,.94);
  --ink:#1d2024;
  --text:#32363b;
  --muted:#5f646a;
  --line:#dcdcd8;
  --line-soft:#e7e7e3;
  --accent:#3a586f;
  --accent-dark:#294055;
  /* status chips: available reads as the brand slate, reserved as amber */
  --ok-bg:#e6ecf1;
  --ok-fg:#28405a;
  --warn-bg:#f6ead8;
  --warn-fg:#78471a;
  --footer-bg:#eceff2;
  --footer-line:#d7dde3;
  --white:#fff;

  --font-display:"Jost","Outfit",-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-body:"Geist",-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

  --maxw:76rem;
  --gut:1.25rem;
  --sec:clamp(2rem,3.4vw,3.1rem);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;
  background:var(--paper);
  color:var(--text);
  font-family:var(--font-body);
  font-size:1.0625rem;
  line-height:1.7;
  -webkit-font-smoothing:antialiased;
}
figure{margin:0}           /* load bearing: browser default is 1em 40px */
img{max-width:100%;display:block}
h1,h2,h3,h4{font-family:var(--font-display);color:var(--ink);margin:0;font-weight:500;letter-spacing:-.02em}
p{margin:0 0 1.1em}
p:last-child{margin-bottom:0}
a{color:var(--accent);text-underline-offset:.22em;text-decoration-thickness:1px}
a:hover{color:var(--accent-dark)}
ul{margin:0;padding:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gut)}
.skip{position:absolute;left:-9999px}
.skip:focus{left:1rem;top:1rem;z-index:80;background:var(--ink);color:#fff;padding:.6rem 1rem}

/* ---------- buttons: sharp, no fills except one primary ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-body);font-size:.95rem;font-weight:500;
  padding:.85rem 1.6rem;border:1px solid transparent;border-radius:0;
  text-decoration:none;white-space:nowrap;cursor:pointer;
  transition:background .18s ease,color .18s ease,transform .12s ease,border-color .18s ease;
}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn-primary:hover{background:var(--accent-dark);border-color:var(--accent-dark);color:#fff}
.btn-ghost{background:transparent;color:var(--ink);border-color:var(--line)}
.btn-ghost:hover{border-color:var(--ink);color:var(--ink);background:transparent}
/* One accent on the page. btn-dark was near-black, which put a third button
   colour next to the slate primary and the white button on the closing band.
   The class name stays because it is in the markup on nineteen pages. */
.btn-dark{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn-dark:hover{background:var(--accent-dark);border-color:var(--accent-dark);color:#fff}
.btn-light{background:#fff;color:var(--ink);border-color:#fff}
.btn-light:hover{background:var(--paper-deep);color:var(--ink)}

/* ---------- announcement strip ---------- */
/* Announcement strip. Deliberately quiet: a saturated band here fights the header
   below it and the hero below that, which is the note in handoff section 4. Whole
   strip is the link. */
.announce{background:var(--paper-deep);border-bottom:1px solid var(--line-soft)}
.announce a{
  display:block;text-align:center;padding:.5rem var(--gut);
  color:var(--text);font-size:.82rem;letter-spacing:.01em;text-decoration:none;
}
.announce a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:.22em}

/* ---------- header ---------- */
.site-header{
  position:sticky;top:0;z-index:40;
  background:var(--paper-veil);
  backdrop-filter:saturate(140%) blur(8px);
  border-bottom:1px solid var(--line-soft);
  transition:transform .25s ease;
}
/* site.js writes data-hidden="true" / "false" (it sets the string, it does not
   remove the attribute), so the hidden state must be matched on the value. */
.site-header[data-hidden="true"]{transform:translateY(-100%)}
.header-bar{display:flex;align-items:center;justify-content:space-between;gap:1rem;min-height:64px}
.brand{display:flex;align-items:center;gap:.65rem;text-decoration:none;color:var(--ink)}
.brand img{width:34px;height:34px;object-fit:cover;border-radius:0}
.brand span{font-family:var(--font-display);font-size:1.02rem;font-weight:500;line-height:1.15;display:block;letter-spacing:-.01em}
.brand small{display:block;font-family:var(--font-body);font-size:.7rem;font-weight:400;color:var(--muted);letter-spacing:.02em;margin-top:.12rem}

.nav-toggle{
  display:inline-flex;align-items:center;gap:.5rem;
  background:transparent;border:1px solid var(--line);color:var(--ink);
  font:inherit;font-size:.88rem;padding:.5rem .85rem;border-radius:0;cursor:pointer;
}
.nav-toggle .bars{position:relative;width:15px;height:9px;display:inline-block}
.nav-toggle .bars i,.nav-toggle .bars::before,.nav-toggle .bars::after{
  content:"";position:absolute;left:0;right:0;height:1.5px;background:currentColor
}
.nav-toggle .bars::before{top:0}
.nav-toggle .bars i{top:3.75px}
.nav-toggle .bars::after{bottom:0}

.site-nav{display:none}
.site-nav[data-open="true"]{
  display:block;position:absolute;left:0;right:0;top:100%;
  background:var(--paper);border-bottom:1px solid var(--line);
  max-height:calc(100svh - 7rem);overflow:auto;
  box-shadow:0 18px 40px rgba(34,38,43,.10);
}
.site-nav[data-open="true"] ul{
  list-style:none;display:grid;grid-template-columns:1fr;
  padding:.5rem var(--gut) 1rem;
}
.site-nav[data-open="true"] li{margin:0}
.site-nav[data-open="true"] a{
  display:block;padding:.62rem 0;color:var(--ink);text-decoration:none;
  font-size:1rem;border-bottom:1px solid var(--line-soft);
}
.site-nav[data-open="true"] .nav-cta{padding:0 var(--gut) 1.15rem}
.site-nav[data-open="true"] .nav-cta .btn{width:100%;margin-bottom:.5rem}
@media (min-width:22rem){
  .site-nav[data-open="true"] ul{grid-template-columns:1fr 1fr;column-gap:1.25rem}
}
@media (min-width:68rem){
  .site-header[data-hidden="true"]{transform:none}
  .nav-toggle{display:none}
  /* brand and nav share one row. A two-line header at desktop is broken, and it
     is how the previous build shipped for two days. */
  .site-header .wrap{display:flex;align-items:center;justify-content:space-between;gap:2rem}
  .header-bar{flex:0 0 auto;min-height:0}
  .site-nav{display:block;flex:0 1 auto}
  .site-nav ul{list-style:none;display:flex;gap:1.55rem;align-items:center}
  .site-nav a{
    color:var(--text);text-decoration:none;font-size:.925rem;white-space:nowrap;
    padding:.2rem 0;border-bottom:1px solid transparent;
  }
  .site-nav a:hover{color:var(--ink);border-bottom-color:var(--ink)}
  .site-nav a[aria-current]{color:var(--ink);border-bottom-color:var(--ink)}
  .site-nav .nav-cta{display:none}
  .header-bar{min-height:72px}
}

/* =============================================================
   HERO - asymmetric split. Type on paper, photograph running to
   the right edge and off the top. No scrim over a cat's face.
   ============================================================= */
.hero{border-bottom:1px solid var(--line-soft)}
.hero-in{
  display:grid;grid-template-columns:1fr;
  max-width:var(--maxw);margin-inline:auto;
}
/* Square on phones, not 4:3. The hero photograph is a portrait phone shot like
   every other photograph on this site, and a landscape frame throws away the
   top and bottom of it. Same rule as the card and split frames. */
.hero-photo{position:relative;aspect-ratio:1/1;overflow:hidden;order:-1}
/* the picture element wraps the img, so target the img itself */
.hero-photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%}
.hero-copy{padding:2.25rem var(--gut) 2.75rem}
.hero h1{
  font-size:clamp(2.5rem,10vw,3.4rem);line-height:1.02;
  margin:0 0 1rem;max-width:14ch;
}
.hero-lede{font-size:1.1rem;color:var(--muted);max-width:34ch;margin:0 0 1.6rem}
.hero-cta{margin:0}
@media (min-width:56rem){
  /* The photograph column is slightly NARROWER than the copy column and the
     frame is close to square. Every photograph on this site is a 4:3 portrait
     off a phone: a wide frame in front of one crops a third of its height away,
     which is what "too cropped on desktop" meant. Roughly square keeps about
     three quarters of the frame, which fits a whole sitting kitten with air
     above and below. The height is capped so the trust strip still clears the
     fold on a 900px laptop. */
  .hero-in{grid-template-columns:minmax(0,1fr) minmax(0,.92fr);align-items:stretch;gap:0}
  /* The cap has to scale with the viewport, not sit at a fixed rem. A fixed cap
     holds the frame at one height while the column keeps widening, so the frame
     goes landscape again on a big monitor and the crop comes back: at 1920 a
     44rem cap showed 57% of the photograph. This keeps roughly 70% or better
     visible from 1280 through 2560 while the trust strip still clears the fold. */
  .hero-photo{order:0;aspect-ratio:auto;height:min(80svh,70rem)}
  .hero-copy{
    padding:clamp(3rem,7vw,6rem) clamp(2rem,4vw,3.5rem) clamp(3rem,7vw,6rem) var(--gut);
    display:flex;flex-direction:column;justify-content:center;
  }
  .hero h1{font-size:clamp(3rem,4.6vw,4.35rem)}
  .hero-lede{font-size:1.16rem}
}
@media (min-width:76rem){
  /* let the photograph bleed to the window edge, not the column edge */
  .hero-in{max-width:none;margin:0}
  .hero-copy{padding-left:max(var(--gut),calc(50vw - (var(--maxw) / 2)))}
}

/* ---------- facts row: one strip, never a stack ---------- */
/* Three even columns, not a growing lead pushing two stats to the far edge:
   that left a canyon down the middle of the strip.
   All three also read the same way round now. The lead is a strong line with a
   muted note under it; the two stats were the opposite, a muted label with the
   value under that, so the eye met bold-then-quiet on the left and
   quiet-then-bold on the right. The dl markup is untouched and still correct;
   column-reverse only flips how the pair is painted. */
.facts{border-bottom:1px solid var(--line-soft)}
.facts-row{
  display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:.9rem;
  padding:.85rem 0;align-items:start;
}
.facts-lead b{
  display:block;font-family:var(--font-display);font-weight:500;
  font-size:clamp(.95rem,2.2vw,1.08rem);color:var(--ink);letter-spacing:-.01em;
  line-height:1.25;
}
.facts-lead span{
  display:block;font-size:clamp(.72rem,1.6vw,.8rem);color:var(--muted);
  margin-top:.15rem;line-height:1.35;
}
.facts-pair{display:contents;margin:0}
.facts-pair div{
  display:flex;flex-direction:column-reverse;align-items:flex-start;
  min-width:0;padding-left:.9rem;border-left:1px solid var(--line-soft);
}
/* balance the small labels: at two columns "Kittens go home at" would otherwise
   wrap with "at" alone on the second line */
.facts-pair dt,.facts-lead span{text-wrap:balance}
.facts-pair dt{
  font-size:clamp(.72rem,1.6vw,.8rem);color:var(--muted);
  margin:.15rem 0 0;line-height:1.35;
}
.facts-pair dd{
  margin:0;font-family:var(--font-display);font-weight:500;
  font-size:clamp(.95rem,2.2vw,1.08rem);color:var(--ink);
  letter-spacing:-.01em;line-height:1.25;white-space:nowrap;
}
/* Two columns is the base case, not one: a single column stacked the lead above
   the stat on every phone, which is the bug Mark reported on 27 Aug ("wraps into
   2 lines, instead of 1"). It measured 2 rows and 113px at 320 through 479.
   Two columns is 1 row at every width, 99px at 320 falling to 64px at 479.

   The third column still waits for 30rem. That was already a deliberate call and
   it survived re-measurement: at 375 to 414px three columns push the nowrapped
   "12 to 14 weeks" out of its own grid cell, which the document-level overflow
   check cannot see because the row has min-width:0. Written min-width, like every
   breakpoint here. */
.facts-pair div:last-child{display:none}
@media (min-width:30rem){
  .facts-row{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr) minmax(0,1fr);gap:clamp(1rem,3vw,2.5rem)}
  /* :last-child has to be named again here. `.facts-pair div:last-child` is
     (0,2,1) and outranks `.facts-pair div` (0,1,1), so restoring the third
     column with the plain selector left it hidden at every width. */
  .facts-pair div,.facts-pair div:last-child{
    display:flex;padding-left:clamp(1rem,3vw,2.5rem);
    border-left:1px solid var(--line-soft);
  }
}

/* ---------- generic section ---------- */
.section{padding:var(--sec) 0}
/* Two on the page, both carrying real copy from the live site. The rule is at
   most one per three sections; eight sections allows three. */
.eyebrow{
  display:block;font-size:.78rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);margin:0 0 .55rem;
}
.section + .section{border-top:1px solid var(--line-soft)}
.lede{
  font-family:var(--font-display);font-weight:400;
  font-size:clamp(1.22rem,2.3vw,1.55rem);line-height:1.45;
  color:var(--ink);max-width:44ch;letter-spacing:-.01em;
}
.section h2{font-size:clamp(1.7rem,3.2vw,2.45rem);line-height:1.1;margin:0 0 .9rem}
.section h3{font-size:1.18rem;margin:0 0 .5rem;font-weight:500}
.body-col{max-width:62ch;color:var(--text)}
.more{margin-top:1rem}
.more a{font-size:.95rem}

/* =============================================================
   THE LITTER - photographs first, captions underneath in plain
   type. No pill on the photo, no chip on the photo, no card.
   ============================================================= */
.litter-head{
  display:grid;gap:1rem;margin-bottom:2.5rem;
  grid-template-columns:1fr;
}
/* Headline and its explainer stack. A big left headline with a small paragraph
   floating in the right column is the templated section header this rebuild is
   getting rid of, not a layout to reproduce in a new skin. */
.litter-head p{max-width:58ch;margin:0}
.litter-head{margin-bottom:1.75rem;gap:.65rem}
/* THE LITTER TRACK (homepage only).
   The carousel the live build has, tuned to what Mark asked for in handoff
   section 23: two cards visible on a phone, not one portrait at a time. Each
   card is 62% of the track on the narrowest phones and 46% from 22rem, so two
   sit on screen and the third peeks, and that sliver is the only thing that
   tells a thumb to swipe. At 56rem the three fit exactly, the track stops
   overflowing, and site.js hides both arrows on its own. */
.carousel{position:relative}
.litter{
  list-style:none;display:flex;gap:1rem;
  overflow-x:auto;scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;padding-bottom:.25rem;
  align-items:stretch;
}
.litter::-webkit-scrollbar{height:0}
.litter li{display:flex;flex:0 0 62%;scroll-snap-align:start}
@media (min-width:22rem){.litter li{flex:0 0 46%}}
@media (min-width:56rem){
  .litter{gap:2rem;overflow-x:visible;scroll-snap-type:none}
  .litter li{flex:0 0 calc((100% - 4rem) / 3)}
}

/* =============================================================
   THE KITTEN AND CAT CARD
   One implementation for all six pages that use it: the homepage
   carousel, the kittens page, the three local landing pages and
   the kings and queens page. Nothing is printed over a cat: the
   status word and the photo-set link sit under the photograph,
   not on top of it.
   ============================================================= */
/* No height:100% here. As a grid item it made the percentage resolve against a
   row height that is itself content-derived, so a card whose blurb wrapped to a
   second line overflowed its own body and the price row stopped pinning to the
   bottom. Plain stretch, which is the default for both the grid and the flex
   track, gives the card the full row height with no circularity. */
.card{display:flex;flex-direction:column;width:100%;position:relative}
.card-media{
  position:relative;overflow:hidden;background:var(--paper-deep);
  aspect-ratio:1/1;
}
.card-media img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .7s cubic-bezier(.16,1,.3,1);
}
.card:hover .card-media img{transform:scale(1.035)}

.card-body{padding:.8rem 0 0;display:flex;flex-direction:column;flex:1}
.card h3{font-size:1.12rem;margin:0;line-height:1.2;font-weight:500}
@media (min-width:56rem){.card h3{font-size:1.32rem}}
.card h3 a{color:var(--ink);text-decoration:none}
/* stretched link: a click anywhere on the card counts. site.js relies on this,
   and it is why a Playwright element-click on the photograph is refused. */
.card h3 a::after{content:"";position:absolute;inset:0}
.card:hover h3 a{text-decoration:underline;text-decoration-thickness:1px}

/* EVEN CARDS.
   The cards carry different amounts of copy: some have a note, and the meta
   line wraps to a different number of lines depending on the breed name. Left
   alone every row below sits at a different height. The meta therefore reserves
   a fixed number of lines, and the price row is pushed to the bottom of the
   body, so the rows line up across a whole grid or track. If you change the
   meta format, re-measure this min-height. */
.card-meta{
  font-size:.82rem;color:var(--muted);margin:.25rem 0 0;line-height:1.45;
  min-height:calc(4 * 1.45em);
}
@media (min-width:24rem){.card-meta{min-height:calc(3 * 1.45em)}}
.card-body > p:not(.card-meta){
  font-size:.88rem;color:var(--text);margin:.4rem 0 0;line-height:1.5;
}

/* status word and photo-set link, on one line under the photograph */
.card-line{
  display:flex;align-items:baseline;justify-content:space-between;gap:.5rem;
  margin:.45rem 0 0;flex-wrap:nowrap;
}
/* class name kept because site.js reads .card-zoom[data-photos] to build the
   quick view from the card rather than from duplicated data attributes */
.card-zoom{
  position:relative;z-index:2;align-self:flex-start;text-align:left;
  background:none;border:0;padding:0;margin:0;
  font:inherit;font-size:.8rem;color:var(--accent);cursor:pointer;
  text-decoration:underline;text-underline-offset:.2em;white-space:nowrap;
}
.card-zoom:hover{color:var(--accent-dark)}
/* Status. Still under the photograph rather than stuck on a kitten's face, but
   it is the one thing on a card a buyer scans for, so it is no longer a grey
   whisper: a small tinted chip, sharp like everything else, with the colour
   carrying the meaning. Both combinations are checked for AA by the suite. */
.pill{
  font-size:.74rem;font-weight:500;letter-spacing:.01em;white-space:nowrap;
  padding:.2rem .5rem;background:var(--ok-bg);color:var(--ok-fg);
}
.pill-reserved{background:var(--warn-bg);color:var(--warn-fg)}

/* Health results on a breeding cat, under the role chip.
   Outlined, where .pill is filled. That is deliberate: a filled tinted chip means
   role or availability on this site, and stacking three filled chips in one narrow
   column blurs them into a single band of colour. A hairline outline separates
   "what this cat is" from "what this cat tested". Text is var(--text), which the
   contrast check already clears against the page ground. */
.card-tags{
  list-style:none;display:flex;flex-wrap:wrap;gap:.35rem;
  margin:.5rem 0 0;padding:0;
}
.card-tags li{
  font-size:.72rem;font-weight:500;letter-spacing:.01em;line-height:1.35;
  color:var(--text);border:1px solid var(--line);
  padding:.15rem .45rem;margin:0;white-space:nowrap;
}
.price{font-family:var(--font-display);font-size:1.02rem;color:var(--ink);font-weight:500}

/* The price row pins to the bottom of the body whatever sits above it.
   It STACKS on narrow columns and only goes side by side from 36rem. That is
   not a style preference: at a 179px column "Meet Chesnut" fits on one line and
   "Meet Bianca" fits on two, or the reverse depending on the name, so a wrapping
   row made the foot 37px tall on one card and 73px on its neighbour and the two
   stopped lining up. A name must not be able to change the layout. */
.card-foot{
  display:flex;flex-direction:column;align-items:stretch;gap:.5rem;
  margin-top:auto;padding-top:.6rem;border-top:1px solid var(--line-soft);
  flex-wrap:nowrap;
}
.card-foot .btn{width:100%}
@media (min-width:36rem){
  .card-foot{flex-direction:row;align-items:center;justify-content:space-between}
  .card-foot .btn{width:auto}
}
.btn-sm{padding:.4rem .8rem;font-size:.82rem;position:relative;z-index:2}

/* =============================================================
   REVIEWS - set large on the paper, split by a rule. No cards.
   ============================================================= */
/* Both reviews on ONE LINE, side by side, from 40rem. They were stacking into
   two tall blocks on anything narrower than a laptop. */
.quotes{display:grid;grid-template-columns:1fr;gap:0;margin-top:1rem}
.quote{padding:1.25rem 0 1rem;border-top:1px solid var(--line-soft)}
.quote:last-child{border-bottom:1px solid var(--line-soft)}
@media (min-width:24rem){
  .quotes{grid-template-columns:1fr 1fr;gap:0 1.5rem}
  .quote{border-bottom:0}
  .quote:last-child{border-bottom:0}
}
@media (min-width:40rem){.quotes{gap:0 2.5rem}}
.stars{color:#7f6118;font-size:.85rem;letter-spacing:.14em;margin-bottom:.6rem}
.quote blockquote{
  margin:0 0 .75rem;font-family:var(--font-display);font-weight:400;
  font-size:clamp(.95rem,2.4vw,1.28rem);line-height:1.5;color:var(--ink);
  letter-spacing:-.01em;max-width:38ch;
}
.quote figcaption{font-size:.88rem;color:var(--muted);line-height:1.5;margin-top:.15rem}
.quote figcaption b{color:var(--ink);font-weight:500;display:block}

/* ---------- FAQ ---------- */
.faq{max-width:62rem;margin-top:1rem}
.faq details{border-top:1px solid var(--line-soft)}
.faq details:last-of-type{border-bottom:1px solid var(--line-soft)}
.faq summary{
  list-style:none;cursor:pointer;padding:1.15rem 2.5rem 1.15rem 0;position:relative;
  font-family:var(--font-display);font-size:1.06rem;color:var(--ink);font-weight:500;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"";position:absolute;right:.35rem;top:50%;width:11px;height:11px;
  border-right:1.5px solid var(--muted);border-bottom:1.5px solid var(--muted);
  transform:translateY(-70%) rotate(45deg);transition:transform .2s ease;
}
.faq details[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.faq .faq-body{padding:0 0 1.35rem;max-width:60ch;color:var(--text)}

/* =============================================================
   PHOTO BANDS
   The photograph full bleed, a slate scrim over it, the type on top. Used
   twice on the homepage: the reserving section, left set, and the closing
   call to action, centred. Same material, two compositions, so the page does
   not read as the same block twice.

   The scrim is the brand accent darkened, not a generic black wash, and it is
   deep enough that white type clears AA over the lightest part of ANY of these
   photographs. That is verified by sampling rendered pixels with the type
   hidden, not by reading the CSS: a scrim is a pseudo-element and a photograph
   is not a background-color, so a computed-style check reports a number it
   never actually looked at.
   ============================================================= */
.photo-band{
  position:relative;isolation:isolate;overflow:hidden;
  display:grid;align-items:center;
  padding:clamp(3rem,7vw,4.75rem) 0;
  /* a solid base under the photograph, so wherever the photograph does not
     reach, the band is still the band rather than page ground */
  background:var(--accent-dark);
}
.photo-band img{
  position:absolute;inset:0;z-index:-2;
  width:100%;height:100%;object-fit:cover;object-position:50% 35%;
}
.photo-band::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(41,64,86,.88),rgba(26,42,58,.94));
}
.photo-band h2{color:#fff;line-height:1.08;margin:0 0 .75rem}
.photo-band p{color:rgba(255,255,255,.9)}
.photo-band .eyebrow{color:rgba(255,255,255,.72)}
/* .answer:not(.lede) p sets the ink colour and outranks a plain
   .photo-band .answer p, so the paragraph came out near-black on the scrim.
   Matched at the same weight here on purpose. */
.photo-band .answer:not(.lede) p,.photo-band .answer p{
  color:rgba(255,255,255,.92);font-family:var(--font-display);font-weight:400;
  font-size:clamp(1.08rem,1.9vw,1.3rem);line-height:1.5;letter-spacing:-.01em;
}
.photo-band .prose strong,.photo-band .prose b{color:#fff}
/* :not(.btn) is load bearing. Without it this rule outranks .btn-light, whose
   whole job is dark type on a white button, and the closing band's button
   becomes white on white. */
.photo-band .more a,.photo-band .in a:not(.btn){color:#fff}
.photo-band .more a:hover,.photo-band .in a:not(.btn):hover{color:rgba(255,255,255,.8)}

/* the reserving band: left set, and no taller than its own copy */
.photo-band-left{min-height:min(52svh,26rem)}
.photo-band-left .in{max-width:46rem}
.photo-band-left h2{font-size:clamp(1.7rem,3.2vw,2.45rem)}

/* THE CAT HAS TO FIT.
   Every photograph on this site is a 3:4 portrait. object-fit:cover in a band
   that is roughly 4:1 shows about an eighth of the picture's height, so the cat
   is guillotined wherever object-position happens to land. From 56rem the
   reserving band switches to CONTAIN and anchors the picture right: the whole
   animal is in frame at any width, whatever photograph is dropped in, and the
   slate base fills the rest of the band. The scrim is then weighted, opaque
   where the type sits on the left and thinner on the right so the cat reads. */
@media (min-width:56rem){
  .photo-band-left img{
    /* The img element is given the photograph's own shape and pinned to the
       right, so cover fills it exactly: the whole animal, no letterbox, and no
       slate gap inside the element. Then the left edge is masked so the picture
       dissolves into the band instead of sitting in it as a pasted rectangle.
       The mask percentages are reliable only because the element and the
       picture are the same shape. */
    inset:0 0 0 auto;left:auto;
    width:auto;height:100%;aspect-ratio:4/5;
    object-fit:cover;object-position:50% 50%;
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 62%);
    mask-image:linear-gradient(90deg,transparent 0,#000 62%);
  }
  .photo-band-left::after{
    background:
      linear-gradient(90deg,
        rgba(41,64,86,.97) 0%, rgba(41,64,86,.95) 40%,
        rgba(33,53,72,.80) 62%, rgba(28,46,63,.62) 100%),
      linear-gradient(180deg, rgba(41,64,86,.30), rgba(26,42,58,.42));
  }
}

/* the closing band: centred, and the last thing on the page */
.close-photo{
  min-height:min(58svh,30rem);
  place-items:center;text-align:center;
  padding-inline:var(--gut);
}
.close-photo .in{max-width:38rem}
.close-photo h2{font-size:clamp(1.85rem,3.8vw,2.75rem)}
.close-photo p{margin:0 auto 1.5rem;max-width:46ch}
.close-photo .btn-row{justify-content:center;margin-top:0}

/* Buttons on a slate band. The pages carry .btn-primary and .btn-ghost in
   their closing blocks; on the band the slate primary would be invisible and
   the ghost's hairline would vanish. Restyled here rather than by rewriting
   the markup on twelve pages, so no page decides its own button colour. */
.photo-band .btn-primary{background:#fff;color:var(--ink);border-color:#fff}
.photo-band .btn-primary:hover{background:var(--paper-deep);color:var(--ink);border-color:var(--paper-deep)}
.photo-band .btn-ghost{background:transparent;color:#fff;border-color:rgba(255,255,255,.6)}
.photo-band .btn-ghost:hover{background:rgba(255,255,255,.1);color:#fff;border-color:#fff}
/* Where a band carries two buttons the markup gives both a filled class, so
   they came out as two identical white blocks. The second one outlines. A lone
   button stays filled, which is why this is :not(:first-child) and not a class. */
.photo-band .btn-row .btn:not(:first-child){
  background:transparent;color:#fff;border-color:rgba(255,255,255,.6);
}
.photo-band .btn-row .btn:not(:first-child):hover{
  background:rgba(255,255,255,.12);color:#fff;border-color:#fff;
}

/* =============================================================
   CLOSING - the plain band, used by the other eighteen pages.
   ============================================================= */
.closing{padding:var(--sec) 0;border-top:1px solid var(--line-soft);background:var(--paper-deep)}
.closing-in{max-width:46rem}
.closing h2{font-size:clamp(2rem,4.4vw,3.1rem);line-height:1.05;margin:0 0 .85rem}
.closing p{max-width:44ch;color:var(--text)}


/* ---------- footer ---------- */
/* The footer is its own colour now: a pale wash of the brand slate rather than
   the page ground. It sits directly under the closing band, so it stays LIGHT
   on purpose. Repeating the band's slate here would put roughly nine hundred
   pixels of solid dark at the bottom of a site whose owner does not want heavy
   dark designs. */
.site-footer{
  background:var(--footer-bg);border-top:1px solid var(--footer-line);
  padding:clamp(2rem,5vw,4rem) 0 1.5rem;
}
/* MOBILE FOOTER.
   Four blocks stacked one per row made the footer 1,183px on a 390 phone: one
   and a half screens, a fifth of the whole page, for navigation nobody scrolled
   down to find. The brand block spans the width and the three link groups sit
   three across underneath it, so the footer is two bands rather than four.

   Three across beats two across at every width, which is not obvious and was
   measured rather than assumed: two columns are wider so fewer labels wrap, but
   the groups then need three rows instead of one, and the extra rows cost more
   than the wrapping saves. 390: 806px against 935. 320: 941 against 989.
   430: 777 against 820. No link is dropped; these are real internal links. */
/* Below 24rem three columns are 93px wide and "Persian kittens in Vancouver WA"
   breaks over four lines. It still measures shorter there, but shorter and
   unreadable is not the trade. Two columns below 24rem, three from there. */
.footer-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.4rem .9rem}
.footer-brand{grid-column:1 / -1}
.footer-grid > div:last-child{grid-column:1 / -1}
.footer-grid > div:last-child ul{display:grid;grid-template-columns:1fr 1fr;column-gap:1rem}
@media (min-width:24rem){
  .footer-grid{grid-template-columns:repeat(3,1fr)}
  .footer-grid > div:last-child{grid-column:auto}
  .footer-grid > div:last-child ul{display:block}
}
@media (min-width:44rem){.footer-grid{gap:1.75rem 1.5rem}}
@media (min-width:64rem){
  /* four across, brand included */
  .footer-grid{grid-template-columns:1.6fr 1fr 1fr 1fr;gap:3rem}
  .footer-brand{grid-column:auto}
}
.footer-brand h2{font-size:1.12rem;margin:0 0 .7rem}
.footer-brand p{font-size:.9rem;color:var(--muted);max-width:38ch;margin-bottom:.75rem}
.footer-brand a{color:var(--text)}
.site-footer h3{font-size:.88rem;margin:0 0 .6rem;color:var(--ink);font-weight:500}
.site-footer ul{list-style:none}
.site-footer li{margin-bottom:.38rem}
/* smaller and tighter on a phone, where every long label was wrapping to two
   lines inside a 165px column and each wrap cost a row */
.site-footer li a{font-size:.86rem;line-height:1.35;color:var(--muted);text-decoration:none}
@media (min-width:64rem){.site-footer li a{font-size:.92rem;line-height:1.5}}
.site-footer li a:hover{color:var(--ink);text-decoration:underline}
.footer-social{display:flex;gap:.6rem;margin-top:.85rem}
.footer-social a{
  width:34px;height:34px;display:grid;place-items:center;
  border:1px solid var(--footer-line);color:var(--text);
}
.footer-social a:hover{border-color:var(--ink);color:var(--ink)}
.footer-social svg{width:16px;height:16px;fill:currentColor}
.footer-legal{
  margin-top:1.75rem;padding-top:1.15rem;border-top:1px solid var(--footer-line);
  display:grid;gap:.4rem;font-size:.83rem;color:var(--muted);
}
.footer-legal p{margin:0}
.footer-legal a{color:var(--muted)}
@media (min-width:56rem){
  .footer-legal{grid-template-columns:auto 1fr auto;align-items:center;gap:2rem}
  .footer-legal p:nth-child(2){text-align:center}
  .footer-legal p:last-child{text-align:right}
}

/* =============================================================
   JS-driven components. Kept minimal and in the new language so
   the existing assets/js/site.js keeps working unchanged.
   ============================================================= */

/* mobile quick bar */
.quickbar{
  position:fixed;left:0;right:0;bottom:0;z-index:35;
  padding:.6rem var(--gut) calc(.6rem + env(safe-area-inset-bottom));
  background:var(--paper-veil);border-top:1px solid var(--line);
  backdrop-filter:blur(8px);
}
.quickbar-btn{
  display:block;text-align:center;background:var(--accent);color:#fff;
  padding:.85rem 1rem;text-decoration:none;font-size:.95rem;font-weight:500;
}
.quickbar-btn:hover{background:var(--accent-dark);color:#fff}
/* mobile-first: the bar exists by default and is removed at the desktop width,
   so every width breakpoint in this sheet stays min-width. */
body{padding-bottom:4.5rem}
@media (min-width:64rem){.quickbar{display:none}body{padding-bottom:0}}

.quick-sheet{position:fixed;inset:0;z-index:60;background:rgba(34,38,43,.5);display:grid;align-items:end}
.quick-sheet[hidden]{display:none}
.quick-panel{background:var(--paper);max-height:82svh;overflow:auto;padding:1.15rem var(--gut) 1.5rem}
.quick-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem}
.quick-head h2{font-size:1.15rem}
.quick-close{background:none;border:0;font-size:1.6rem;line-height:1;color:var(--muted);cursor:pointer}
.quick-list{list-style:none}
.quick-list li a{
  display:grid;grid-template-columns:56px 1fr auto;gap:.85rem;align-items:center;
  padding:.7rem 0;border-bottom:1px solid var(--line-soft);text-decoration:none;color:var(--text);
}
.quick-list img{width:56px;height:56px;object-fit:cover}
.qk-name{display:block;font-family:var(--font-display);color:var(--ink);font-size:1rem}
.qk-meta{display:block;font-size:.8rem;color:var(--muted)}
.qk-price{font-family:var(--font-display);color:var(--ink)}
.quick-all{display:block;text-align:center;margin-top:1.15rem}

/* kitten quick view modal */
.kmodal{position:fixed;inset:0;z-index:70;background:rgba(34,38,43,.62);display:grid;place-items:center;padding:1rem}
.kmodal[hidden]{display:none}
.kmodal-panel{
  position:relative;background:var(--paper);max-width:54rem;width:100%;
  max-height:90svh;overflow:auto;display:grid;grid-template-columns:1fr;
  box-shadow:0 30px 70px rgba(34,38,43,.3);
}
@media (min-width:48rem){.kmodal-panel{grid-template-columns:1fr 1fr}}
.kmodal-close{
  position:absolute;top:.4rem;right:.55rem;z-index:2;
  background:var(--paper);border:1px solid var(--line);width:34px;height:34px;
  font-size:1.35rem;line-height:1;color:var(--ink);cursor:pointer;
}
.kmodal-photo{aspect-ratio:1/1;overflow:hidden;background:var(--paper-deep)}
.kmodal-photo img{width:100%;height:100%;object-fit:cover}
.kmodal-thumbs{display:flex;gap:.4rem;padding:.4rem}
.kmodal-thumbs button{border:1px solid var(--line);background:none;padding:0;cursor:pointer;width:52px;height:52px}
.kmodal-thumbs button[aria-current="true"]{border-color:var(--ink)}
.kmodal-thumbs img{width:100%;height:100%;object-fit:cover}
.kmodal-body{padding:1.5rem}
.kmodal-body .eyebrow{font-size:.82rem;color:var(--muted);display:block;margin-bottom:.3rem}
.kmodal-body h2{font-size:1.6rem;margin:0 0 .5rem}
.kmodal-meta{font-size:.9rem;color:var(--muted);margin:0 0 .9rem}
.kmodal-blurb,.kmodal-parents{font-size:.95rem}
.kmodal-actions{margin-top:1.15rem;display:flex;gap:.6rem;flex-wrap:wrap}
.kmodal-more{margin-top:.75rem;font-size:.88rem}

/* gallery lightbox (shared markup, kept for parity) */
.lightbox{position:fixed;inset:0;z-index:75;background:rgba(20,22,25,.92);display:none;place-items:center;padding:1.5rem}
.lightbox[data-open="true"]{display:grid}
.lightbox img{max-width:100%;max-height:78svh;object-fit:contain}
.lightbox figcaption,.lightbox-count{color:#e8e2d6;font-size:.85rem;text-align:center;margin-top:.75rem}
.lightbox-close,.lightbox-nav{
  position:absolute;background:none;border:0;color:#fff;font-size:2rem;cursor:pointer;padding:.5rem .8rem
}
.lightbox-close{top:.5rem;right:.8rem}
.lightbox-prev{left:.4rem;top:50%;transform:translateY(-50%)}
.lightbox-next{right:.4rem;top:50%;transform:translateY(-50%)}

/* carousel arrows (site.js adds these if a track overflows) */
.carousel-nav{
  position:absolute;top:34%;z-index:3;width:38px;height:38px;
  display:grid;place-items:center;font-size:1.3rem;line-height:1;
  background:var(--paper-veil);border:1px solid var(--line);color:var(--ink);
  cursor:pointer;border-radius:0;
  transition:border-color .18s ease;
}
.carousel-nav:hover{border-color:var(--ink)}
.carousel-nav[hidden]{display:none}
.carousel-prev{left:-.35rem}
.carousel-next{right:-.35rem}

/* =============================================================
   MOTION
   The scroll reveal is gone at Mark's request. What remains is
   feedback only: a photograph lifts under the cursor, a button
   depresses when pressed. Nothing moves because the page scrolled,
   and nothing is hidden waiting to be revealed, so there is no
   longer any opacity rule that JavaScript has to switch off.
   ============================================================= */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .card-media img,.gallery img{transition:none}
  .btn{transition:none}
}

/* =============================================================
   THE OTHER EIGHTEEN PAGES
   Everything above this line came from the homepage draft. What
   follows covers every remaining component in the site, in the
   same language: no cards, one ground, hairlines, sharp corners.
   Class names are the ones already in the markup, so these pages
   needed almost no HTML changes.
   ============================================================= */

/* ---------- prose ---------- */
.prose{max-width:64ch}
/* Sixteen pages open with <div class="wrap prose" style="max-width:48rem">.
   Because that element is itself the .wrap, the inline max-width centred the
   whole intro in the window while every section below it started at the page
   gutter, so each page opened out of line with itself. The wrap goes back to
   full width and the reading measure is applied to its children instead.
   !important is here to beat the inline style without editing sixteen files;
   if the markup ever drops that inline style, drop this too. */
.wrap.prose{max-width:var(--maxw)!important}
.wrap.prose > *{max-width:62ch}
.wrap.prose > .split,.wrap.prose > .table-scroll,.wrap.prose > .grid,
.wrap.prose > .steps,.wrap.prose > .faq,.wrap.prose > .gallery{max-width:none}
.prose h2{margin-top:2.25rem}
.prose h3{margin-top:1.75rem;font-size:1.12rem}
.prose h2:first-child,.prose h3:first-child{margin-top:0}
.prose ul,.prose ol{margin:0 0 1.1em;padding-left:1.15rem}
.prose li{margin-bottom:.45rem}
.prose li:last-child{margin-bottom:0}
.prose dl{margin:0 0 1.1em}
.prose dt{font-weight:500;color:var(--ink)}
.prose dd{margin:0 0 .75rem}
.prose strong,.prose b{color:var(--ink);font-weight:500}
.prose hr{border:0;border-top:1px solid var(--line-soft);margin:2rem 0}

/* The lead answer on every page. Used to be a panel with a coloured rule down
   its left side, which is a documentation-site pattern, not a cattery. It is a
   lead paragraph now. The class stays because the WebPage schema on every page
   names .answer as its speakable selector. */
.answer{margin-bottom:1.25rem}
.answer > p:last-child{margin-bottom:0}
.answer.lede p,.section > .wrap > .answer > p:first-child{font-family:var(--font-display)}
.answer:not(.lede) p{
  font-family:var(--font-display);font-weight:400;
  font-size:clamp(1.12rem,2vw,1.35rem);line-height:1.5;
  color:var(--ink);letter-spacing:-.01em;max-width:46ch;
}

/* ---------- sections ---------- */
.section-tight{padding:clamp(2rem,4vw,3.25rem) 0 clamp(1.5rem,3vw,2.25rem)}
/* the old build alternated tinted bands down the page; one ground now, so this
   is a no-op kept so the class in the markup does not go stale */
.section-mist{background:transparent}
.section-head{margin-bottom:1.5rem}
.section-head h2{margin-bottom:.6rem}
.section-head p{max-width:60ch;margin:0}
.updated{font-size:.82rem;color:var(--muted);margin-top:1.75rem}

/* ---------- breadcrumb ---------- */
.breadcrumb{padding:.75rem 0 0;font-size:.82rem;color:var(--muted)}
.breadcrumb ol{list-style:none;display:flex;flex-wrap:wrap;gap:.4rem;margin:0;padding:0}
.breadcrumb li{display:flex;align-items:center;gap:.4rem}
.breadcrumb li + li::before{content:"/";color:var(--line)}
.breadcrumb a{color:var(--muted)}
.breadcrumb a:hover{color:var(--ink)}
.breadcrumb [aria-current]{color:var(--ink)}

/* ---------- grids ---------- */
.grid{display:grid;grid-template-columns:1fr;gap:1.5rem}
@media (min-width:22rem){.grid-3{grid-template-columns:repeat(2,1fr);gap:1.5rem 1rem}}
@media (min-width:36rem){.grid-2{grid-template-columns:repeat(2,1fr)}}
@media (min-width:48rem){.grid-3{grid-template-columns:repeat(3,1fr);gap:2.5rem 2rem}}
@media (min-width:56rem){.grid-2{gap:0 3rem}}

/* ---------- split ---------- */
.split{display:grid;grid-template-columns:1fr;gap:1.75rem;margin-top:1.5rem}
.split-text h3{border-top:1px solid var(--line);padding-top:.85rem;margin-bottom:.6rem}
.split-text p,.split > .prose p{max-width:60ch}
.split-media{overflow:hidden}
/* Square on phones, portrait from 48rem. Every photograph on this site is a
   portrait phone shot: a wide frame keeps about a quarter of the picture and
   cuts the cat in half. Handoff sections 10 and 25 are the record of getting
   this wrong twice. Do not widen these frames. */
.split-media img{
  width:100%;height:auto;display:block;
  aspect-ratio:1/1;object-fit:cover;object-position:50% 35%;
}
@media (min-width:48rem){
  .split{grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:3rem;align-items:start}
  /* 5:4, not 4:5. A portrait frame here stood a foot taller than the text
     beside it and left a slab of empty page under the copy. The crop comes off
     the TOP, which on these photographs is empty backdrop above the cat. */
  .split-media img{aspect-ratio:5/4;object-position:50% 68%}
}

/* the reserving section runs the other way, so two split sections in a row do
   not read as the same layout twice */
.split-flip .split-media{order:-1}
@media (min-width:48rem){
  .split-flip{grid-template-columns:minmax(0,1fr) minmax(0,1.25fr)}
  .split-flip .split-text h3{border-top:0;padding-top:0}
}
.split-text .eyebrow{margin-bottom:.4rem}
.split-text h2{font-size:clamp(1.6rem,3vw,2.25rem);margin:0 0 .8rem}

/* ---------- numbered steps (purchasing, grooming, the three local pages) ---------- */
.steps{list-style:none;counter-reset:s;margin:1.5rem 0 0;padding:0}
.steps li{
  counter-increment:s;
  display:grid;grid-template-columns:1.5rem 1fr;gap:0 1rem;
  padding:1.15rem 0;border-top:1px solid var(--line-soft);margin:0;
}
.steps li:last-child{border-bottom:1px solid var(--line-soft)}
.steps li::before{
  content:counter(s);font-family:var(--font-display);
  font-size:1rem;color:var(--muted);line-height:1.55;
}
/* both children sit in column two; without this the paragraph auto-places into
   the number column and wraps one word per line */
.steps h3,.steps p{grid-column:2}
.steps h3{margin:0 0 .3rem;font-size:1.08rem;font-weight:500}
.steps p{margin:0 0 .6rem;max-width:60ch}
.steps p:last-child{margin-bottom:0}
@media (min-width:56rem){
  .steps li{grid-template-columns:1.75rem 15rem 1fr;gap:0 1.75rem;align-items:baseline}
  .steps p{grid-column:3;grid-row:1}
  .steps h3{margin:0}
}

/* ---------- callout ---------- */
.callout{
  margin:1.5rem 0;padding:1.15rem 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.callout p{max-width:62ch;margin-bottom:.75rem}
.callout p:last-child{margin-bottom:0}

/* ---------- tables ---------- */
.table-scroll{overflow-x:auto;margin:1.5rem 0;-webkit-overflow-scrolling:touch}
.table-scroll table{border-collapse:collapse;width:100%;min-width:34rem;font-size:.94rem}
.table-scroll caption{
  text-align:left;font-size:.85rem;color:var(--muted);
  margin-bottom:.65rem;caption-side:top;
}
.table-scroll th,.table-scroll td{
  text-align:left;padding:.7rem 1rem .7rem 0;vertical-align:top;
  border-bottom:1px solid var(--line-soft);
}
.table-scroll th{
  font-family:var(--font-display);font-weight:500;color:var(--ink);
  border-bottom:1px solid var(--line);white-space:nowrap;
}
.table-scroll tbody tr:last-child td{border-bottom:0}

/* ---------- closing call to action ---------- */
/* One component for the closing block on every page. The homepage used to have
   its own full-bleed photo version with centred white type over a dark band;
   it uses this now, so there is one closing block on the site instead of two. */
.cta-band{
  padding:clamp(2rem,4.5vw,3.25rem) 0;
  border-top:1px solid var(--line);
  max-width:46rem;
}
.cta-band h2{font-size:clamp(1.7rem,3.6vw,2.5rem);line-height:1.08;margin:0 0 .7rem}
.cta-band p{max-width:46ch}
.btn-row{display:flex;flex-wrap:wrap;gap:.65rem;margin-top:1.25rem}
.btn-row .btn{flex:0 0 auto}

/* ---------- gallery ---------- */
.gallery{display:grid;grid-template-columns:repeat(2,1fr);gap:.65rem;margin-top:1.5rem}
@media (min-width:36rem){.gallery{grid-template-columns:repeat(3,1fr);gap:1rem}}
@media (min-width:64rem){.gallery{grid-template-columns:repeat(4,1fr)}}
.gallery button{
  padding:0;border:0;background:var(--paper-deep);cursor:pointer;
  overflow:hidden;display:block;border-radius:0;
}
.gallery img{
  width:100%;height:100%;aspect-ratio:1/1;object-fit:cover;display:block;
  transition:transform .7s cubic-bezier(.16,1,.3,1);
}
.gallery button:hover img{transform:scale(1.04)}

/* ---------- contact page ---------- */
/* Three blocks, source order is the reading order on a phone, explicitly placed
   at 56rem so the form sits beside the prose rather than three screens below it.
   Handoff section 25 is why: the form used to start 2,604px down a phone. */
.contact-split{display:grid;grid-template-columns:1fr;gap:2rem}
@media (min-width:56rem){
  .contact-split{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:3.5rem;align-items:start}
  .contact-intro{grid-column:1;grid-row:1}
  .contact-form-col{grid-column:2;grid-row:1/span 2}
  .contact-more{grid-column:1;grid-row:2}
}
.dl-facts{margin:1.25rem 0 0}
.dl-facts div{padding:.7rem 0;border-top:1px solid var(--line-soft)}
.dl-facts div:last-child{border-bottom:1px solid var(--line-soft)}
.dl-facts dt{font-size:.82rem;color:var(--muted);margin:0}
.dl-facts dd{margin:.1rem 0 0;color:var(--ink)}

.form{border-top:1px solid var(--line);padding-top:1.5rem}
.form h2{font-size:1.35rem;margin-bottom:1rem}
.field{display:grid;gap:.4rem;margin-bottom:1.15rem}
/* label above the input, always. No placeholder-as-label anywhere on this site. */
.field label{font-size:.88rem;color:var(--ink);font-weight:500}
.field input,.field select,.field textarea{
  font:inherit;font-size:1rem;color:var(--text);
  background:var(--white);border:1px solid var(--line);border-radius:0;
  padding:.7rem .8rem;width:100%;
}
.field textarea{min-height:9rem;resize:vertical}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent);
}
/* muted enough to read as helper text, dark enough to pass AA on the ground */
.hint{font-size:.82rem;color:var(--muted)}
.form .btn{margin-top:.35rem}

/* ---------- reviews page ---------- */
/* The full reviews run long, so they are set at reading size rather than at the
   homepage's display size, and the grid gives them a column each. */
.grid-2 .quote{padding:1.5rem 0}
.grid-2 .quote blockquote{
  font-family:var(--font-body);font-size:1rem;line-height:1.65;
  letter-spacing:0;max-width:62ch;color:var(--text);
}

/* ---------- kings and queens cards ---------- */
/* These carry no price and no photo chip, so the foot rule would draw a line
   under nothing. */
.card:not(.kitten) .card-foot{border-top:0;padding-top:0}
