/* Bless Your Paws Puppies - v2
   Character: a pressed-flower garden album. Blush paper, forest ink, sage stems,
   seed-packet cards. Wide gallery-first layout: the puppies are the product, so
   photography gets the room. Deliberately NOT Kingdom Family Companions'
   cream/espresso ledger. */

@font-face{font-family:"Lora";font-style:normal;font-weight:400 700;
  font-display:swap;src:url("fonts/lora-variable.woff2") format("woff2")}
@font-face{font-family:"Lora";font-style:italic;font-weight:400 600;
  font-display:swap;src:url("fonts/lora-italic.woff2") format("woff2")}
@font-face{font-family:"Mulish";font-style:normal;font-weight:400 700;
  font-display:swap;src:url("fonts/mulish-variable.woff2") format("woff2")}

:root{
  --forest:#223d2c; --forest-soft:#34523f;
  --sage:#7f8e79; --sage-deep:#6d7a68; --sage-light:#a8b89e;
  --rose:#feb5bc; --pink-pale:#fbc4db;
  --paper:#fdf9f9; --paper-raise:#faf2f1; --rule:#e4d7d6;
  --draft:#8a5512; --draft-bg:#f8ecd9;
  --maxw:1560px;
  color-scheme: light only;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--paper);color:var(--forest);
  font-family:"Mulish",system-ui,sans-serif;font-size:17px;line-height:1.65}
/* light-mode lock: iOS Safari inverts unlocked pages into dark-on-dark */
@media (prefers-color-scheme: dark){
  :root{--paper:#fdf9f9;--forest:#223d2c}
  body{background:var(--paper);color:var(--forest)}
}
img{max-width:100%;height:auto;display:block}
/* img{display:block} above outranks the UA stylesheet's [hidden]{display:none},
   which silently un-hides carousel slides and stacks every photo. restore it. */
[hidden]{display:none !important}

/* Fraunces ships wonky f/g/y by default. WONK 0 turns the swashes off, which is
   the fix for the odd-looking f. SOFT rounds the terminals a little. */
/* Third display face. Fraunces draws a hooked descending f; Petrona has an
   unconventional J. Lora is a transitional serif with entirely normal f, g, j, y
   and J, which is what this client needs. Ligatures and swashes off as well. */
h1,h2,h3,.display,.packet-name,.price,.q-btn{font-family:"Lora",Georgia,serif;
  font-feature-settings:"liga" 0,"dlig" 0,"swsh" 0,"calt" 0}
h1,h2,h3{font-weight:600;line-height:1.14;margin:0 0 .5rem;text-wrap:balance;
  letter-spacing:-.01em}
h1{font-size:clamp(2rem,3.6vw,3.1rem)}
h2{font-size:clamp(1.55rem,2.3vw,2.2rem)}
h3{font-size:1.2rem}
p{margin:0 0 1rem}
a{color:var(--forest)}
.wrap{width:min(94%,var(--maxw));margin-inline:auto}
/* grid children default to min-width:auto, so a wide child can push a track past
   the viewport. this stops the sideways scroll. */
.pgrid>*,.grid-2>*,.grid-3>*,.foot-grid>*,.puppy-top>*{min-width:0}
.eyebrow{font-size:.74rem;letter-spacing:.17em;text-transform:uppercase;
  color:var(--sage-deep);margin:0 0 .5rem;font-weight:700}
.lede{font-size:clamp(1.05rem,1.25vw,1.22rem);color:var(--forest-soft)}
.fine{font-size:.88rem;color:var(--sage-deep)}
.center{text-align:center}
.prose{max-width:70ch}

.chip{display:inline-block;font-size:.72rem;font-weight:700;letter-spacing:.05em;
  padding:.2rem .55rem;border-radius:3px;vertical-align:.12em;margin-left:.35rem}
.chip-draft{background:var(--draft-bg);color:var(--draft);border:1px dashed var(--draft)}
.chip-sample{background:var(--pink-pale);color:var(--forest);border:1px dashed var(--sage)}

/* ---------- header ---------- */
.site-head{background:var(--paper);border-bottom:1px solid var(--rule);
  position:sticky;top:0;z-index:40}
.head-row{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  min-height:82px}
.brand{display:flex;align-items:center;text-decoration:none;flex:none}
/* the primary lockup is 1596x474, so ~3.4:1. At 76px it stood 6px shy of the 82px
   bar and read as though it were bursting out of it. 56px leaves 13px of air above
   and below, and the two smaller breakpoints hold that same 13px. */
.brand img{height:56px;width:auto;display:block}
.nav{display:flex;align-items:center;gap:.1rem;flex-wrap:wrap;justify-content:flex-end}
.nav a{text-decoration:none;padding:.5rem .6rem;border-radius:3px;font-size:.94rem;
  white-space:nowrap}
.nav a:hover{background:var(--paper-raise)}
.nav a.nav-cta{background:var(--forest);color:var(--paper);margin-left:.4rem;
  padding:.6rem 1rem}
.nav a.nav-cta:hover{background:var(--forest-soft)}
.nav-toggle{display:none;background:none;border:1.5px solid var(--forest);
  border-radius:3px;padding:.5rem .7rem;font:inherit;font-weight:700;
  color:var(--forest);cursor:pointer;min-height:44px}

/* ---------- hero: photo drifts behind the copy ----------
   The image is oversized and anchored right, then its left edge is dissolved with
   a mask so it fades into the paper underneath the headline instead of stopping at
   a hard edge. Copy sits above it, over plain paper, so contrast is never at risk. */
.hero{padding:0;position:relative;overflow:hidden;isolation:isolate}
/* the source is 3:2. at 1026x634 the frame was wider than that, so cover was
   trimming the puppy top and bottom. a taller frame lands nearer 3:2 and keeps him
   whole. */
.hero-drift{position:relative;display:grid;align-items:center;
  min-height:clamp(24rem,34vw,32rem)}
/* every direct child shares the one cell. the grid child here is the .wrap, NOT
   .hero-copy, so targeting .hero-copy left the .wrap auto-placed into row 2 and
   stacked the copy underneath the photo. */
.hero-drift>*{grid-area:1/1}
/* width stays proportional so the photo always reaches back behind the copy. a
   fixed rem cap broke the drift at wide viewports: the photo pinned right while the
   copy stayed in the centred wrap, and the two separated. 160vh at 3:2 keeps the
   height near a viewport so it cannot run away vertically either. */
/* 16:9 rather than the source 3:2, which buys a hero roughly a sixth shorter at the
   same width with the fade intact. The height it loses is blanket and background, not
   the puppy.
   object-position is pinned to 0% deliberately, and it is the only value that should
   be used here. havilah-01 is 1731x1154 and the top of his head is 5.4% down the
   frame, so the source itself carries very little headroom. Cover shows 84.4% of the
   source height, so every percent of object-position eats 0.19% of that headroom:
   at 22% the crown sat 2.3% below the top edge and read as though he were about to
   hit his head, and by 34% it was clipped outright. At 0% the crown sits 6.4% down,
   which is the most this photograph can give. More air than that needs a different
   photograph, not a different crop. */
.hero-photo{justify-self:end;align-self:center;
  width:min(74%,178vh);aspect-ratio:16/9;z-index:0;pointer-events:none}
.hero-photo img{width:100%;height:100%;object-fit:cover;object-position:50% 0%;
  -webkit-mask-image:linear-gradient(to right,transparent 0%,rgba(0,0,0,.25) 14%,
    #000 42%,#000 100%);
  mask-image:linear-gradient(to right,transparent 0%,rgba(0,0,0,.25) 14%,
    #000 42%,#000 100%)}
/* a narrower measure: the headline was setting two very long lines */
.hero-drift>.wrap{z-index:1;align-self:center}
.hero-copy{position:relative;width:min(33rem,46%);
  padding:clamp(2.25rem,4vw,3.5rem) 0}
.hero-copy .lede{max-width:30rem}
.hero-copy h1{text-shadow:0 1px 0 var(--paper)}
/* older engines without mask-image get a plain right-hand photo rather than a
   full-bleed image with copy on top of it */
@supports not ((mask-image:linear-gradient(#000,#000)) or (-webkit-mask-image:linear-gradient(#000,#000))){
  .hero-photo{width:54%}
}

.hero-split{display:grid;grid-template-columns:.68fr 1.32fr;gap:clamp(2rem,4vw,4rem);
  align-items:center}
/* desktop_only_img wraps its <img> in a <picture> so a phone never downloads a photo it
   hides. The PICTURE therefore has to BE the layout box and carry the layout classes,
   because every placement rule here is a child combinator: .hic>.hic-photo, .grid-2>*,
   .pgrid>*. display:contents was tried first and is wrong for exactly that reason. It
   makes the <img> the grid ITEM while those selectors still see it as a grandchild, so
   .hic>.hic-photo matched nothing, the photo was auto-placed into the heading's row, and
   that row grew to the height of the picture: the heading pinned to the top and the copy
   pushed 650px below it. The layout looked broken in a way that measuring widths did not
   catch, because the widths were all correct.
   overflow:hidden so .framed's border-radius clips the photo, and the <img> simply fills
   whatever box the classes on the picture produce. */
picture{display:block;overflow:hidden}
picture>img{display:block;width:100%;height:100%;object-fit:cover}
.framed{width:100%;max-width:100%;border-radius:6px;border:1.5px solid var(--forest);
  box-shadow:0 2px 0 var(--sage-light)}
.hero-split .framed{aspect-ratio:3/2;object-fit:cover}
/* section imagery: give photos real presence, they are the product */
.grid-2 .framed{aspect-ratio:3/2;object-fit:cover}
/* A photo whose subject runs the full width of the frame cannot be cropped to 3/2. This
   keeps the litter photo's own 2.16:1 so no puppy is cropped out of the litter. Declared
   after the .grid-2 rule above because both are (0,2,0) and the later one wins. */
.grid-2 .framed.keep-wide{aspect-ratio:1536/1024}
/* a portrait original keeps a portrait frame. cropping a 3:4 photo of people to
   3:2 cuts off either their heads or their torsos. */
/* a 16:9 original keeps a 16:9 frame, so none of the subject is cropped away */
.framed.wide16{aspect-ratio:1672/941;object-fit:cover}
/* .78/1.22 puts a feature photo near 780px in a 1339 wrap. .62/1.38 gave 894px,
   which overwhelmed the paragraph beside it. */
/* 1.30fr rather than 1.22fr: the photographs sit in this column and were asked to be
   larger. Both variants move together so the About photo and the one below it stay the
   same size as each other. */
/* minmax, not a plain fraction, and not a second breakpoint either.
   The .70fr was measured at 1440, where it gives the text 454px. It was never checked just
   above the mobile breakpoint: at 901px it gave the text 282px, about 4.7 words a line,
   while the photo took 524px. One pixel narrower the mobile rule hands that same text the
   full 846px, so there was a 3x cliff across a 1px change of viewport.
   Stepping the ratio at a new breakpoint only moves the cliff somewhere else. A floor on
   the text column removes it: below roughly 1200px the floor wins and the photo yields,
   above it the fraction wins and the 1440 design is untouched. Fluid, no step. */
.grid-2.narrow-left{grid-template-columns:minmax(26rem,.70fr) 1.30fr}
/* a softer lean than narrow-left/right, for a photo that should lead without dominating */
/* Same floor, same reason. `hic-flip` is what makes this bite: the modifier names the
   PHOTO side, so lean-left + flip puts the body copy in the .85fr column, which was 342px
   at 901px with the photo on 463px. Any modifier added later needs the floor on whichever
   column can end up holding text, which with flip in play is both of them. */
.grid-2.lean-left{grid-template-columns:1.15fr minmax(26rem,.85fr)}
.grid-2.lean-right{grid-template-columns:minmax(26rem,.85fr) 1.15fr}
/* narrow-RIGHT means the right column is the narrow one, so the photo-left rows
   get the wide half. It had been left identical to narrow-left, which is why the
   inverted rows put their photo in the small column. */
.grid-2.narrow-right{grid-template-columns:1.30fr minmax(26rem,.70fr)}
.btn{display:inline-flex;align-items:center;text-decoration:none;border-radius:3px;
  font-weight:700;padding:.75rem 1.35rem;border:1.5px solid var(--forest);
  font-size:1rem;min-height:48px}
.btn-primary{background:var(--forest);color:var(--paper)}
.btn-primary:hover{background:var(--forest-soft)}
/* forest on rose measures 7.08:1. White on rose is 1.67:1 and is forbidden here, so a
   pink button keeps a forest label. */
.btn-pink{background:var(--rose);color:var(--forest);border-color:var(--rose)}
.btn-pink:hover{background:var(--pink-pale);border-color:var(--pink-pale)}
.btn-ghost{background:transparent;color:var(--forest)}
.btn-ghost:hover{background:var(--paper-raise)}
.btn-row{display:flex;gap:.8rem;flex-wrap:wrap;margin-top:1.4rem}
.btn-row>.btn{text-align:center}
/* a page title living inside the left column of a feature row, so the headline sits
   beside the photo rather than stacked above the whole row */
.col-title{margin-bottom:1.25rem}
.col-title h1,.col-title h2{margin-bottom:.35rem}
/* A heading inside a floored column must not be sized by the viewport.
   The page-level h1 is clamp(2rem,3.6vw,3.1rem), which is right for a full-width hero and
   wrong here: now that the text column has a 26rem floor, 3.6vw kept growing the type while
   the column stayed 416px, so the same heading took 2 lines at 1120, 3 at 1280, 3 at 1440
   and 2 again at 1600. Non-monotonic, which is the tell that type and container are keyed
   to different things. A gentler curve keeps it to two lines across the whole band.
   balance evens those two lines out rather than leaving one orphaned word. */
.col-title h1{font-size:clamp(1.85rem,2.6vw,2.5rem)}
.col-title h1,.col-title h2{text-wrap:balance}
/* The label is 31 characters and will not fit one line in a 416px column at the base
   button size. It was already nowrap on mobile; there is no reason that stops at 901px. */
.breed-link{white-space:nowrap;font-size:.92rem;padding-left:1rem;padding-right:1rem}
.col-title .eyebrow{margin-bottom:.4rem}

/* heading on the left, action on the right, on one baseline. for sections where a
   closing button under the grid read as parked rather than placed. */
.section-head{display:flex;align-items:flex-end;justify-content:space-between;
  gap:1.5rem;flex-wrap:wrap;margin-bottom:1.75rem}
.section-head>div{min-width:0}
.section-head h2{margin-bottom:.25rem}
.section-head .btn{flex:none}

/* a section-closing action, tied to the block above it by a rule rather than
   floating loose underneath */
.section-cta{display:flex;justify-content:center;align-items:center;gap:1rem;
  margin-top:clamp(1.75rem,3vw,2.75rem);padding-top:clamp(1.5rem,2.5vw,2rem);
  border-top:1px solid var(--rule);flex-wrap:wrap}
.section-cta p{margin:0;color:var(--sage-deep);font-size:.95rem}
.band-forest .section-cta{border-top-color:rgba(253,249,249,.28)}
.band-pink .section-cta,.band-raise .section-cta{border-top-color:var(--sage-light)}

.sprig{display:block;margin:0 auto;color:var(--sage)}

section{padding:clamp(2.5rem,4vw,4rem) 0}
.band-forest{background:var(--forest);color:var(--paper)}
.band-forest h2,.band-forest h3{color:var(--paper)}
.band-forest .eyebrow{color:var(--rose)}
.band-forest p,.band-forest li{color:#e9ded9}
.band-forest a{color:var(--pink-pale)}
.band-pink{background:var(--pink-pale)}
/* a coloured band whose height is set by a tall photo reads as a big slab. this
   trims the vertical padding so the colour hugs the content. */
section.band-tight{padding-top:clamp(1.5rem,2.5vw,2.25rem);
  padding-bottom:clamp(1.5rem,2.5vw,2.25rem)}
.band-tight .grid-2{align-items:center}
.band-raise{background:var(--paper-raise)}

/* ---------- cards ---------- */
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
  gap:clamp(1.25rem,2vw,2rem)}
/* an exact column count so a small litter spans the full width instead of
   clustering at the left with dead space beside it. these MUST be unwound on
   narrow screens: pinned at 4 columns on a phone they force a horizontal scroll. */
@media (min-width:1100px){
  .pgrid.cols-3{grid-template-columns:repeat(3,1fr)}
  .pgrid.cols-4,.pgrid.cols-7{grid-template-columns:repeat(4,1fr)}
}
@media (min-width:700px) and (max-width:1099px){
  .pgrid.cols-3,.pgrid.cols-4,.pgrid.cols-7{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:699px){
  .pgrid,.pgrid.cols-3,.pgrid.cols-4,.pgrid.cols-7{grid-template-columns:1fr}
}
.grid-2>*{min-width:0}
/* ---- heading, photo, copy -------------------------------------------------
   A section stacked on a phone should read heading, then the photo, then the copy.
   Left to auto-placement it reads either heading-copy-photo or photo-heading-copy
   depending on which column comes first in the source, and both are wrong: the first
   buries the photo under a wall of text, the second opens with a photo of nothing in
   particular before you know what you are looking at.

   So the source order IS heading, photo, copy, and mobile needs no rules at all. The
   two-column desktop arrangement is scoped with min-width rather than set
   unconditionally and undone below 900px. That is deliberate and it is the third time
   this bit: an undo rule like `.hic>*` loses on specificity to `.hic>.hic-head`, and a
   surviving `grid-column:2` then creates an implicit second track that eats the whole
   row. Scope desktop-only placement to desktop and there is nothing to override.

   Add `hic-flip` when the photo belongs in the first column instead of the second. */
/* `.grid-2` sets align-items:center and is declared after this block, so a bare
   `.hic` at (0,1,0) loses and every item was vertically centred in its row, which is
   what pushed a heading away from its own paragraph. Compound selector to win. */
.grid-2.hic{align-items:start}
/* the copy blocks sit at the top of their own content-sized rows; the photo centres
   itself across the full span, which is what matters when the TEXT is the taller side
   and the spacer rows have collapsed to zero */
.hic>.hic-head,.hic>.hic-copy{align-self:start}
/* the heading and the copy are separate rows here, so the grid's row-gap lands between
   them and stacks with the heading's bottom margin and the paragraph's top margin. Zero
   the row-gap and let the margins alone set the distance. */
.grid-2.hic{row-gap:0}
.hic>.hic-head{margin-bottom:.9rem}
.hic>.hic-head.col-title{margin-bottom:.9rem}
.hic>.hic-copy>:first-child{margin-top:0}
.hic>.hic-photo{align-self:center}
@media (min-width:901px){
  /* Four rows: a flexible spacer, the heading, the copy, another flexible spacer. The
     two 1fr rows split the leftover height of the spanning photo evenly, which centres
     the heading and copy as a GROUP while leaving them adjacent to each other.
     `auto 1fr` was wrong in the other direction: it put all the slack in the copy's row
     and pinned the text to the top of the picture. Plain `auto auto` is wrong too, since
     the slack then lands between the heading and its own paragraph. */
  .hic{grid-template-rows:1fr auto auto 1fr}
  .hic>.hic-head{grid-column:1;grid-row:2}
  .hic>.hic-copy{grid-column:1;grid-row:3}
  .hic>.hic-photo{grid-column:2;grid-row:1 / -1}
  .hic-flip>.hic-head{grid-column:2;grid-row:2}
  .hic-flip>.hic-copy{grid-column:2;grid-row:3}
  .hic-flip>.hic-photo{grid-column:1;grid-row:1 / -1}
}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1.5rem,3vw,3rem);
  align-items:center}
.grid-3{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:clamp(1.25rem,2vw,2rem)}
.packet{position:relative;background:#fff;border:1.5px solid var(--forest);
  border-radius:4px;padding:11px;display:flex;flex-direction:column;height:100%}
.packet::before{content:"";position:absolute;inset:5px;border:1px dashed var(--sage);
  border-radius:2px;pointer-events:none}
.packet img{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:2px}
/* parent originals run from 0.75 to 1.42 aspect. a square frame crops both
   orientations gently, where a 4/5 frame gutted the landscape ones. */
.packet.parent img{aspect-ratio:1/1;object-position:50% 30%}
/* The facts block under a parent photo is reference, not a tap target: denser rows and
   less padding, so the card is a summary rather than a half-empty panel. .facts li gets
   min-height:44px under 900px for touch, which is right for the interactive lists and
   wrong here, so it is released for these rows only. */
.packet.parent .packet-body{padding:.85rem .8rem .8rem;gap:.1rem}
.packet.parent .facts{margin-top:.5rem!important;border-top:1px solid var(--rule)}
.packet.parent .facts li{padding:.34rem 0;min-height:0;font-size:.9rem}
.packet.parent .facts li:last-child{border-bottom:0;padding-bottom:0}
.parent-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:clamp(1.25rem,2vw,2rem);align-items:stretch}
.parent-grid.row-4{grid-template-columns:repeat(4,1fr)}
.parent-grid .packet-body{gap:.2rem;flex:1;display:flex;flex-direction:column;
  padding:.9rem .7rem .55rem}
/* the facts table fills, the note sits on the floor of the card, so a card with
   three rows and a card with five still line their notes up */
.parent-grid .facts{margin:.5rem 0 0}
.parent-grid .facts li{padding:.3rem 0;font-size:.9rem}
.parent-grid .facts li:last-child{border-bottom:none}
.parent-grid .packet-name{margin-bottom:.1rem}
.parent-grid .packet-meta{margin-bottom:.15rem}
.parent-grid .dogmeta{margin-bottom:.2rem}
.parent-grid .pnote{margin-top:auto;padding-top:.6rem;font-size:.83rem;
  line-height:1.45;color:var(--sage-deep);border-top:1px solid var(--rule)}
.parent-grid .pnote:empty{display:none}
.dogmeta{font-size:.86rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--sage-deep);font-weight:700;margin:0 0 .35rem}
.packet-body{padding:1rem .6rem .5rem;display:flex;flex-direction:column;gap:.2rem;
  flex:1}
.packet-name{font-size:1.35rem;font-weight:700;margin:0}
.packet-meta{font-size:.9rem;color:var(--sage-deep);margin:0}
.packet-row{display:flex;justify-content:space-between;align-items:baseline;
  gap:.5rem;margin-top:auto;padding-top:.6rem}
.price{font-size:1.2rem;font-weight:700}
.status{font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--forest);background:var(--sage-light);padding:.2rem .55rem;border-radius:3px}
/* forest on rose is 7.08:1. An adopted card has no price, so the badge sits alone and
   carries the row on its own. */
.status-adopted{background:var(--rose)}
.packet-row:has(> .status-adopted:only-child){justify-content:flex-start}
.packet-link.is-adopted .packet{border-style:solid}
.packet-link.is-adopted img{filter:saturate(.92)}
a.packet-link{text-decoration:none}
a.packet-link:hover .packet{border-color:var(--sage-deep)}

/* flex column so the body fills the card and the two cards close on the same line even
   if one caption wraps at a width the other does not */
.door{display:flex;flex-direction:column;text-decoration:none;
  border:1.5px solid var(--forest);border-radius:6px;overflow:hidden;background:#fff}
.door-body{flex:1}
.door img{width:100%;aspect-ratio:3/2;object-fit:cover}
/* The dead band under the last line was the paragraph's own bottom margin sitting on
   top of the padding, not the padding alone. Padding on the parent stops the margin
   collapsing out, so it has to be removed explicitly. */
.door-body{padding:1.15rem 1.4rem 1.25rem}
.door-body>:last-child{margin-bottom:0}
.door:hover{border-color:var(--sage-deep)}

/* ---------- facts ---------- */
.facts{list-style:none;margin:0 0 1.25rem;padding:0;border-top:1px solid var(--rule)}
.facts li{display:flex;justify-content:space-between;gap:1rem;padding:.6rem 0;
  border-bottom:1px solid var(--rule);font-size:.97rem}
a[href^="mailto:"]{overflow-wrap:anywhere}
.foot-grid>*,.foot-top>*{min-width:0}

/* flex and grid children default to min-width:auto, so an unbreakable value with no
   break opportunity in it sets the min-content width of whatever column it sits in.
   The email address did exactly that and pushed the contact page 48px wider than a
   320px screen, dragging the photo beside it out with the column. */
.facts li{min-width:0}
.facts .k{color:var(--sage-deep)}
.facts .v{text-align:right;font-weight:700;min-width:0;overflow-wrap:anywhere}
/* In a .hic the copy column is competing with a photo's height, and these rows are
   reference data rather than tap targets. 6px a side instead of 9.6 gives 39px rows and
   returns 50px to the column. */
.hic-copy .facts li{padding:.375rem 0}
/* A lone button in a .hic copy column spans it, so it lines up with the facts list and
   the lede above rather than floating at its label width. .btn sets no justify-content,
   so the label needs centring once the box is wider than the text. */
.hic-copy .btn-row>.btn{width:100%;justify-content:center}
.checklist{list-style:none;margin:0;padding:0}
.checklist li{padding:.4rem 0 .4rem 1.7rem;position:relative;font-size:.97rem}
.checklist li::before{content:"";position:absolute;left:0;top:.8em;width:11px;
  height:6px;border-left:2px solid var(--sage);border-bottom:2px solid var(--sage);
  transform:rotate(-45deg)}

/* ---------- puppy page carousel ---------- */
.puppy-top{display:grid;grid-template-columns:1.45fr 1fr;
  gap:clamp(1.5rem,3vw,3rem);align-items:stretch}
.puppy-info{display:flex;flex-direction:column}
.name-row{display:flex;align-items:baseline;justify-content:space-between;
  gap:1rem;flex-wrap:wrap}
.name-row h1{margin:0}
.name-row .price{font-size:clamp(1.6rem,2.4vw,2.1rem);white-space:nowrap}
.puppy-info .facts{margin-bottom:1rem}
.puppy-info .facts li{padding:.42rem 0;font-size:.94rem}
.puppy-info .reserve{margin:1rem 0}
.puppy-info .share-row{margin-top:auto;padding-top:1.25rem}
.carousel{position:relative}
.frame{position:relative;border:1.5px solid var(--forest);border-radius:6px;
  overflow:hidden;background:#fff}
.frame img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
/* stacked slides, crossfaded. the first keeps position:relative so the frame still
   derives its height from a real image rather than collapsing. */
.frame img{position:absolute;inset:0;height:100%;opacity:0;
  transition:opacity .7s ease-in-out}
.frame img:first-of-type{position:relative}
.frame img.is-on{opacity:1;z-index:1}
@media (prefers-reduced-motion: reduce){
  .frame img{transition:none}
}
.carousel{display:flex;flex-direction:column}
.cnav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;
  border-radius:50%;border:1.5px solid var(--forest);background:rgba(253,249,249,.92);
  cursor:pointer;font-size:1.3rem;line-height:1;color:var(--forest);z-index:2}
.cprev{left:12px} .cnext{right:12px}
.cnav:hover{background:var(--forest);color:var(--paper)}
.count{position:absolute;bottom:12px;right:12px;background:rgba(34,61,44,.85);
  color:var(--paper);font-size:.8rem;padding:.25rem .6rem;border-radius:3px;z-index:2}
.cthumbs{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.7rem}
.cthumbs button{padding:0;border:1.5px solid var(--rule);border-radius:3px;
  background:none;cursor:pointer;width:84px;height:84px;overflow:hidden}
.cthumbs button[aria-current="true"]{border-color:var(--forest);border-width:2.5px}
.cthumbs img{width:100%;height:100%;object-fit:cover}

/* ---------- gallery ---------- */
.gal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
  gap:1.25rem}
.gal-grid a{display:block;border:1.5px solid var(--rule);border-radius:4px;
  overflow:hidden}
.gal-grid a:hover{border-color:var(--forest)}
.gal-grid img{aspect-ratio:1/1;object-fit:cover}
/* Placeholder tint under the lazy-loaded photos. 237 of the 277 images on this site are
   loading="lazy" on purpose, and CSS aspect-ratio already reserves their space, so there
   is no layout shift: measured CLS 0. What a visitor sees instead is a correctly sized
   WHITE box that fills in a moment later, which reads as something failing rather than
   something arriving. A tint makes the unloaded state look deliberate. Purely perceptual;
   it changes no bytes and no timing. Scoped to photo boxes, never to a transparent PNG
   like the logo, where a ground would show through. */
.gal-grid img,.packet img{background:var(--paper-raise)}
.filter-row{display:flex;gap:.6rem;flex-wrap:wrap;margin-bottom:.25rem;
  align-items:stretch}
/* the select is styled to match the buttons beside it exactly, so the row reads as
   one control group rather than a dropdown bolted on */
.filter-select{position:relative;display:inline-flex}
.filter-select select{font:inherit;font-size:.94rem;padding:.5rem 2.4rem .5rem 1.1rem;
  border:1.5px solid var(--forest);background:none;border-radius:3px;
  color:var(--forest);min-height:44px;cursor:pointer;appearance:none;
  -webkit-appearance:none}
.filter-select::after{content:"";position:absolute;right:1rem;top:50%;
  width:8px;height:8px;margin-top:-6px;pointer-events:none;
  border-right:2px solid var(--forest);border-bottom:2px solid var(--forest);
  transform:rotate(45deg)}
.filter-select select:focus-visible{outline:3px solid var(--sage);outline-offset:2px}
.filter-select.on select{background:var(--forest);color:var(--paper)}
.filter-select.on::after{border-color:var(--paper)}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap}
.filter-row button{font:inherit;font-size:.94rem;padding:.5rem 1.1rem;cursor:pointer;
  border:1.5px solid var(--forest);background:none;border-radius:3px;
  color:var(--forest);min-height:44px}
.filter-row button.cur{background:var(--forest);color:var(--paper)}

/* ---------- reserve ---------- */
/* Solid, not dashed. A dashed outline is the visual language of a placeholder, which is
   the wrong signal on the one box that asks for $500. */
.reserve{background:var(--paper-raise);border:1px solid var(--sage-light);
  border-radius:6px;padding:1.25rem 1.3rem;margin:1.5rem 0}
.reserve h3{margin-bottom:.35rem}
/* The reserve box sits in the narrow puppy-info column, about 353px of usable width, so
   there is no room to put the button beside the text. Filling the column is the next best
   use of the space: it removed a ragged 127px gap to the right of a 227px button. */
.apply-gate{display:grid}
/* Back inside the box, under the action. A hairline rather than a margin alone, so the
   softer alternative reads as a footnote to the button and not as more of the same. */
.reserve .talk-first{margin:1rem 0 0;padding-top:.9rem;border-top:1px solid var(--rule)}
.apply-gate .btn{width:100%;justify-content:center}
/* The block was a heading and then nothing but small grey text, so it had two extremes
   and no middle. The terms line is the commercial fact and reads at body size. */
.reserve .terms{font-size:1rem;color:var(--forest);margin:0 0 .2rem}
.pay-row{display:flex;gap:.6rem;flex-wrap:wrap;margin:.85rem 0 .5rem}
.guard-msg{display:none;background:var(--pink-pale);border-radius:3px;
  padding:.8rem 1rem;font-size:.92rem;margin-top:.6rem}
.guard-msg.show{display:flex;flex-direction:column;align-items:flex-start;gap:.9rem}
/* the same box carries the reply after a real submission. Forest on pale pink is 7.91:1;
   the error state keeps forest type and only changes the ground, because white on either
   pink is under 2:1 and is forbidden in this palette. */
.guard-msg.is-error{background:var(--rose)}
/* the follow-on action after a successful application */
.guard-msg .btn{display:inline-flex;margin:0}
/* Acknowledgements. Each one is a real sentence a person can read, not a wall with one
   checkbox at the bottom, because the whole point is that they saw the terms. */
/* Acknowledgements. Reverted 2026-08-26 to this, the version that worked.
   Do NOT float the legend to make it full width: float:left leaves an active float that
   the first .ack must flow around, which squeezes that row into a narrow column on the
   right and looks catastrophic. If this box ever needs restyling, change the border and
   the spacing and leave the legend alone. */
.acks{border:1.5px solid var(--forest);border-radius:4px;padding:1.1rem 1.2rem;
  margin:1.5rem 0 1.25rem;background:var(--paper-raise)}
.acks legend{font-weight:700;padding:0 .45rem}
.ack{display:flex;gap:.7rem;align-items:flex-start;padding:.5rem 0;font-size:.95rem}
.ack + .ack{border-top:1px solid var(--rule)}
.ack input{margin-top:.28rem;width:1.15rem;height:1.15rem;flex:none;
  accent-color:var(--forest)}
/* .35rem/.25rem left the button cramped between two paragraphs that both use a 1rem
   rhythm. Match the rhythm instead of inventing a tighter one. */
.apply-gate{margin:1.1rem 0 1.15rem}

/* ---------- share row ---------- */
.share-row{display:flex;gap:.8rem;margin-top:1.75rem;align-items:center;flex-wrap:wrap}
.share-lbl{font-size:.76rem;font-weight:700;letter-spacing:.17em;
  text-transform:uppercase;color:var(--sage-deep)}
.share-row a{color:var(--sage-deep);display:inline-flex;width:44px;height:44px;
  align-items:center;justify-content:center;border:1.5px solid var(--rule);
  border-radius:3px}
.share-row a:hover{color:var(--paper);background:var(--forest);border-color:var(--forest)}

/* ---------- let's chat launcher ---------- */
.chat-fab{position:fixed;right:18px;bottom:18px;z-index:60;display:inline-flex;
  align-items:center;gap:.5rem;background:var(--forest);color:var(--paper);
  border:none;border-radius:999px;padding:.8rem 1.25rem;font:inherit;font-weight:700;
  cursor:pointer;box-shadow:0 6px 18px rgba(34,61,44,.28);min-height:48px}
.chat-fab:hover{background:var(--forest-soft)}
.chat-fab svg{width:18px;height:18px}
.chat-panel{position:fixed;right:18px;bottom:82px;z-index:60;display:none;
  width:min(92vw,22rem);background:#fff;border:1.5px solid var(--forest);
  border-radius:6px;padding:1.35rem;box-shadow:0 12px 32px rgba(34,61,44,.22)}
.chat-panel.open{display:block}
.chat-panel h3{font-size:1.25rem;margin-bottom:.3em}
.chat-panel p{font-size:.92rem;color:var(--forest-soft);margin-bottom:1rem}
.chat-panel .row{display:flex;gap:.8rem;align-items:baseline;padding:.55rem 0;
  border-top:1px solid var(--rule)}
.chat-panel .row .lbl{font-size:.72rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--sage-deep);min-width:4.6rem}
.chat-panel .row a{font-weight:700;overflow-wrap:anywhere}

/* ---------- forms ---------- */
form{display:flex;flex-direction:column;gap:1rem;max-width:36rem}
/* A submit button is a flex item of that column, and a flex item BLOCKIFIES: the .btn
   rule's inline-flex becomes flex, and the default align-items:stretch then runs it the
   full width of the form. At 576px that reads as a banner rather than a button, and no
   width rule exists to find, which is what made it puzzling. align-self is the fix.
   Full width is right on a phone, where a stacked CTA should fill its container, so the
   stretch comes back below the breakpoint. */
form button[type=submit]{align-self:flex-start}
@media (max-width:900px){form button[type=submit]{align-self:stretch}}
/* the form is a flex container, so its gap does not reach a sibling paragraph after it */
form + .fine,form + p{margin-top:1.35rem}
/* .prose is 70ch and the form is 36rem, so anything after the form ran wider than every
   field above it. Match the form, not the prose column. */
form ~ .fine,form ~ p{max-width:36rem}
/* a bordered card keeps the form visually anchored beside a facts column instead
   of floating as loose fields */
.formcard{background:var(--card,#fff);border:1.5px solid var(--rule);border-radius:5px;
  padding:clamp(1.25rem,2vw,1.75rem)}
.formcard form{max-width:none;gap:.9rem}
.formcard .field{display:flex;flex-direction:column;gap:.3rem}
.formcard .facts{margin-bottom:.75rem}
/* a column that stacks a card above a photo needs its own rhythm */
.grid-2>div>.formcard+.framed,.grid-2>div>.formcard+picture,
.contact-grid>div>.formcard+.framed,.contact-grid>div>.formcard+picture{margin-top:1.25rem}

/* contact: two explicit rows so the right column starts level with the lede rather
   than with the page title above it */
.contact-grid{display:grid;grid-template-columns:1fr 1fr;
  gap:0 clamp(1.5rem,3vw,3rem);align-items:start}
.cg-head{grid-column:1;grid-row:1;margin-bottom:1.25rem}
.cg-gap{grid-column:2;grid-row:1}
.contact-grid>div:nth-of-type(3){grid-column:1;grid-row:2}
.contact-grid>div:nth-of-type(4){grid-column:2;grid-row:2}
@media (max-width:900px){
  .contact-grid{grid-template-columns:1fr;gap:0}
  .cg-gap{display:none}
  .contact-grid>div:nth-of-type(3),.contact-grid>div:nth-of-type(4){
    grid-column:1;grid-row:auto}
  .contact-grid>div:nth-of-type(4){margin-top:1.5rem}
}
.formcard .facts li:last-child{border-bottom:none}
.formcard button{margin-top:.4rem;justify-content:center}
label{font-size:.9rem;font-weight:700}
input,select,textarea{font:inherit;padding:.7rem .8rem;
  border:1.5px solid var(--sage-deep);border-radius:3px;background:#fff;width:100%;
  color:var(--forest);min-height:48px}
textarea{min-height:8rem}

/* ---------- process steps: five across, photo over caption ---------- */
.steps-row{display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(1rem,1.6vw,1.75rem);align-items:start}
.step{display:flex;flex-direction:column;gap:.75rem}
.step-media img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:6px;
  border:1.5px solid var(--forest);box-shadow:0 2px 0 var(--sage-light)}
.step-num{display:inline-flex;align-items:center;justify-content:center;
  width:2.1rem;height:2.1rem;border-radius:50%;background:var(--forest);
  color:var(--paper);font-family:"Lora",Georgia,serif;font-size:1rem;
  font-weight:600;flex:none}
.step-head{display:flex;align-items:center;gap:.6rem}
.step h3{font-size:1.02rem;margin:0;line-height:1.25}
.step p{margin:0 0 .45rem;font-size:.9rem}
.step .fine{font-size:.82rem}
@media (max-width:760px){.steps-row{grid-template-columns:repeat(2,1fr)}}
@media (max-width:460px){.steps-row{grid-template-columns:1fr}}

/* ---------- dog cards: two per row, photo over the detail ---------- */
.dogpair{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1.5rem,3vw,2.75rem);
  align-items:start;margin-bottom:clamp(2rem,3.5vw,3rem)}
.dogpair:last-of-type{margin-bottom:0}
.dogrow{display:flex;flex-direction:column;gap:1rem}
.dogrow .dog-photo{width:100%;border-radius:6px;border:1.5px solid var(--forest);
  box-shadow:0 2px 0 var(--sage-light);aspect-ratio:4/3;object-fit:cover;
  object-position:50% 30%}
@media (max-width:860px){.dogpair{grid-template-columns:1fr}}
.dog-kicker{font-size:.76rem;font-weight:700;letter-spacing:.19em;
  text-transform:uppercase;color:var(--sage-deep);margin:0 0 .35rem}
.dogrow h3{font-size:clamp(1.5rem,2.2vw,2rem);margin-bottom:.15rem}
/* set apart from the body copy without shouting: a rule down the side, the display face,
   and the same sage the rest of the page uses for secondary text */
/* forest, not sage-deep. sage-deep measures 4.34:1 against --paper, which is under the
   4.5 floor for normal text: it is fine for the fine print it was chosen for, and not for
   a paragraph meant to be read. The rule and the display face set this apart, not colour. */
.faith-note{border-left:3px solid var(--sage-light);padding:.15rem 0 .15rem 1.1rem;
  margin-top:1.5rem;font-family:var(--display);font-size:1.02rem;line-height:1.6;
  color:var(--forest)}
.faith-note a{color:var(--forest)}

.reg-name{font-family:"Lora",Georgia,serif;font-style:italic;color:var(--sage-deep);
  margin:0 0 1rem}
.health{border-left:3px solid var(--sage);padding:.2rem 0 .2rem 1.15rem;
  margin:1.25rem 0 0;display:grid;grid-template-columns:1fr auto;
  gap:1.5rem;align-items:start}
.health p{margin:0 0 .45rem;font-size:.95rem}
.health .btn{margin-top:.55rem;padding:.55rem 1.1rem;font-size:.88rem;min-height:44px}
/* stretch rather than flex-start so both record links take the width of the wider one.
   Two links of different widths stacked on top of each other read as an accident. */
.health-btns{display:flex;flex-wrap:wrap;gap:.6rem}
.health-btns .btn{flex:1 1 auto;justify-content:center}
.health-qr{margin:0;text-align:center;flex:none;width:132px}
.health-qr img{width:132px;height:132px;border:1px solid var(--rule);
  border-radius:4px;background:#fff}
.health-qr figcaption{font-size:.68rem;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--sage-deep);margin-top:.4rem;line-height:1.35}
.health.no-qr{grid-template-columns:1fr}

/* ---------- three-column centred list ---------- */
/* A centred wrapping row rather than three fixed columns. It was named for a count and
   then held two, which left a hole on the right. Flex centres whatever is actually there.
   */
.tri{display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(1.25rem,2.5vw,2.5rem);max-width:64rem;margin:0 auto}
.tri>div{background:var(--card,#fff);border:1px solid var(--rule);border-radius:5px;
  padding:1.35rem 1.5rem;flex:1 1 16rem;max-width:22rem}
/* When there is only one card it sits alone in a wide band and reads as an afterthought.
   Given the room, it takes it: wider panel, list in two columns, heading across the top. */
.tri.one>div{flex:1 1 auto;max-width:44rem;padding:1.75rem 2rem;width:100%}
.tri.one h3{text-align:center;margin-bottom:1.1rem}
.tri.one .checklist{columns:2;column-gap:2.5rem}
.tri.one .checklist li{break-inside:avoid}
@media (max-width:620px){.tri.one .checklist{columns:1}}
.tri h3{margin:0 0 .75rem;font-size:1.05rem}
.tri .checklist li{font-size:.93rem}
@media (max-width:900px){.tri{max-width:34rem}.tri>div{flex:1 1 100%;max-width:none}}

/* ---------- closing call to action ---------- */
.closing{background:var(--paper-raise);border-top:1px solid var(--rule);
  border-bottom:1px solid var(--rule)}
/* the file is 1731x1154, a native 3:2. 4/3 here cropped 11% off the sides while
   the mobile rule below already used 3/2. */
.closing-photo{aspect-ratio:3/2;object-fit:cover}
@media (max-width:900px){.closing-photo{aspect-ratio:3/2;margin-top:1.5rem}}
.closing .inner{max-width:44rem;margin:0 auto;text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:.85rem}
/* Two columns from 901px: the copy left, a puppy right. Left-aligned, because a centred
   column with left-aligned rows of tiles under it reads as neither one thing nor the other.
   The copy column has a floor for the same reason narrow-left does: below it the photo
   yields rather than the text being squeezed. */
@media (min-width:901px){
  .closing .inner{display:grid;grid-template-columns:minmax(26rem,1fr) minmax(0,1.25fr);
    gap:clamp(1.75rem,3vw,2.5rem);align-items:center;max-width:none;text-align:left}
  .closing-copy{max-width:34rem}
  .closing-photo{margin:0}
}
.closing h2{margin:0 0 .55rem}
/* no 44ch cap: the copy box is capped instead, so the lede fills the same width as the
   tiles and buttons below it rather than stopping 245px short of them. */
.closing .contact-line{font-size:1.02rem;color:var(--forest-soft);margin:0}
/* label and value centred inside each tile. .ct-label already carries a -.14em right
   margin to cancel the trailing letter-spacing gap, which is what keeps the centring
   optically true rather than a fraction of an em left of centre. */
.closing .contact-tile{text-align:center;align-items:center}
/* Not `anywhere` in here: the address's min-content width has to stay the whole address
   so the flex row refuses to squeeze a tile below it. Global rule is unchanged. */
.closing .ct-value{overflow-wrap:normal}
/* A wrapping centred row, not a grid. It held two tiles when it was written and now
   holds three, and `repeat(2,auto)` stranded the third on its own row against the left
   edge of an otherwise centred block. Flex-wrap centres whatever ends up on each line,
   however many tiles there are. */
.contact-pair{display:flex;flex-wrap:wrap;justify-content:center;gap:.85rem;
  margin:.35rem 0 .25rem}
.contact-pair>.contact-tile{flex:0 1 auto;min-width:0}
.contact-tile{display:flex;flex-direction:column;gap:.2rem;text-decoration:none;
  background:var(--paper);border:1.5px solid var(--rule);border-radius:5px;
  padding:.9rem 1.1rem;text-align:left;min-height:44px}
.contact-tile:hover{border-color:var(--forest)}
/* letter-spacing adds a trailing gap after the final character. the negative
   margin removes it so the label sits flush with the value below it. */
.ct-label{font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--sage-deep);font-weight:700;margin-right:-.14em}
.ct-value{font-family:"Lora",Georgia,serif;font-size:1.02rem;font-weight:600;
  overflow-wrap:anywhere}
@media (max-width:620px){
  .contact-pair{flex-direction:column;align-items:stretch}
}
/* info@blessyourpawspuppies.com is 29 characters and was breaking after the dot in a
   250px tile. A little smaller in this band only; the tiles still match the buttons. */
.closing .ct-value{font-size:.88rem}
/* Between 901 and 1100 the copy column is 443-541px, so two side-by-side tiles leave the
   email about 215px and it breaks after the dot. Shrinking the type further would make it
   unreadable, so the tiles stack in that band and each gets the full column width. Both
   rows still fill the column, so they still line up with the buttons. */
@media (min-width:901px) and (max-width:1100px){
  .closing .contact-pair{flex-direction:column;align-items:stretch}
}
.closing .btn-row{margin:.9rem 0 0;justify-content:center}
/* Both rows fill the copy column and their children share it equally, which is what makes
   the two buttons the same width as the contact tiles directly above them. flex-basis 0
   rather than auto: with auto, the email tile's longer text would take more room than the
   phone tile and the two rows would stop lining up. */
@media (min-width:901px){
  /* The two buttons stay stacked, one per row, filling the copy box. */
  .closing .btn-row{flex-direction:column;align-items:stretch;justify-content:flex-start}
  .closing .btn-row>.btn{flex:0 0 auto;min-width:0;justify-content:center}
  /* The two info tiles share one line and grow to fill the copy box between them.
     min-width:auto overrides the base rule's min-width:0, which together with the
     ct-value rule below means a tile can never be squeezed narrower than the address it
     contains: the pair wraps to two rows rather than breaking a word. */
  .closing .contact-pair{flex-direction:row;flex-wrap:wrap;justify-content:flex-start;
    align-items:stretch}
  .closing .contact-pair>.contact-tile{flex:1 1 auto;min-width:auto}
}
/* Below the two-column breakpoint the equal-split rule does not apply, which left the two
   buttons at 243px and 197px side by side: the row was the right width and the buttons
   were not. Stack them full width instead, which is this project's standing CTA rule and
   is what happens at 390 already. */
@media (max-width:900px){
  .closing .btn-row{flex-direction:column;align-items:stretch}
  .closing .btn-row>.btn{justify-content:center}
}

/* ---------- FAQ accordion ---------- */
.faq{max-width:60rem;margin:0 auto;border-top:1px solid var(--rule)}
.faq details{border-bottom:1px solid var(--rule)}
.faq summary{list-style:none;cursor:pointer;padding:1.1rem 3rem 1.1rem 0;
  position:relative;font-family:"Lora",Georgia,serif;font-size:1.08rem;
  font-weight:600;min-height:48px;display:flex;align-items:center}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"";position:absolute;right:.9rem;top:1.5rem;width:11px;
  height:11px;border-right:2px solid var(--sage-deep);
  border-bottom:2px solid var(--sage-deep);transform:rotate(45deg);
  transition:transform .18s ease}
.faq details[open] summary::after{transform:rotate(-135deg)}
.faq summary:hover{color:var(--forest-soft)}
.faq .ans{padding:0 3rem 1.2rem 0;color:var(--forest-soft)}
.faq .ans p{margin:0 0 .7rem}
.faq .ans p:last-child{margin:0}
@media (prefers-reduced-motion: reduce){.faq summary::after{transition:none}}

.draft-banner{background:var(--draft-bg);border:1.5px dashed var(--draft);
  color:var(--draft);border-radius:4px;padding:1.1rem 1.35rem;font-weight:700;
  margin:0 0 1.75rem}

/* ---------- footer ---------- */
/* no margin: a coloured final section must butt straight against the footer,
   otherwise a strip of page background shows through between them */
.site-foot{background:var(--forest);color:#e9ded9}
.site-foot a{color:var(--pink-pale)}
.foot-top{display:grid;grid-template-columns:auto 1fr;gap:2.5rem;align-items:start;
  padding:3rem 0 1rem}
/* a cream panel around a colour logo reads as a sticker on the footer. instead:
   the pink paw mark, which already sits well on green, with the name typeset. */
.foot-brand{display:flex;align-items:center;gap:.9rem}
.foot-brand img{height:56px;width:auto}
.foot-brand .fb-name{font-family:"Lora",Georgia,serif;font-size:1.35rem;
  font-weight:600;color:var(--paper);line-height:1.15;display:block}
.foot-brand .fb-sub{font-size:.68rem;letter-spacing:.24em;text-transform:uppercase;
  color:var(--rose);display:block;margin-top:.15rem}
.foot-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:2rem}
.foot-grid h3{color:var(--paper);font-size:1rem}
.foot-grid ul{list-style:none;margin:0;padding:0}
.foot-grid li{margin-bottom:.5rem;font-size:.93rem}
.foot-legal{border-top:1px solid rgba(253,249,249,.25);padding:1.35rem 0;
  font-size:.85rem;display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap}

@media print{
  .site-head,.site-foot,.btn,.nav,.cthumbs,.filter-row,.chat-fab,.chat-panel,
  .cnav,.share-row{display:none}
  body{background:#fff;color:#000}
}

/* ---------- mobile ---------- */
@media (max-width:1180px){
  .nav a{font-size:.86rem;padding:.5rem .42rem}
  .brand img{height:48px}
}
@media (max-width:900px){
  .dogpair{grid-template-columns:1fr}
  /* stack the hero: the drift only works when there is width to fade across.
     The photo was sitting flush against the sticky header with a zero gap, which read
     as the puppy bumping the top edge of the page. */
  .hero-drift>*{grid-area:auto}
  /* Option B, but only the caption goes on the photograph.
     The mock this was chosen from had a one-line lede. Production has six lines plus a
     chip, so overlaying the whole copy block put text over 72% of the picture starting
     28% down: structurally correct, and a wall of green over the puppy. The eyebrow,
     headline and buttons stay on the image as a compact block at its foot; the lede
     moves below the photograph onto paper, where it is easier to read anyway.

     Mechanically: .wrap and .hero-copy become display:contents so their children are
     grid items of .hero-drift directly, which is what lets one of them sit outside the
     photo's rows. Row 1 is the breathing space above the caption, rows 2 to 4 are the
     caption over the foot of the photo, row 5 is below it. */
  .hero-drift{position:relative;padding-top:0;display:grid;
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:1fr auto auto auto auto}
  .hero-drift>.wrap,.hero-copy{display:contents}
  .hero-photo{width:auto;justify-self:auto;margin:0;position:relative;
    aspect-ratio:auto;min-height:min(158vw,39rem);
    grid-column:1;grid-row:1 / 5;align-self:stretch}
  .hero-photo img{position:absolute;inset:0;width:100%;height:100%;
    object-fit:cover;object-position:50% 50%;border:0;border-radius:0;background:none}
  .hero-photo::after{content:"";position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(to top,rgba(34,61,44,.92) 0%,rgba(34,61,44,.72) 30%,
      rgba(34,61,44,.22) 58%,transparent 78%)}
  /* display:contents drops the wrap, so each item carries the gutter itself */
  .hero-copy>*{grid-column:1;padding-inline:1.25rem}
  /* "Family-raised in Warsaw and Winona Lake" is 39 characters, and at the site's
     .16em tracking it wrapped to two lines, spending caption height on nothing. Tighter
     tracking and a hair smaller holds it to one line at 320px and up. */
  .hero-copy>.eyebrow{grid-row:2;z-index:2;color:var(--rose);margin:0 0 .25rem;
    font-size:.68rem;letter-spacing:.1em}
  .hero-copy>h1{grid-row:3;z-index:2;color:#fff;
    text-shadow:0 1px 14px rgba(0,0,0,.45);
    font-size:clamp(1.5rem,7vw,1.9rem);margin:0 0 .1rem}
  .hero-copy>.btn-row{grid-row:4;z-index:2;margin:.85rem 0 0;
    padding-bottom:1.5rem}
  /* Inverted for the scrim. The site's buttons are forest on paper; over a forest scrim
     that is 1.05:1 for the filled one's background and 1.05:1 for the ghost one's text
     and border, so the ghost button was simply not there. Paper on forest is 11.85:1. */
  .hero-copy .btn-primary{background:var(--paper);color:var(--forest);
    border-color:var(--paper)}
  .hero-copy .btn-ghost{background:transparent;color:var(--paper);
    border-color:rgba(253,249,249,.8)}
  /* on paper now, so it goes back to body colour and needs its own breathing room */
  .hero-copy>.lede{grid-row:5;color:var(--sage-deep);font-size:1rem;
    margin:1.5rem 0 0}
  .hero-photo img{-webkit-mask-image:none;mask-image:none}
  .dogrow{grid-template-columns:1fr;gap:1.25rem}
  .health{grid-template-columns:1fr}
  .health-qr{width:110px}
  .health-qr img{width:110px;height:110px}
  .hero-split,.grid-2,.puppy-top,.foot-grid,.foot-top{grid-template-columns:1fr}
  .foot-top{gap:1.5rem;padding-bottom:0}
  .foot-brand img{height:48px}
  .nav-toggle{display:block}
  .nav{position:fixed;inset:0;background:var(--paper);flex-direction:column;
    justify-content:center;gap:.3rem;display:none;z-index:50;overflow-y:auto}
  .nav.open{display:flex}
  .nav a{font-size:1.25rem;padding:.7rem 1.2rem}
  .nav a.nav-cta{margin-left:0}
  .nav-close{position:absolute;top:1rem;right:1.25rem}
  /* stretch, not flex-start: a lone .btn child here is a flex item, and flex-start
     sizes it to its label. It was the one container on the site not following the
     full-width-on-mobile CTA rule. */
  .section-head{flex-direction:column;align-items:stretch;gap:1rem}
  .section-head .btn{justify-content:center}
  .cthumbs button{width:64px;height:64px}
  .facts li{min-height:44px}

  /* ---- the compound-selector trap. `.grid-2` above is specificity (0,1,0) while
     `.grid-2.narrow-left` is (0,2,0), so the two narrow variants kept their desktop
     columns on a phone and squeezed the copy into a third of the screen on eight
     pages. Any new .grid-2 modifier has to be named here too. */
  .grid-2.narrow-left,.grid-2.narrow-right,
  .grid-2.lean-left,.grid-2.lean-right{grid-template-columns:1fr}
  /* a photo that earns its place beside a fact list on desktop, but which the grid
     directly below repeats once everything is stacked */
  .hide-mobile{display:none}
  /* .grid-2.hic sets row-gap:0 to keep a heading next to its own paragraph on desktop.
     On mobile the photo is a row between the heading and the copy, so that same rule
     left the copy flush against the bottom of the picture. Margins here rather than a
     row-gap, which would re-open the desktop gap. */
  .hic-photo{margin:.375rem 0 1.25rem}
  .parent-grid.row-4{grid-template-columns:1fr}

  /* ---- call to action rows. flex-grow on a wrapping row does both halves of the
     rule: buttons that fit sit side by side, a button that wraps fills its line. */
  .btn-row,.pay-row{gap:.6rem}
  .btn-row>.btn,.pay-row>.btn{flex:1 1 auto;justify-content:center}
  /* section-cta turns to a column, so growth would go vertical. stretch instead. */
  .section-cta{flex-direction:column;align-items:stretch;text-align:center;gap:.85rem}
  .section-cta>.btn{justify-content:center}

  /* ---- share row: five icons plus a label wrapped to two lines and left the last
     icon orphaned. Label on its own row, icons in five equal columns, fits at 390. */
  .share-row{display:grid;grid-template-columns:repeat(5,1fr);gap:.5rem;
    align-items:center}
  .share-lbl{grid-column:1 / -1;margin-bottom:.1rem}
  .share-row a{width:auto;min-width:0}

  /* ---- the chat launcher measured 147px, 38% of the screen, and sat on top of
     real content including a form field on the waitlist page. Icon only here. */
  .chat-fab{right:12px;bottom:12px;padding:0;width:54px;height:54px;
    border-radius:50%;justify-content:center;gap:0}
  .chat-fab .fab-label{display:none}
  /* z-index 60 put it above the z-index 50 nav overlay, so it floated over the
     open menu */
  body.nav-open .chat-fab{display:none}
  /* and it must not come to rest on the last line of a page */
  main{padding-bottom:4.5rem}

  /* ---- a trailing link reads as a stranded fragment when it wraps onto the tail of
     a sentence, so on a phone it takes its own line */
  .own-line{display:block;margin-top:.3rem}

  /* ---- a single word alone on the last line. Narrow columns produce these constantly,
     and `pretty` exists for exactly this: it pulls a word back from the last line by
     re-breaking the ones above it. `balance` evens out short headings. Browsers without
     support simply ignore both and break as they do now. */
  p,li,figcaption,blockquote,.v,.k{text-wrap:pretty}
  h1,h2,h3,.eyebrow,.dog-kicker{text-wrap:balance}

  /* ---- these two labels are long enough to wrap to two lines in a ghost button,
     which looks broken next to a single-line heading */
  .breed-link{white-space:nowrap;font-size:.92rem;padding-left:1rem;padding-right:1rem}

  /* ---- a slimmer bar gives a phone back some screen */
  .head-row{min-height:68px}
  .brand img{height:42px}
}
/* No stacking breakpoint here on purpose. Stacking the key above its value doubles the
   height of every fact list, which on a puppy page is six rows of pure scrolling. The
   pair stays side by side at every width; `min-width:0` plus `overflow-wrap` on the
   value is what stops a long one forcing the row wider than the screen. */
@media (max-width:900px){
  .hero{overflow:visible}
}
