/* ============================================================
   Meow Homez — site design system  ·  “PINE & CLAY”
   Boutique pet-care: warm ivory canvas, solid elevated cards,
   deep pine primary, clay accent, Fraunces + Plus Jakarta Sans.
   All design tokens live in theme.css — edit that ONE file to
   rebrand. This file only consumes the tokens.
   Every selector from the previous stylesheet is preserved —
   only the visual treatment changed.
   ============================================================ */
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{font-family:var(--font-body),system-ui,sans-serif;color:var(--ink);line-height:1.65;font-size:var(--fs-base);
  background-color:var(--bg);
  /* dual/tri-tone canvas (tokens in theme.css): clay warmth top-left, a whisper
     of pine top-right, plus a barely-there two-tone paw tile so the page never
     reads flat. Scrolls with content — no background-attachment:fixed (iOS). */
  background-image:var(--bg-wash-clay),var(--bg-wash-pine),var(--bg-pattern-paws);
  background-repeat:no-repeat,no-repeat,repeat;
  background-size:auto,auto,var(--bg-paws-size) var(--bg-paws-size)}
img{display:block;max-width:100%}
a{text-decoration:none;color:inherit}
::selection{background:var(--clay-200);color:var(--pine-900)}

/* visible keyboard focus everywhere (WCAG 2.4.7) */
/* Design review 2026-08-06: this rule used to end in `border-radius:6px`. It ties on specificity
   with the component rules in components.css and wins on source order, so every pill CTA and
   every text field visibly CHANGED SHAPE the instant a keyboard user reached it — the one moment
   the UI should look most stable. The radius is gone; the outline now follows whatever radius the
   component already has. */
:focus-visible{outline:var(--focus);outline-offset:2px}
.nav-links a:focus-visible{outline:var(--focus);outline-offset:3px}   /* .btn half moved to /components.css (Wave 6) */

/* Design review 2026-08-06 (WCAG 1.4.1 Use of Colour): `a{text-decoration:none;color:inherit}`
   above strips every link affordance, and the only rule that restored one was `.legal a` — which
   the guides hub never enters. Result: "read our companion guide on boarding anxious cats" was
   indistinguishable from the sentence around it on every article, so the internal-linking strategy
   was invisible to readers AND to anyone who cannot perceive the (absent) colour change.
   Scoped to prose only, so cards, nav, buttons and breadcrumbs are untouched. */
:where(body.mkt) main p a:not(.btn):not([class]),
:where(body.mkt) main li a:not(.btn):not([class]),
:where(body.mkt) main dd a:not(.btn):not([class]){
  color:var(--brand-strong);text-decoration:underline;text-decoration-thickness:.075em;
  text-underline-offset:.18em;text-decoration-color:var(--clay-300);border-radius:3px}
:where(body.mkt) main p a:not(.btn):not([class]):hover,
:where(body.mkt) main li a:not(.btn):not([class]):hover,
:where(body.mkt) main dd a:not(.btn):not([class]):hover{
  color:var(--clay-700);text-decoration-color:currentColor}

h1,h2,h3{font-family:var(--font-head),serif;font-weight:600;line-height:1.12;letter-spacing:-.4px;color:var(--ink)}
.container{max-width:var(--max);margin:0 auto;padding:0 24px}
/* .eyebrow/.eyebrow2 moved to /components.css (Wave 7). The page-scoped .page-hero tints that used
   to live below were retired 2026-08-07 when the interior hero wash was restored — see line ~57. */
.muted{color:var(--muted)}
/* two-tone emphasis: serif italic + clay for emotional keywords */
.em{font-family:var(--font-head),serif;font-style:italic;font-weight:500;color:var(--accent)}
.hero .em,.band .em,.on-dark .em{color:var(--accent-bright)}
/* 2026-08-07 — `.page-hero .em{color:var(--accent)}` was removed here, and `.page-hero .eyebrow`
   with it (see the page-hero block). Both were (0,2,0) overrides that beat `.wash .em` /
   `.wash .eyebrow` — harmless only for as long as `.page-hero{background:transparent}` kept the
   hero on body ivory, where --accent (#B4552F) measures 4.59:1. Restoring the wash puts these two
   on the clay-2 stop, where the same colour is 4.22:1 — under AA for 12.5px uppercase text.
   The `.wash` scope rules already say --clay-700 (5.36:1 on clay-2); they now apply, as intended. */
section{position:relative}

/* hard hairline dividers retired — sections now blend via the
   .wash-* gradient handoff system (see “section washes” below) */
.section{position:relative}

/* ——— buttons ————————————————————————————————————————— */
/* The button family moved to /components.css (Wave 6, owner OK 2026-07-28) as the scope-gated
   `:where(body.mkt) .btn*` section — every marketing page links components.css BEFORE this file
   and carries <body class="mkt">. Page-scoped button overrides (.nav-links .btn, .plan .btn,
   .mob-cta .btn, the A7 pressed overlay, reduced-motion) stay HERE: they are page layout, and
   loading after components.css keeps their cascade position exactly as before the move. */

/* ——— nav — calm warm-white pill, hairline, quiet ————— */
.nav-wrap{position:sticky;top:0;z-index:60;padding:14px 0}
.nav{position:relative;display:flex;align-items:center;justify-content:space-between;gap:16px;
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  background:rgba(255,255,255,.86);border:1px solid var(--line);
  border-radius:var(--r-full);padding:8px 10px 8px 20px;
  box-shadow:0 1px 2px rgba(15,29,23,.05),0 10px 30px rgba(15,29,23,.08)}
.nav.on-dark{background:rgba(21,39,32,.55);border-color:rgba(255,255,255,.18)}
/* Design refresh 2026-08-06 — the aurora sheen is DELETED. A 3s infinite background-position
   animation ran forever on the stickiest, backdrop-blurred element on every page: it repainted a
   blurred layer every frame for the entire visit (the most expensive thing on the page on a cheap
   Android), and WCAG SC 2.2.2 asks that anything moving for more than five seconds be stoppable —
   a reduced-motion guard covers the users who set the flag, not the ones who never heard of it.
   The pill keeps its glass, hairline and shadow; it just stops shimmering at people.
   The static ::after is retained (no animation) so the z-index layering below is unchanged. */
.nav{overflow:hidden}
.nav>*{position:relative;z-index:1}   /* keep brand, links & CTA crisp above the glass */
.brand{font-family:var(--font-head),serif;font-weight:600;font-size:var(--fs-lg);color:var(--brand-strong);display:flex;align-items:center;gap:8px}
.on-dark .brand{color:#fff}
.nav-links{display:flex;align-items:center;gap:2px;font-size:14.5px;font-weight:600;color:var(--ink-2)}
.on-dark .nav-links{color:#fff}
.nav-links a:not(.btn){position:relative;padding:11px 14px;border-radius:var(--r-full);z-index:1;
  transition:color var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease)}
/* Neumorphic nav pills (owner 2026-08-09). A hovered link lifts out of the bar and the CURRENT
   page sits pressed INTO it — the one place soft UI genuinely earns its keep, because "raised vs
   recessed" states where you are without a colour having to carry the message on its own
   (WCAG 1.4.1). The link ink is untouched, so contrast stays at 13.39:1. */
.nav-links a:not(.btn){transition:color var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.nav-links a:not(.btn):hover{color:var(--brand-strong);background:var(--pine-50);box-shadow:var(--nu-raise-sm)}
.nav-links a:not(.btn):active{box-shadow:var(--nu-press)}
/* Owner 2026-08-07: "too much whitespace in the navbar before and after items". Measured rather
   than nudged — .nav is `justify-content:space-between` with two children (.brand and .nav-links),
   so ALL the free space collapses into ONE gap. Inside the 1120px pill: 1042px usable, 910px of
   content, 132px of slack — every pixel of it between the logo and "Home", and none anywhere else.
   That is the unevenness; it is not the link padding, and it gets worse the wider the screen.
   Two auto margins fix it without touching markup or site-enhance.js. Flexbox shares free space
   EQUALLY between multiple auto margins, so one before the first link and one before the CTA splits
   that 132px into 66px either side: brand left, links optically centred, Book flush right — the
   layout the bar already looked like it was trying to be.
   Scoped above 860px on purpose: at or below it site-enhance.js lifts the Book CTA OUT of
   .nav-links (nav.insertBefore) so it cannot hide behind ☰, and a margin rule written for a child
   that has moved to a different parent is how a one-row header silently becomes two. */
@media(min-width:861px){
  .nav-links{flex:1}
  .nav-links > a:not(.btn):first-child{margin-left:auto}
  .nav-links > .nav-cta{margin-left:auto}
}
.on-dark .nav-links a:not(.btn):hover{color:#fff;background:rgba(255,255,255,.14)}
.nav-toggle{display:none}   /* compact mobile “Book” pill retired — marketing pages show the FULL menu at every width (the booking app keeps its own compact header) */
@media(max-width:860px){
  /* Design refresh 2026-08-06 — THE HEADER USED TO LEAVE. `position:static` was chosen so a
     three-row stacked menu would not eat a small viewport — correct for the menu that existed then,
     but site-enhance.js has since folded the links behind ☰, so the header is ONE row and there is
     nothing to eat. What the static rule actually did was abandon the visitor: past ~125px of a
     13,000px homepage there was no logo, no menu and no way to book for the remaining sixteen
     screenfuls. A 52px sticky row costs 6% of the viewport and gives all three back. It hides on
     scroll-down and returns on scroll-up (site-enhance.js toggles .nav-hide), so reading is never
     obstructed — the bar is there the instant a thumb moves up.
     The height is a budget, not a leftover, and every term in it was chosen: the ☰ button is 44px
     because that is the tap-target floor, + 4 + 4 of .nav padding + its 1px hairline top and bottom
     = 54, + 4 + 4 of .nav-wrap padding = 62px, 7.3% of a 390×844 screen. Padding added here comes
     straight out of the photograph, so design-refresh-phase2.test.js pins the 62. */
  /* Panel audit 2026-08-07 — STICKY WAS NEVER STICKING PAST THE HERO, and two reviewers found it
     independently. `.nav-wrap` is not a page-level element: on every marketing page it sits inside
     the hero's own .container, and a position:sticky box is confined to its PARENT's box. So the
     header stuck for the height of that container and then scrolled away with it — on /pricing that
     container is 350px tall, so the "header that comes back" left the screen before the reader had
     finished the first swipe. Phase 3 declared this fix shipped, DEPLOY_STATUS certified it, and
     three assertions guarded it — every one of them a substring grep for `position:sticky`, which
     was present and true and proved nothing about whether the element stays on screen.
     Fixed at ≤860px by taking it out of flow entirely, which no ancestor can confine, and giving
     the two hero types back the space it used to occupy so nothing else moves. */
  .nav-wrap{position:fixed;top:0;left:0;right:0;padding:4px 12px;transition:transform var(--dur) var(--ease)}
  .nav-wrap.nav-hide{transform:translateY(calc(-100% - 8px))}
  .nav{flex-wrap:wrap;justify-content:space-between;border-radius:var(--r-lg);padding:4px 8px 4px 14px;row-gap:2px;min-height:52px}
  .brand{flex:0 1 auto;justify-content:flex-start;font-size:18px}
  .nav-links{width:100%;flex-wrap:wrap;justify-content:center;gap:2px;font-size:13.5px}
  .nav-links a:not(.btn){padding:10px 11px;min-height:44px;display:inline-flex;align-items:center}
  .nav-links .btn{margin:8px 2px 2px;min-height:44px}
  html{scroll-padding-top:64px}   /* an in-page anchor must not land under the bar */
}
@media(prefers-reduced-motion:reduce){.nav-wrap{transition:none}}

/* ——— hero (dark-photo variant, kept for reuse) ———————— */
.hero{position:relative;overflow:hidden;border-bottom-left-radius:34px;border-bottom-right-radius:34px}
.hero-bg{position:absolute;inset:0;background-size:cover;background-position:center}
.hero-veil{position:absolute;inset:0;background:linear-gradient(135deg,rgba(21,39,32,.88),rgba(30,56,48,.62))}
.hero-inner{position:relative;padding:18px 0 76px;color:#fff}
.hero-grid{display:grid;grid-template-columns:1.3fr .8fr;gap:30px;align-items:center;margin-top:46px}
.hero h1{font-size:54px;color:#fff;text-shadow:0 2px 30px rgba(0,0,0,.25)}
.hero p.lead{font-size:19px;opacity:.95;max-width:480px;margin-top:16px}
.hero-cta{margin-top:26px;display:flex;gap:12px;flex-wrap:wrap}
.trust{display:flex;gap:26px;margin-top:34px;flex-wrap:wrap}
.trust .n{font-family:var(--font-head),serif;font-weight:600;font-size:22px}
.trust .l{font-size:var(--fs-xs);opacity:.85}
@media(max-width:860px){.hero-grid{grid-template-columns:1fr}.hero h1{font-size:40px}}

/* ——— hero — Framed Luxe (homepage) ————————————————— */
.hero-luxe{position:relative}
.hero-luxe .hl-in{padding:16px 0 68px}
.hero-luxe .hl-grid{display:grid;grid-template-columns:1fr 1.05fr;gap:48px;align-items:center;margin-top:38px}
.hero-luxe .eyebrow{color:var(--accent)}
.hero-luxe .hl-rule{width:56px;height:3px;background:linear-gradient(90deg,var(--clay-400),var(--clay-600));border-radius:3px;margin:18px 0}
.hero-luxe h1{font-size:var(--fs-hero);color:var(--ink);font-weight:600}
.hero-luxe h1 .em{color:var(--accent)}
.hero-luxe .lead{color:var(--muted);font-size:18px;max-width:46ch;margin-top:18px}
.hero-luxe .hl-cta{display:flex;gap:12px;margin-top:28px;flex-wrap:wrap}
.hero-luxe .trust{gap:32px;margin-top:32px}
.hero-luxe .trust .n{color:var(--brand-strong);font-size:24px}
.hero-luxe .trust .l{color:var(--muted);opacity:1}
.hero-luxe .hl-frame{position:relative}
.hero-luxe .hl-photo{height:460px;border-radius:var(--r-lg);background-size:cover;background-position:center;
  box-shadow:var(--shadow-lg);border:6px solid #fff;
  transition:transform var(--dur-slow) var(--ease),box-shadow var(--dur-slow) var(--ease)}
.hero-luxe .hl-frame:hover .hl-photo{transform:translateY(-4px);box-shadow:0 6px 14px rgba(15,29,23,.08),0 36px 76px rgba(15,29,23,.2)}
/* Design refresh 2026-08-06 — THE FIRST PRICE A VISITOR SEES. This badge led with
   "$17/day with Platinum · $25 standard": every word true, but Platinum is undefined jargon at
   second one and resolves 3,000px later to a prepaid pass. Leading with the rate almost everyone
   actually pays, and letting the pass be the discovered upgrade, is the honest order — and the
   badge is now a LINK to /member-pass, which the orange sale-sticker version never was.
   Treatment: an ivory plate on the photograph, not a gradient sticker. Numerals are tabular so any
   admin value renders without reflowing the plate. */
.hero-luxe .hl-badge{position:absolute;top:20px;right:20px;display:block;text-decoration:none;
  background:rgba(253,251,247,.94);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  color:var(--ink);border:1px solid var(--clay-200);
  border-radius:var(--r);padding:13px 17px;text-align:right;max-width:250px;
  box-shadow:var(--shadow-md);font-variant-numeric:tabular-nums;
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.hero-luxe .hl-badge:hover{transform:translateY(-2px);box-shadow:var(--shadow-lg)}
.hero-luxe .hl-badge .big{font-family:var(--font-head),serif;font-size:30px;font-weight:600;line-height:1;letter-spacing:-.02em}
.hero-luxe .hl-badge .big .per{font-size:13px;font-weight:500;color:var(--muted);letter-spacing:0}
.hero-luxe .hl-badge .s{font-size:12px;color:var(--muted);margin-top:5px}
.hero-luxe .hl-badge .up{margin-top:8px;padding-top:8px;border-top:1px solid var(--clay-200);
  font-size:var(--fs-xs);font-weight:700;color:var(--brand-strong)}
/* On a phone the plate lifts off the photograph entirely: at 390px it covered the mural sign and
   one of the two cats — the only proof on the page that this is a real home. */
@media(max-width:560px){
  .hero-luxe .hl-badge{position:static;max-width:none;margin-top:12px;text-align:left;
    display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 14px;backdrop-filter:none}
  .hero-luxe .hl-badge .s{margin-top:0}
  .hero-luxe .hl-badge .up{flex-basis:100%;margin-top:6px;padding-top:6px}
}
/* Design review 2026-08-06: this card renders whatever review the admin picks (data-hero-quote).
   The Omer M. quote is 9 lines; centred, italic, 15.5px serif it out-massed the H1 and dragged the
   whole hero composition down. The CMS rail is untouched — the LAYOUT now absorbs any length:
   left-aligned, Jakarta roman (serif italic is reserved for display sizes now), clamped to 4 lines
   with a decorative Fraunces open-quote as the one surviving piece of romance. */
.hero-luxe .hl-quote{margin-top:16px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:20px 22px 18px;
  box-shadow:var(--shadow-sm);text-align:left;position:relative}
.hero-luxe .hl-quote::before{content:'\201C';position:absolute;left:16px;top:-2px;
  font-family:var(--font-head),serif;font-size:56px;line-height:1;color:var(--clay-200);pointer-events:none}
.hero-luxe .hl-quote p{position:relative;font-family:var(--font-body),sans-serif;font-style:normal;
  font-size:15.5px;color:var(--ink-2);line-height:1.6;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
.hero-luxe .hl-quote .who{font-size:var(--fs-xs);color:var(--accent);font-weight:700;margin-top:10px}
@media(max-width:680px){.hero-luxe .hl-quote p{-webkit-line-clamp:3}}
/* fluid-headings 2026-08-07: `.hero-luxe h1{font-size:38px}` here overrode --fs-hero
   (clamp(34px,8.2vw,52px), line ~174) back to a fixed 38px below 860px — which is every phone and
   most tablets, exactly the widths the clamp exists for. The fluid token was doing nothing anywhere
   it mattered.

   Deleting the override outright is WRONG, and this took three tries to get right:
     · Attempt 1 removed it. 8.2vw reaches the 52px ceiling at 634px, so every viewport from
       634–860px jumped from 38px to the FULL DESKTOP headline inside the single-column layout —
       a change sold as "smaller headings on phones" making tablets bigger instead.
     · Attempt 2 capped it at 42px. Better, but still a 4px INCREASE from 634–860px, i.e. an
       unrequested visual change on the one surface I have no browser here to look at.
     · This one caps at 38px — the value that is live today. Below 463px the clamp scales down to
       a 34px floor (the phone win, which is the entire point of the branch); from 463px to 860px
       it resolves to exactly 38px, byte-for-byte what renders now. Desktop sits outside this
       block and still gets 52px.

   So the diff is: phones get smaller, nothing else moves at all. If the tablet headline should be
   larger that is a separate, deliberate decision — not a side effect of a phone fix. */
@media(max-width:860px){.hero-luxe .hl-grid{grid-template-columns:1fr;gap:34px}
  .hero-luxe h1{font-size:clamp(34px,8.2vw,38px)}
  .hero-luxe .hl-photo{height:340px}
  .hero-luxe .hl-quote{position:static;margin-top:16px;max-width:none;left:0}}

/* ——— savings calculator ———————————————————————————— */
.savecalc{max-width:940px;margin:0 auto}
.savecalc-controls{display:flex;gap:30px;flex-wrap:wrap;justify-content:space-between;align-items:flex-start}
.sc-field{flex:1;min-width:240px}
.sc-field>label{display:flex;justify-content:space-between;align-items:baseline;font-weight:700;margin-bottom:10px}
.sc-field>label b{color:var(--brand-strong);font-family:var(--font-head),serif;font-size:22px}
#scNights{width:100%;accent-color:var(--brand);height:6px;cursor:pointer}
.sc-quick{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.sc-quick button,.sc-cats button{border:1.5px solid var(--line-strong);background:#fff;border-radius:var(--r-full);
  padding:10px 16px;min-height:44px;min-width:44px;font-weight:700;cursor:pointer;font-family:var(--font-body);color:var(--ink-2);
  transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease)}
.sc-quick button:hover,.sc-cats button:hover{border-color:var(--brand);color:var(--brand-strong)}
.sc-cats{display:flex;gap:8px;flex-wrap:wrap}
.sc-quick button.on,.sc-cats button.on{background:var(--brand);color:#fff;border-color:var(--brand);font-weight:800;
  box-shadow:0 6px 14px rgba(47,87,73,.28)}
.savecalc-headline{text-align:center;margin:30px 0 22px}
.savecalc-headline .big{font-family:var(--font-head),serif;font-size:42px;font-weight:600;color:var(--brand-strong);line-height:1.05}
.savecalc-headline .sub{color:var(--muted);margin-top:8px;font-size:15px}
#scResults .sc-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px 16px;text-align:center;position:relative;
  box-shadow:var(--shadow-sm)}
#scResults .sc-card.best{border:2px solid var(--clay-500);box-shadow:0 4px 10px rgba(15,29,23,.05),0 18px 40px rgba(180,85,47,.18)}
#scResults .sc-card h4{font-size:var(--fs-md);margin-bottom:3px;font-family:var(--font-head),serif;font-weight:600}
#scResults .sc-rate{font-size:13px;color:var(--muted)}
#scResults .sc-save{font-family:var(--font-head),serif;font-size:27px;font-weight:600;color:var(--ok);margin:9px 0 2px}
#scResults .sc-cost{font-size:var(--fs-xs);color:var(--muted);line-height:1.4}
#scResults .sc-badge{position:absolute;top:-11px;left:50%;transform:translateX(-50%);background:var(--clay-600);color:#fff;
  font-size:10.5px;font-weight:800;letter-spacing:.4px;padding:4px 12px;border-radius:var(--r-full);white-space:nowrap;
  box-shadow:0 4px 12px rgba(180,85,47,.35)}

/* ——— glass card (dark hero contexts only) ———————————— */
.glass{backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.32);border-radius:var(--r-lg);color:#fff}
.price-card{padding:26px}
.price-card .from{font-size:var(--fs-sm);opacity:.9}
.price-card .big{font-family:var(--font-head),serif;font-weight:600;font-size:46px;line-height:1}
.price-card ul{list-style:none;margin:14px 0 18px;font-size:var(--fs-sm);opacity:.95}
.price-card li{padding:4px 0;display:flex;gap:8px}

/* ——— sections & grid ———————————————————————————————— */
.section{padding:40px 0} /* 78 -> 56 (2026-07-13) -> 40 (2026-08-09): the owner has now asked twice
   for less air. Compounding with the smaller card padding below, a five-section page loses ~160px. */
/* legacy .section.tint band retired — superseded by the .wash-* gradient
   system below (kept as a no-op so any stray .tint markup stays harmless) */
.section.tint{background:none}

/* ============================================================
   SECTION WASHES — dual/tri-tone gradient bands with colour
   HANDOFF: every band ENDS in the exact tone the next band
   STARTS with, so adjacent sections blend with no visible seam
   and the page reads as one continuous colour journey.
   Tones live in theme.css (--wash-*): ivory · pine-1 · pine-2
   (mists) · clay-1 · clay-2 (mists). Naming: .wash-iv-pi2 =
   ivory → pine-2, .wash-pi2-cl2 = pine-2 → ivory → clay-2 (tri).
   180deg with generous mid stops = long smooth ramps, no
   banding, and they scale cleanly at any viewport width.
   Plain CSS gradients only — no assets, no attachment:fixed.
   ============================================================ */
.wash{background-color:var(--wash-ivory)}   /* paint-before-load fallback */
/* hero bands (richest): tri-tone + the shared paw tile + a pine breath */
.wash-hero{background-image:var(--bg-wash-pine),var(--bg-pattern-paws),
    linear-gradient(180deg,var(--wash-clay-2) 0%,var(--wash-clay-1) 30%,var(--wash-ivory) 64%,var(--wash-pine-1) 100%);
  background-repeat:no-repeat,repeat,no-repeat;
  background-size:auto,var(--bg-paws-size) var(--bg-paws-size),auto}
.wash-hero-inner{background-image:var(--bg-wash-pine),
    linear-gradient(180deg,var(--wash-clay-2) 0%,var(--wash-clay-1) 48%,var(--wash-ivory) 100%);
  background-repeat:no-repeat,no-repeat}
/* dual-tone ramps */
/* Design review 2026-08-06 — the invariant, written down so it stops being folklore:
   EVERY <section> in a washed page carries a wash class, and the tone a band ENDS on is the tone
   the next band STARTS on. The "More from Meow Homez" cross-sell shipped as a bare
   <section class="section"> on pricing, member-pass and referrals, so it rendered body ivory
   between two pine-2 endpoints — six hard full-width luminance steps, all of them on money pages.
   .wash-iv is the flat ivory link in the chain: a section that changes nothing still has to SAY
   it changes nothing, or the next section added by hand or by the CMS breaks the run again. */
/* ── SEAM SMOOTHING (owner 2026-08-15: "make background blending smooth") ──────────────────────
   The invariant above was already satisfied — every band ended on the exact tone the next one
   started on — and there was STILL a visible crease at each join. Matching the colour is only half
   of it. Two linear ramps that meet at the same colour but arrive with different SLOPES produce a
   Mach band: the eye does not detect the value step (there isn't one), it detects the sudden change
   in rate, and paints an edge that is not in the pixels. It is most obvious where a band ramping
   DOWN into pine-2 meets one ramping straight back UP to ivory — the CTA → FAQ join, exactly where
   this was reported.
   The fix is to flatten each ramp as it approaches its endpoints: hold the terminal tone for the
   last ~10% and ease out of it for the first ~10%. The derivative is then ≈0 on both sides of every
   join, so there is no rate change to see. Endpoint COLOURS are untouched, so the invariant — and
   every existing handoff — still holds exactly as documented above. */
.wash-iv{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 100%)}
.wash-iv-pi1{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 10%,var(--wash-pine-1) 92%,var(--wash-pine-1) 100%)}
.wash-pi1-iv{background-image:linear-gradient(180deg,var(--wash-pine-1) 0%,var(--wash-pine-1) 10%,var(--wash-ivory) 92%,var(--wash-ivory) 100%)}
.wash-iv-pi2{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 9%,var(--wash-pine-1) 52%,var(--wash-pine-2) 91%,var(--wash-pine-2) 100%)}
.wash-pi2-iv{background-image:linear-gradient(180deg,var(--wash-pine-2) 0%,var(--wash-pine-2) 9%,var(--wash-pine-1) 44%,var(--wash-ivory) 91%,var(--wash-ivory) 100%)}
.wash-iv-cl2{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 9%,var(--wash-clay-1) 52%,var(--wash-clay-2) 91%,var(--wash-clay-2) 100%)}
.wash-cl2-iv{background-image:linear-gradient(180deg,var(--wash-clay-2) 0%,var(--wash-clay-2) 9%,var(--wash-clay-1) 44%,var(--wash-ivory) 91%,var(--wash-ivory) 100%)}
.wash-pi1-pi2{background-image:linear-gradient(180deg,var(--wash-pine-1) 0%,var(--wash-pine-1) 10%,var(--wash-pine-2) 92%,var(--wash-pine-2) 100%)}
/* tri-tone crossovers (green ↔ warm through ivory) */
.wash-pi2-cl2{background-image:linear-gradient(180deg,var(--wash-pine-2) 0%,var(--wash-pine-2) 9%,var(--wash-ivory) 50%,var(--wash-clay-2) 91%,var(--wash-clay-2) 100%)}
.wash-cl2-pi2{background-image:linear-gradient(180deg,var(--wash-clay-2) 0%,var(--wash-clay-2) 9%,var(--wash-ivory) 50%,var(--wash-pine-2) 91%,var(--wash-pine-2) 100%)}
/* soft “bumps” — start AND end on ivory (safe around sections that can
   be hidden, e.g. #seasonalSection: the handoff chain holds either way) */
.wash-iv-pi-iv{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 9%,var(--wash-pine-1) 50%,var(--wash-ivory) 91%,var(--wash-ivory) 100%)}
.wash-iv-cl-iv{background-image:linear-gradient(180deg,var(--wash-ivory) 0%,var(--wash-ivory) 9%,var(--wash-clay-1) 50%,var(--wash-ivory) 91%,var(--wash-ivory) 100%)}
/* texture modifier: faint pine dot grid layered under the content,
   over the gradient (isolate → z-index:-1 stays inside this section) */
.wash-dots{isolation:isolate}
.wash-dots::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:var(--bg-pattern-dots);background-size:var(--bg-dots-size) var(--bg-dots-size)}
/* clean edge into the dark footer (the soft fade read as a muddy smear) */
.to-dark::after{content:none}
/* AA on the mists: clay-600 eyebrows/italics sit at ~4.4:1 on the deepest
   wash tones — deepen to clay-700 (≥5.3:1 everywhere) inside washed bands. */
.wash .eyebrow{color:var(--clay-700)}
.wash .em{color:var(--clay-700)}
.wash .band .em,.wash .ref-card .em{color:var(--accent-bright)}   /* dark cards keep bright accent */
.sec-head{max-width:640px;margin-bottom:18px}
.sec-head h2{font-size:var(--fs-2xl);margin:12px 0}
.grid{display:grid;gap:14px} /* 18 -> 14: the gutter is the other half of "too big horizontally" */
.g3{grid-template-columns:repeat(3,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
.g2{grid-template-columns:1fr 1fr}
@media(max-width:860px){.g3,.g4,.g2{grid-template-columns:1fr}}

/* ——— cards — moved to /components.css (Wave 7) ——————— */

/* ——— photo tiles ————————————————————————————————————— */
.ph{background-size:cover;background-position:center;border-radius:var(--r-lg);box-shadow:var(--shadow-sm)}

/* masonry photo gallery — CSS GRID collage (handles ANY image count from admin).
   WHY GRID: this was CSS multicol (column-count) with will-change/transform on
   the items. Transformed/composited boxes inside a multicol fragmentation
   context hit a long-standing Chromium/WebKit paint bug: the tile sitting at a
   column break simply doesn't paint until :hover starts a transform transition
   and forces re-rasterisation → “image hidden unless hovered”. Grid creates no
   fragmentation context, so every tile paints and the hover lift is safe.     */
/* REVERTED to the pre-2026-08-09 gallery, byte-for-byte from db412e7 (owner: "back to old grid
   style gallery, exact one before we changed layout").

   Restored WHOLESALE rather than unpicked change by change: three separate passes had landed on
   this block in one day — the accordion layout, the greyscale filter, and the seam/frame rework —
   and reversing them individually is how you end up with a fourth arrangement that matches none of
   them. Taking the original out of git is the only way to be certain "exact" means exact.

   So the 18px gutters, the per-tile radius and shadow, the hover LIFT and full-colour images are
   all back exactly as they were. The greyscale and the clipped single-surface treatment are gone
   with the accordion they were built around. */
.masonry{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:104px;grid-auto-flow:dense;gap:18px}
.masonry .mph{position:relative;overflow:hidden;grid-row:span 3;margin:0;border-radius:var(--r-lg);
  box-shadow:var(--shadow-sm);background:var(--sand);
  transition:transform var(--dur-slow) var(--ease),box-shadow var(--dur-slow) var(--ease)}
.masonry .mph:nth-child(4n+1){grid-row:span 4}   /* rhythm: every 4th tile runs taller */
.masonry .mph img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .6s var(--ease)}
.masonry .mph:hover{transform:translateY(-5px);box-shadow:var(--shadow-lg)}
.masonry .mph:hover img{transform:scale(1.04)}
/* admin captions — always visible when present (never hover-gated) */
.masonry .mph-cap{position:absolute;left:0;right:0;bottom:0;z-index:1;padding:34px 14px 11px;
  font-size:var(--fs-xs);font-weight:600;letter-spacing:.2px;color:#fff;text-shadow:0 1px 2px rgba(15,29,23,.55);
  background:linear-gradient(180deg,rgba(15,29,23,0),rgba(15,29,23,.68));pointer-events:none}
@media(prefers-reduced-motion:reduce){.masonry .mph,.masonry .mph img,.hero-luxe .hl-photo,.card,.btn{transition:none}.masonry .mph:hover,.hero-luxe .hl-frame:hover .hl-photo,.card:hover{transform:none}.masonry .mph:hover img{transform:none}}
@media(max-width:860px){.masonry{grid-template-columns:repeat(2,1fr);grid-auto-rows:92px;gap:14px}}
@media(max-width:520px){.masonry{grid-auto-rows:72px;gap:10px}}

/* ——— gallery: the ACCORDION IS GONE (owner 2026-08-09, "a bad idea — revert to the old gallery")
   It lived for one day. The masonry grid above is the whole gallery again at every width, which is
   what it was before and what the .mph markup was always shaped for — so this is a deletion, not a
   second layout hiding behind a media query.

   Two things from that day are KEPT, because they were separate requests and still stand on their
   own with the mosaic back:
     · greyscale at rest, colour on hover (and colour at rest on touch);
     · one clipped container with 2px seams instead of eight white-matted tiles — which was the
       answer to "the spacing and white border makes it less premium", a complaint about the FRAMES
       rather than about the accordion.
   Say the word and either can come off too; they are independent of the layout.

   The per-tile hover LIFT stays retired for a structural reason, not a stylistic one: the container
   now clips (overflow:hidden, so the rounded corners actually cut the photos), and a tile that
   translates upward slides under that edge and shears against it. Colour is the hover response. */

/* ——— referral “coupon” card — deep pine + clay glow —— */
.ref-card{position:relative;overflow:hidden;border-radius:var(--r-lg);padding:26px 28px 24px;color:#fff;
  background:linear-gradient(150deg,var(--pine-800),var(--pine-950));border:1px solid rgba(255,255,255,.08);
  box-shadow:0 26px 60px rgba(15,29,23,.35)}
.ref-card::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(440px 260px at 90% -12%, rgba(232,168,126,.30), transparent 60%),
            radial-gradient(360px 240px at -8% 112%, rgba(232,168,126,.14), transparent 60%)}
.ref-card>*{position:relative;z-index:1}
.ref-top{display:flex;justify-content:space-between;align-items:center;gap:12px}
.ref-top .src{font-size:var(--fs-xs);opacity:.82}
.ref-badge{font-size:11px;font-weight:800;letter-spacing:.3px;display:inline-flex;align-items:center;gap:7px;
  background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.26);padding:5px 12px;border-radius:var(--r-full)}
.ref-badge::before{content:"";width:7px;height:7px;border-radius:50%;background:#5BE5A6;box-shadow:0 0 0 3px rgba(91,229,166,.25)}
.ref-label{font-size:11px;opacity:.72;margin-top:20px;text-transform:uppercase;letter-spacing:1.6px;font-weight:800}
.ref-code{margin-top:8px;display:flex;align-items:center;gap:14px}
.ref-code b{font-family:var(--font-head),serif;font-size:38px;letter-spacing:8px;font-weight:600;line-height:1}
.ref-copy{margin-left:auto;background:rgba(232,168,126,.16);border:1px solid rgba(232,168,126,.5);color:#fff;
  font-family:var(--font-body);font-weight:800;font-size:12px;border-radius:10px;padding:12px 16px;min-height:44px;cursor:pointer;
  transition:background-color var(--dur-fast) var(--ease);white-space:nowrap;
  /* inline-flex + gap so the "Copied" checkmark sits beside the word. */
  display:inline-flex;align-items:center;justify-content:center;gap:6px}
/* `gap` applies between flex ITEMS, not between glyphs — a zero-width empty span would still
   push 6px of gap and make the resting button wider than it was. :empty removes it from layout
   entirely, so "Copy" measures exactly what it measured before and only "Copied" gains the gap. */
.ref-copy .ref-copy-ic:empty{display:none}
.ref-copy:hover{background:rgba(232,168,126,.3)}
.ref-perf{height:1px;margin:20px 0 16px;background:repeating-linear-gradient(90deg,rgba(255,255,255,.5) 0 7px,transparent 7px 15px)}
.ref-rows{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.ref-r{display:flex;align-items:center;gap:11px}
.ref-r .ic{flex:none;width:36px;height:36px;border-radius:11px;background:rgba(232,168,126,.16);border:1px solid rgba(232,168,126,.4);
  display:flex;align-items:center;justify-content:center;font-size:var(--fs-md)}
.ref-r .l{font-size:10.5px;opacity:.7;text-transform:uppercase;letter-spacing:.6px;font-weight:800}
.ref-r b{font-size:14.5px;display:block;margin-top:1px}
.ref-foot{font-size:12px;opacity:.78;margin-top:16px}
@media(max-width:420px){.ref-rows{grid-template-columns:1fr}.ref-code b{font-size:32px;letter-spacing:6px}}

/* ——— steps ——————————————————————————————————————————— */
.step .num{font-family:var(--font-head),serif;font-weight:600;color:var(--clay-500);font-size:32px}
.step h3{margin:8px 0 6px;font-size:18px}
.step p{color:var(--muted);font-size:14.5px}

/* ——— pricing cards ——————————————————————————————————— */
/* MATERIAL CARDS (owner 2026-08-09) — pricing plans AND the Member Pass grid, which web-pricing.js
   also renders as .plan. Padding comes down 28→20px with the same leaner pass the .card family got
   on the same day; the two are peers and would otherwise sit at visibly different weights. */
.plan{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:20px;position:relative;
  display:flex;flex-direction:column;height:100%;box-shadow:var(--md-rest);
  transition:transform .3s var(--md-ease),box-shadow .3s var(--md-ease)}
/* Scale per the reference, but 1.03 rather than 1.05, and the difference was computed rather than
   guessed. On the 3-up grid a card is (1120 − 48 − 2×14)/3 = 348px:
       1.03 → +10.4px wide → 5.2px each side, clears the 7px half-gutter by 1.8px
       1.05 → +17.4px wide → 8.7px each side, OVERLAPS its neighbour by 1.7px
   So 1.05 is the value that makes the row jostle, and 1.03 is the largest step that does not.
   (The 4-up pass grid is roomier and clears at either, but one value across all three families
   beats a per-grid exception.)
   z-index is therefore BELT-AND-BRACES, not load-bearing — it costs nothing and keeps the lift
   correct if the container ever narrows or the gutter tightens again. Worth saying plainly: an
   earlier draft of this comment claimed the z-index was rescuing an overlap at 1.03, which the
   arithmetic does not support. */
.plan:hover{transform:scale(1.03);box-shadow:var(--md-hover);z-index:2}
.plan.feature{border:2px solid var(--brand);box-shadow:var(--md-hover),0 22px 48px rgba(47,87,73,.14)}
/* The featured plan already sits a level higher at rest, so its hover has to clear its own resting
   shadow or the card would appear to SINK when the pointer arrives. */
.plan.feature:hover{box-shadow:var(--md-hover),0 28px 56px rgba(47,87,73,.20)}
.plan .price,.plan>p.muted{margin-bottom:0}
.plan ul{flex:1 0 auto}
.plan>a:last-child,.plan .btn{margin-top:auto}

/* SEASONAL & HOLIDAY CARDS — the third family the owner named. Scoped to #seasonalGrid rather than
   applied to .card globally: the generic card family was deliberately slimmed the same day
   (26→20px padding), and re-elevating all of them would undo that everywhere at once.
   These carry icons, so they also take the reference's second idea — a rule under the heading that
   WIDENS on hover. It is the one flourish in that snippet worth keeping: it draws the eye along the
   card it belongs to instead of decorating the page. */
#seasonalGrid .card{box-shadow:var(--md-rest);
  transition:transform .3s var(--md-ease),box-shadow .3s var(--md-ease)}
#seasonalGrid .card:hover{transform:scale(1.03);box-shadow:var(--md-hover);z-index:2}
#seasonalGrid .card h3{position:relative;display:inline-block;padding-bottom:8px}
#seasonalGrid .card h3::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:22px;
  background:var(--accent);border-radius:2px;transition:width .3s var(--md-ease)}
#seasonalGrid .card:hover h3::after{width:44px}
/* The icon warms to the accent on hover, as the sample's does — a VALUE change on a colour the
   brand already owns, not a new hue. */
#seasonalGrid .card .ic{transition:background-color .3s var(--ease),color .3s var(--ease)}
#seasonalGrid .card:hover .ic{background:var(--clay-100);color:var(--clay-700)}

/* A scaled card that also SLIDES is two motions for one hover — the same objection raised against
   the gallery. Reduced motion keeps the elevation change and drops the movement entirely, so the
   affordance survives without the travel. */
@media(prefers-reduced-motion:reduce){
  .plan,#seasonalGrid .card,#seasonalGrid .card h3::after{transition:box-shadow .01ms}
  .plan:hover,#seasonalGrid .card:hover{transform:none}
  #seasonalGrid .card:hover h3::after{width:22px}
}
/* Touch has no hover: :hover latches after a tap and would leave one card permanently enlarged and
   overlapping its neighbour. Elevation only, no scale. */
@media(hover:none){
  .plan:hover,#seasonalGrid .card:hover{transform:none}
}
.plan .tag{position:absolute;top:-13px;left:24px;z-index:5;background:var(--brand);color:#fff;font-size:12px;font-weight:800;
  letter-spacing:.2px;padding:5px 14px;border-radius:var(--r-full);white-space:nowrap;text-align:center;
  box-shadow:0 6px 16px rgba(15,29,23,.24)}
.plan .price{font-family:var(--font-head),serif;font-weight:600;font-size:42px;color:var(--brand-strong)}
.plan ul{list-style:none;margin:14px 0 20px}
.plan li{padding:6px 0 6px 25px;font-size:14.5px;color:var(--ink-2);position:relative}
.plan li::before{content:"✓";color:var(--ok);font-weight:700;position:absolute;left:0;top:6px}
/* Live/injected price values in bullets read as prices — brand weight + colour, kept inline (no flex-gap splitting) */
.plan li span{font-weight:700;color:var(--brand-strong)}

/* ——— emotional band ————————————————————————————————— */
.band{position:relative;overflow:hidden;background:linear-gradient(140deg,var(--pine-800),var(--pine-950));color:#fff;
  border-radius:22px;padding:34px 32px;text-align:center;box-shadow:var(--shadow-lg)}   /* was 28px / 56px flat */
.band::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(560px 300px at 88% -20%, rgba(232,168,126,.22), transparent 60%),
             radial-gradient(460px 280px at 4% 118%, rgba(232,168,126,.12), transparent 60%)}
.band>*{position:relative;z-index:1}
/* fluid-headings 2026-08-07: 38px fixed → --fs-2xl clamp(25px,6vw,34px). This is the change with
   real consequences, so the numbers are here rather than in a commit message: 38→25px at 390px
   (a 34% reduction — "Pack your bags. We'll spoil the cat." stops wrapping to three lines on a
   phone) and 38→34px on desktop, which is a deliberate 4px loss to keep one scale instead of two. */
.band h2{font-size:var(--fs-2xl);margin-bottom:12px;color:#fff}
.band p{opacity:.92;max-width:560px;margin:0 auto 24px;font-size:var(--fs-md)}

/* ——— reviews ————————————————————————————————————————— */
/* Design review 2026-08-06: the reviews are verbatim Google text of wildly unequal length (24 words
   next to 180), and NOT ONE WORD of them gets edited — the raggedness, the typos and the rambling
   detail are the trust asset. The defect was the container: stretch-aligned grid rows sized every
   card to the tallest in its row, leaving 200-440px of empty card under the short ones. One
   declaration lets each card hug its own content; the row still aligns at the top. */
#reviewsGrid{align-items:start}
.review{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:24px;
  box-shadow:var(--shadow-sm);transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.review:hover{transform:translateY(-3px);box-shadow:var(--shadow-md)}
.review .stars{color:var(--clay-500);letter-spacing:2px}
.review p{font-size:14.5px;margin:10px 0;color:var(--ink-2)}
.review .who{font-weight:700;font-size:var(--fs-sm);color:var(--brand-strong)}

/* ——— faq — quiet accordion with a smooth open ————————— */
.faq details{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 18px;margin-bottom:10px;
  box-shadow:var(--shadow-xs);transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
.faq details[open]{border-color:var(--line-strong);box-shadow:var(--shadow-sm)}
.faq summary{cursor:pointer;font-weight:600;font-family:var(--font-head),serif;font-size:16.5px;list-style:none;
  padding:14px 30px 14px 0;position:relative;min-height:44px;display:flex;align-items:center}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";position:absolute;right:2px;top:50%;transform:translateY(-50%);
  font-family:var(--font-body);font-size:var(--fs-lg);font-weight:400;color:var(--clay-600);
  transition:transform var(--dur) var(--ease)}
.faq details[open] summary::after{transform:translateY(-50%) rotate(45deg)}
.faq p{color:var(--muted);font-size:14.5px;margin:0 0 16px;animation:faq-in var(--dur) var(--ease)}
@keyframes faq-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.faq p{animation:none}.faq summary::after{transition:none}}

/* ——— footer — deep pine band ———————————————————————— */
.footer{background:linear-gradient(180deg,var(--pine-900),var(--pine-950));color:#E9E4DA;padding:56px 0 30px} /* flush with .to-dark fade above — no light gap */
.footer a{color:#E9E4DA}.footer a:hover{color:#fff}
.foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:24px}
.footer h4{font-family:var(--font-head),serif;color:var(--clay-300);margin-bottom:12px;font-size:15px;font-weight:600}
.footer ul{list-style:none;font-size:var(--fs-sm);line-height:2.1}
.foot-bottom{border-top:1px solid rgba(255,255,255,.14);margin-top:32px;padding-top:18px;font-size:var(--fs-xs);color:#C9D2CB;
  display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px}
@media(max-width:860px){.foot-grid{grid-template-columns:1fr 1fr}}

/* ——— page hero (inner pages) ————————————————————————— */
/* Design review 2026-08-06: at 14px top padding the eyebrow sat UNDER the translucent floating nav
   pill — on /member-pass the words "MEOW HOMEZ MEMBER PASSES" were literally occluded at scroll-top,
   and /memory-vault was flush against it. Interior pages get the same breathing room the homepage
   hero already has. (The two pages that also carried an inline nav-wrap padding-top:4px override
   had those removed in the same pass.) */
/* Panel audit 2026-08-07: `background:transparent` sat here and was a silent no-op with teeth. The
   shorthand resets background-image AND background-color, and `.page-hero` (0,1,0) is declared after
   both `.wash` (0,1,0, line ~264) and `.wash-hero-inner` (0,1,0) — equal specificity, later wins. So
   every interior hero carried `class="page-hero wash wash-hero-inner"`, declared a wash, and painted
   nothing. It also broke the wash-chain invariant written down 20 lines below `.wash-hero-inner`:
   the hero is supposed to END on ivory so the next band can START on ivory, and all seven pages do
   open with a `wash-iv-*` section. Removed, so the declared gradient is the rendered gradient. */
/* The gap the owner measured on /add-ons (2026-08-09): 30px of hero padding met 40px of section
   padding under it, and the h1's own 22px top margin made the block above look heavier still.
   14px here leaves one clear step between the headline and the first heading instead of three. */
/* padding-TOP is 0 on purpose (owner 2026-08-09: "the vertical distance between the top of the
   page and the nav bar keeps increasing as I visit each page"). It was not increasing — it was
   three different values, visited in ascending order:
       index          14px   (nav-wrap alone; .hero-luxe has no top padding)
       pricing        32px   (page-hero 28 + an INLINE nav-wrap padding-top:4px)
       member-pass    42px   (page-hero 28 + nav-wrap 14)
   The nav is sticky global chrome and sits INSIDE each page's hero, so the hero's own top padding
   pushed it down by a different amount on every page. Zero here hands the spacing above the nav to
   .nav-wrap alone, which every page shares — so all five now sit at 14px. The hero's breathing room
   moves BELOW the bar, where it was always meant to be. */
.page-hero{color:var(--ink);padding:0 0 14px}
/* Space for the fixed header (see the ≤860px block above). This has to live BELOW both hero rules,
   not beside the position:fixed that causes it: a media query adds no specificity, so
   `.hero-luxe .hl-in{padding:16px 0 68px}` and `.page-hero{...padding:32px 0 30px}` — both (0,2,0)
   and (0,1,0) declared later in this file — simply overwrote the compensation when it sat up there.
   Caught by measuring the gap between the header and the first line of the hero, not by reading. */
@media(max-width:860px){
  /* Measured, not guessed: with the header out of flow each hero box starts higher than its own
     padding suggests (38px and 49px of ancestor box above it), so these two numbers are the ones
     that put the first line of the hero back exactly where it sat when the header was in flow —
     78px and 94px from the top — clear of the 62px bar and with no new void. */
  .hero-luxe .hl-in{padding-top:40px}
  .page-hero{padding-top:45px}
}
.page-hero h1{font-size:var(--fs-3xl);margin-top:14px;color:var(--ink);font-weight:600;max-width:22ch}
.page-hero p{color:var(--muted);max-width:58ch;margin-top:14px;font-size:var(--fs-md)}
/* .page-hero .eyebrow{color:var(--accent)} removed 2026-08-07 — 4.22:1 on the restored clay-2 stop.
   `.wash .eyebrow{color:var(--clay-700)}` (5.36:1) now governs, which is what that rule is for. */

/* ——— form styles — moved to /components.css (Wave 7); the bare input/select/textarea rules are
   scope-gated there on body.mkt so they can never leak into the booking form or admin ——— */

/* ——— Home “Why” — warm tinted bento —————————————————— */
.why-bento{position:relative;overflow:hidden}
.why-bento .container{position:relative;z-index:1}
.why-bento::before{content:none}
.bento-why{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:6px}
.bento-why>div{position:relative;overflow:hidden;border-radius:var(--r-lg);padding:26px;
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow-sm)}
.bento-why>div::before{content:none}
.bento-why>div{transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.bento-why>div:not(.intro):hover{transform:translateY(-4px);box-shadow:var(--shadow-md)}
.bento-why .intro{grid-column:1/3;grid-row:1/3;display:flex;flex-direction:column;justify-content:center;gap:6px}
/* fluid-headings 2026-08-07: 32px fixed → --fs-2xl. 32→25px at 390px, 32→34px on desktop (2px
   larger — the clamp ceiling, not a separate decision). Shares the token with .band h2 above so
   the two section headings cannot drift apart again. */
.bento-why .intro h2{font-size:var(--fs-2xl);margin:8px 0}
.bento-why .gicon{font-size:28px;line-height:1;margin-bottom:10px}
.bento-why h3{font-size:19px;margin-bottom:6px}
.bento-why p{font-size:14.5px;color:var(--ink-2)}
/* palette-family tints — warm, quiet, still distinct (class names preserved) */
.bento-why .t-mint{background:var(--surface)}
.bento-why .t-peach{background:var(--clay-50);border-color:var(--clay-100)}
.bento-why .t-sky{background:var(--pine-50);border-color:var(--pine-100)}
.bento-why .t-lilac{background:var(--sand);border-color:var(--line)}
.bento-why .t-rose{background:var(--clay-100);border-color:var(--clay-200)}
@media(max-width:900px){.bento-why{grid-template-columns:1fr 1fr}.bento-why .intro{grid-column:1/3;grid-row:auto}}
@media(max-width:560px){.bento-why{grid-template-columns:1fr}.bento-why .intro{grid-column:auto}}
@media(prefers-reduced-motion:reduce){.why-bento::before,.bento-why>div{transition:none}.bento-why>div:not(.intro):hover{transform:none}}

/* ——— legal / long-form prose (privacy, terms) ————————— */
.legal{max-width:820px;margin:0 auto;font-size:15.5px;color:var(--ink-2)}
.legal h1,.legal h2,.legal h3,.legal h4{font-family:var(--font-head),serif;color:var(--brand-strong);line-height:1.2;margin:1.8rem 0 .6rem;font-weight:600}
.legal h1{font-size:26px}.legal h2{font-size:22px}.legal h3{font-size:18px}.legal h4{font-size:15.5px;color:var(--ink)}
.legal h2:first-child,.legal h3:first-child{margin-top:0}
.legal p{margin:.65rem 0;line-height:1.75}
.legal ul,.legal ol{margin:.6rem 0 .9rem 1.25rem}
.legal li{margin:.35rem 0;line-height:1.65}
.legal a{color:var(--brand-strong);font-weight:600;text-decoration:underline;text-decoration-color:var(--clay-300);text-underline-offset:2px}
.legal a:hover{text-decoration-color:var(--clay-600)}
.legal strong,.legal b{color:var(--ink);font-weight:700}
.legal hr{border:none;border-top:1px solid var(--line);margin:1.6rem 0}
.legal table{width:100%;border-collapse:collapse;margin:1rem 0}
.legal th,.legal td{border:1px solid var(--line);padding:8px 10px;text-align:left;font-size:var(--fs-sm)}

/* ============================================================
   2026-07 UX review additions (A1–A10):
   CLS insurance · breadcrumbs · sticky mobile CTA · pressed
   states · tap targets · price skeletons
   ============================================================ */

/* ——— A1 · image-box space reservation ———
   REVERTED (2026-07): the aspect-ratio "insurance" on .hl-photo/.ph/.mph was NOT a no-op —
   with a definite height, aspect-ratio computes the WIDTH from the ratio (it is not ignored),
   which shrank the hero photo and pushed masonry tiles out of their fixed grid rows.
   CLS is already covered the original way: explicit heights (inline styles + grid-auto-rows)
   for background boxes, and width/height attributes on every <img>. Do not re-add ratios here
   without also removing the explicit heights. */

/* ——— A5 · breadcrumbs — moved to /components.css (Wave 7) ——— */

/* ——— A2 · mobile sticky action bar (Book + WhatsApp) ——— */
.mob-cta{display:none}                     /* desktop: never shown */
@media(max-width:768px){
  .mob-cta{position:fixed;left:0;right:0;bottom:0;z-index:80;display:flex;gap:10px;
    padding:10px 14px calc(10px + env(safe-area-inset-bottom,0px));
    background:rgba(253,251,247,.96);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
    border-top:1px solid var(--line);box-shadow:0 -6px 24px rgba(15,29,23,.12);
    transition:transform var(--dur) var(--ease),visibility 0s}
  .mob-cta .btn{flex:1;min-height:48px;padding:12px 10px;font-size:15px}
  .mob-cta[hidden]{display:none}           /* pre-JS state (class+attr outweighs the display:flex above) */
  .mob-cta.mob-cta-off{transform:translateY(120%);visibility:hidden;
    transition:transform var(--dur) var(--ease),visibility 0s var(--dur)}   /* footer in view → slide away */
}
@media(prefers-reduced-motion:reduce){.mob-cta,.mob-cta.mob-cta-off{transition:none}}

/* ——— A7 · pressed feedback + referral-card states + tap targets ——— */
/* A flat darkening overlay presses ANY button variant one step (solid, light,
   ghost, outline, gold) without per-variant colour math; 60ms so it feels instant. */
.btn:active,.ref-copy:active{background-image:linear-gradient(rgba(15,29,23,.14),rgba(15,29,23,.14));transition-duration:60ms}
.ref-copy[disabled]{opacity:.55;cursor:not-allowed}
.ref-copy[disabled]:active{background-image:none}
/* sample (signed-out) referral card: amber status dot instead of the live green */
.ref-card[data-ref-state="sample"] .ref-badge::before{background:var(--clay-300);box-shadow:0 0 0 3px rgba(232,168,126,.25)}
/* footer links sit in 2.1-line-height lists (~29px) — pad the hit area to ≥44px
   with negative-margin compensation so the rendered layout doesn't move at all */
.footer ul a,.foot-bottom a{position:relative;display:inline-block;padding:8px 4px;margin:-8px -4px}

/* ——— A10 · price skeleton shimmer (applied by web-pricing.js only while the API is slow) ——— */
/* Design refresh 2026-08-06 — this skeleton used to set color:transparent!important on prices the
   server had ALREADY substituted correctly. Combined with a 300ms trigger (web-pricing.js), any
   mobile connection slower than 300ms watched the hero price badge blank out and shimmer, then
   return with the exact same number — a self-inflicted flash of missing content on the single most
   important figure on the page. The number now stays readable and just pulses; the trigger moved to
   1200ms so it only appears when the API is genuinely slow. */
.price-skel{display:inline-block;min-width:2ch;border-radius:6px;user-select:none;opacity:.55;
  background:linear-gradient(90deg,var(--sand) 25%,var(--pine-50) 37%,var(--sand) 63%);
  background-size:400% 100%;animation:price-skel 1.2s ease infinite}   /* loading state only: the class is removed the moment the fetch resolves */
@keyframes price-skel{from{background-position:100% 0}to{background-position:0 0}}
@media(prefers-reduced-motion:reduce){.price-skel{animation:none}}

/* ============================================================
   2026-07 · LEGAL PAGES v2 (.legal-*) — terms & privacy rebuild
   Long-form policy layout: ~70ch reading measure, linked TOC
   card, numbered anchor sections, definition emphasis, note
   callouts, contact card, print styles. Consumes theme.css
   tokens only — no new colours. Applied inside .legal, so the
   base .legal prose rules above still provide the foundation.
   ============================================================ */
.legal-page{max-width:70ch;margin:0 auto;font-size:var(--fs-base)}
.legal-intro{font-size:17.5px;line-height:1.75;color:var(--ink-2);margin:0 0 .4rem}
.legal-meta{display:inline-flex;flex-wrap:wrap;align-items:center;gap:4px 8px;margin:14px 0 4px;
  padding:9px 18px;background:var(--pine-50);border:1px solid var(--pine-100);border-radius:var(--r-full);
  font-size:13.5px;line-height:1.5;color:var(--ink-2)}
.legal-meta strong{color:var(--brand-strong)}
.legal-meta .legal-meta-sep{color:var(--clay-500)}

/* ——— table of contents card ——— */
.legal-toc{background:var(--paper);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:22px 26px 24px;margin:26px 0 8px;box-shadow:var(--shadow-sm)}
.legal-toc-title{display:block;font-size:var(--fs-xs);letter-spacing:2.4px;text-transform:uppercase;
  color:var(--accent);font-weight:700;margin-bottom:10px}
.legal-toc ol{list-style:none;margin:0;padding:0;columns:2;column-gap:32px}
.legal-toc li{margin:0;padding:0;break-inside:avoid;line-height:1.35}
.legal-toc a{display:flex;align-items:baseline;gap:10px;padding:7px 8px;margin:0 -8px;border-radius:var(--r-xs);
  color:var(--ink-2);font-weight:600;font-size:14.5px;text-decoration:none;
  transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.legal-toc a:hover{background:var(--pine-50);color:var(--brand-strong);text-decoration:none}
.legal-toc-num{flex:none;min-width:1.7em;font-family:var(--font-head),serif;font-weight:600;
  font-size:13.5px;color:var(--accent);font-variant-numeric:tabular-nums}
@media(max-width:620px){.legal-toc{padding:18px 18px 20px}.legal-toc ol{columns:1}}

/* ——— numbered sections with anchor offset ——— */
.legal-sec{margin-top:44px;scroll-margin-top:96px}
.legal-sec>h2{display:flex;align-items:baseline;gap:12px;font-size:23px;margin:0 0 .7rem;
  padding-bottom:10px;border-bottom:1px solid var(--line)}
.legal-sec>h2 .legal-num{flex:none;font-family:var(--font-head),serif;font-weight:600;
  font-size:19px;color:var(--accent);font-variant-numeric:tabular-nums}
.legal-sec h3{font-size:17.5px;margin:1.3rem 0 .45rem}

/* ——— definition emphasis (first-use defined terms) ——— */
.legal-def{font-style:normal;font-weight:700;color:var(--brand-strong)}

/* ——— key-point callout ——— */
.legal-note{background:var(--clay-50);border:1px solid var(--clay-200);border-radius:var(--r);
  padding:14px 18px;margin:.9rem 0}
.legal-note>p{margin:.45rem 0}
.legal-note>p:first-child{margin-top:0}
.legal-note>p:last-child{margin-bottom:0}
.legal-note strong{color:var(--clay-700)}

/* ——— contact card ——— */
.legal-contact{background:var(--paper);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-xs);padding:18px 22px;margin:1rem 0 0}
.legal-contact ul{list-style:none;margin:.5rem 0 0;padding:0}
.legal-contact li{margin:.4rem 0}

/* ——— print: clean, ink-first, keeps blocks intact ——— */
@media print{
  .legal-page{max-width:none;font-size:11.5pt;color:var(--ink)}
  .legal-intro{font-size:12.5pt;color:var(--ink)}
  .legal-meta{background:none;border:1px solid var(--line);border-radius:0;padding:4pt 8pt}
  .legal-toc{box-shadow:none;border-radius:0;break-inside:avoid;page-break-inside:avoid}
  .legal-toc a{color:var(--ink)}
  .legal-note,.legal-contact{box-shadow:none;border-radius:0;break-inside:avoid;page-break-inside:avoid}
  .legal-sec{margin-top:18pt;scroll-margin-top:0}
  .legal-sec>h2,.legal-sec h3{break-after:avoid;page-break-after:avoid}
  .legal-page a{color:var(--ink);text-decoration:none}
}

/* UX-44 (2026-07-28 audit): skip link + screen-reader-only utility. The nav sits inside each
   page's hero container, so <main> starts at the first section — the skip link jumps keyboard
   users over the six-link nav straight to content. Visible only while focused. */
.skip-link{position:absolute;left:12px;top:-48px;z-index:2000;background:var(--brand-deep,#1E3830);color:#fff;font-weight:800;font-size:var(--fs-sm);padding:10px 16px;border-radius:0 0 10px 10px;transition:top .15s}
.skip-link:focus{top:0}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* UX-82 (2026-07-28 audit): the nav never collapsed on phones — six links + CTA stacked to ~209px
   of a 375px first screen. site-enhance.js adds .mnav + the ☰ button; without JS nothing here
   applies and the shipped stacked nav still works. Desktop hides the button and shows the row. */
.mnav-btn{display:none;align-items:center;gap:7px;background:none;border:1.5px solid var(--line-strong,#D3C9B8);border-radius:var(--r-full,999px);padding:9px 15px;font:inherit;font-weight:800;font-size:var(--fs-sm);color:var(--ink);cursor:pointer}
.mnav-btn .mnav-btn-t{font-size:13.5px}
/* Design refresh 2026-08-06 REGRESSION FIX: site-enhance.js clones the header CTA into the mobile
   panel so a reader who opens ☰ can book without closing it again. The clone is a plain .btn
   .btn-solid, and the only rule that hid it lived INSIDE the 680px query — so above 680px the
   desktop header showed "Book a stay" twice, side by side. Hide the clone globally and let the
   open-panel rule below be the single place that reveals it. */
.mnav-book{display:none}
/* Panel audit 2026-08-07 — THE HEADER WAS 144px ON EVERY TABLET. This block used to open at
   680px while the sticky rule above opens at 860px, and between those two numbers the page got the
   worst of both: `position:sticky` plus the FULL six-link nav wrapped onto three rows, pinned to
   the top at 144px — 18% of a 768×820 viewport, against the 62px this refresh budgeted and
   documented. Phase 3 measured 1440px and 390px and never looked between them; no assertion covers
   a width, so the gate was green the whole time.
   One boundary now. Everything below 860px gets the same compact row and the same ☰. */
@media(max-width:860px){
  .mnav .nav-links{display:none;flex-basis:100%;flex-direction:column;align-items:stretch;gap:4px;padding:10px 2px 4px;border-top:1px dashed var(--line);margin-top:10px}
  .mnav.open .nav-links{display:flex}
  .mnav .nav-links a:not(.btn){padding:12px 14px}
  /* 44px, not 43.14px. This is the ONLY way into the menu on a phone and it was landing just under
     the tap-target floor because padding+border happened to add up to 43 — a number nobody chose.
     Pinning it also fixes the header budget: 44 + 4 + 4 of .nav padding = 52 (its min-height),
     + 4 + 4 of .nav-wrap padding = the 60px the sticky bar is allowed to cost. */
  .mnav-btn{display:inline-flex;min-height:44px}
  /* Design review 2026-08-06: pin the toggle above the panel it opens (it is the last child of
     .nav, so wrapping pushed it underneath), and give the in-panel Book CTA the full width. */
  /* `order:-1` is gone: the toggle is now inserted BEFORE the panel in the DOM (site-enhance.js),
     which fixes the tab sequence and the visual order together. The override was also jumping the
     button from the right edge of the header to the left of the logo the instant the menu opened —
     the control moving out from under the thumb that pressed it, which is the very thing the
     original fix was for. Only the open-state colours remain. */
  .mnav.open .mnav-btn{border-color:var(--brand);color:var(--brand-strong);background:var(--pine-50)}
  .mnav .nav-links .mnav-book{display:none}
  .mnav.open .nav-links .mnav-book{display:inline-flex;width:100%;margin-top:10px;min-height:50px}
}
/* UX-75: the current page's nav link (aria-current set by site-enhance.js) */
/* The current page reads as PRESSED IN — a second, non-colour signal for where you are, alongside
   the weight and tint it already had. aria-current carries it for screen readers. */
.nav-links a.nav-here:not(.btn){background:var(--clay-50,#FBF1E8);color:var(--brand-deep,#1E3830);font-weight:800;box-shadow:var(--nu-press)}
/* UX-80: data-bg tiles shimmer while their image decodes instead of sitting as blank cards */
.bg-loading{background:linear-gradient(100deg,var(--sand,#EFE7DA) 40%,var(--paper,#FDFBF7) 50%,var(--sand,#EFE7DA) 60%);background-size:200% 100%;animation:bgshimmer 1.2s linear infinite}
@keyframes bgshimmer{to{background-position:-200% 0}}
@media(prefers-reduced-motion:reduce){.bg-loading{animation:none}}

/* UX-81 (owner report 2026-07-28): one visible Book CTA at a time on phones. The bar slides in
   only after the first screenful (site-enhance.js drops .mob-cta-wait); while a bar exists
   (html.has-sticky), the header's Book button yields at phone widths — the hero CTA owns the
   first screen, the sticky bar owns the rest of the page. */
.mob-cta{transition:transform .25s ease}
.mob-cta.mob-cta-wait{transform:translateY(120%)}
@media(prefers-reduced-motion:reduce){.mob-cta{transition:none}}
/* Panel audit 2026-08-07: this yield rule stayed at 680 when the ☰ fold moved to 860, so between
   681 and 860 the header CTA and the panel's cloned CTA were both visible — two Book buttons, the
   exact duplicate this refresh already fixed once at desktop widths. One boundary, like the rest. */
@media(max-width:860px){ html.has-sticky .nav > a.btn[data-book]{display:none} }

/* Design refresh 2026-08-06: four pages hardcoded an inline font-size on a section <h2> (32px/34px),
   which is exactly how a type scale stops being a type scale. One class, on the token. */
:where(body.mkt) .h2-md{font-size:var(--fs-2xl);margin:10px 0}

/* Photography: one grade for a set shot on different phones in different light, and the paper-mat
   frame that makes the masonry read as one album instead of eight unrelated pictures. */
:where(body.mkt) .masonry img{filter:saturate(1.06) brightness(1.03);background:var(--sand)}
:where(body.mkt) .masonry .mph{background:var(--paper);padding:8px;border-radius:calc(var(--r) + 6px);
  box-shadow:var(--shadow-sm);border:1px solid var(--line-soft,#EFE8DC)}
:where(body.mkt) .masonry .mph img{border-radius:var(--r-sm)}

/* Reviews: clamp the long ones with a real disclosure rather than cutting anyone's words. The
   cards already hug their content (Phase 1); this stops one 180-word review setting the rhythm. */
:where(body.mkt) .review p{display:-webkit-box;-webkit-line-clamp:9;-webkit-box-orient:vertical;overflow:hidden}
:where(body.mkt) .review details p{display:block;-webkit-line-clamp:none;overflow:visible}
:where(body.mkt) .review details>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;
  min-height:44px;font-size:13.5px;font-weight:700;color:var(--brand-strong)}
:where(body.mkt) .review details>summary::-webkit-details-marker{display:none}
:where(body.mkt) .review details[open]>summary{color:var(--muted)}
@media(max-width:680px){ :where(body.mkt) .review p{-webkit-line-clamp:6} }

/* ——— cross-page component classes (design refresh 2026-08-07) ——————————————
   These eight declarations accounted for 97 of the 209 inline style attributes on the marketing
   pages, and every one of them is the SAME component repeated: one cross-sell card, one header, one
   footer, rendered on five or six pages each. Inline they could not be restyled, could not be
   overridden by a media query, and drifted — pricing's card copy and member-pass's card copy were
   already one pixel apart in line-height. Folded here they are one rule apiece.

   Every selector below is deliberately compound (.card h3.card-title-sm, not .card-title-sm). An
   inline style beats any class, so the moment one is removed the class has to out-specify whatever
   the element was already matching — here that is `:where(body.mkt) .card h3` (0,1,1) and
   `:where(body.mkt) .card p` (0,1,1) in components.css. A bare single-class selector would LOSE to
   both and silently resize the cards on five pages. Proven identical by full-page pixel diff at
   1440px and 390px across all six pages before this was committed. */
.card.card-link{display:block;text-decoration:none}
.card h3.card-title-sm{font-size:var(--fs-md)}
.card p.card-sub{margin-top:6px;font-size:var(--fs-sm);line-height:1.6}
.brand .brand-logo{height:28px;width:auto;vertical-align:middle;margin-right:8px}
.btn.nav-cta{padding:11px 20px}
.brand.foot-brand{color:#fff;margin-bottom:10px}
.footer p.foot-blurb{font-size:var(--fs-sm);opacity:.85;max-width:280px}
.footer a.foot-legal,.foot-bottom a.foot-legal{color:var(--gold)}

/* Panel audit 2026-08-07: a long review used to be emitted twice — once clamped, once in full
   inside the disclosure — so opening it showed the same nine lines again. The text is emitted once
   now; the <details> sits BEFORE it in the DOM purely so this sibling selector can reach the
   paragraph, and `order` puts the toggle back underneath where a reader expects it. */
:where(body.mkt) .review{display:flex;flex-direction:column}
:where(body.mkt) .review .stars{order:1}
:where(body.mkt) .review p{order:2}
:where(body.mkt) .review .rv-more{order:3}
:where(body.mkt) .review .who{order:4}
:where(body.mkt) .review .rv-more[open] + p{-webkit-line-clamp:unset;overflow:visible;display:block}

/* ═══ WHERE WE ARE — one home, and how far it is from you ═════════════════════════════════════
   Owner report 2026-08-15: "many customers ask are you in kanata, vanier etc." The section used to
   be a heading reading "cage-free cat sitting NEAR YOU" over three area cards, which reads as three
   locations when there is one. It now leads with the address and reframes the neighbourhoods as
   drive times TO us. See the comment in index.html for the full reasoning and the provenance of
   the figures (measured, not estimated).
   Scoped under body.mkt like everything else in the marketing families — the .card/.ic/.eyebrow2
   pieces it reuses are all defined there and would collide with the booking SPA otherwise. */
:where(body.mkt) .area-split{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(18px,3vw,34px);align-items:stretch}
/* STRETCH, not start (seen on the deployed page 2026-08-15): the drive-time column runs ~400px
   taller than the address card, and with `align-items:start` the card hugged its own content and
   left a hole the size of a card underneath it. Two columns presented as one answer should read as
   one object. The card fills the row and pushes its link to the foot, which is also where the eye
   expects to leave from. */
:where(body.mkt) .area-here{display:flex;flex-direction:column;text-decoration:none;color:inherit;
  background:linear-gradient(160deg,var(--pine-50,#EEF3EE),var(--surface) 62%)}
:where(body.mkt) .area-here>p{margin-bottom:0}
:where(body.mkt) .area-addr{font-family:var(--font-head);font-size:clamp(21px,2.2vw,27px);font-weight:600;
  letter-spacing:-.02em;line-height:1.25;color:var(--ink);margin:8px 0 10px}
:where(body.mkt) .area-go{display:inline-flex;align-items:center;gap:8px;margin-top:auto;padding-top:18px;
  font-size:var(--fs-sm);font-weight:700;color:var(--pine-700,#27493E)}
/* The arrow hints in the direction of travel rather than merely decorating the label. */
:where(body.mkt) .area-go .arw{display:inline-block;transition:transform .22s var(--ease)}
@media(hover:hover) and (pointer:fine){
  :where(body.mkt) .area-here:hover .area-go .arw{transform:translateX(4px)}
}
:where(body.mkt) .area-times h3{font-size:var(--fs-lg);margin-bottom:6px}
:where(body.mkt) .area-times>.muted{font-size:var(--fs-sm);margin-bottom:14px}
/* Two columns of hairline rows: a table's scannability without a table's weight. Tabular figures so
   the minutes line up as a column the eye can run down. */
:where(body.mkt) .drive{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;
  gap:0 clamp(16px,3vw,30px)}
:where(body.mkt) .drive li{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:10px 0;border-bottom:1px solid var(--line);font-size:var(--fs-base);color:var(--ink-2)}
:where(body.mkt) .drive b{font-variant-numeric:tabular-nums;font-weight:700;color:var(--ink);white-space:nowrap}
:where(body.mkt) .area-far{font-size:var(--fs-xs);margin-top:14px}
@media(max-width:880px){ :where(body.mkt) .area-split{grid-template-columns:1fr} }
@media(max-width:520px){ :where(body.mkt) .drive{grid-template-columns:1fr} }
@media(prefers-reduced-motion:reduce){ :where(body.mkt) .area-go .arw{transition:none} }

/* ═══ MOTION (owner 2026-08-15: "I don't see the animations etc that were in the mockup") ═════
   The mockup's motion, ported. Deliberately only the parts that EARN their place on a page a
   customer scrolls once: feedback that says a control heard them, and a reveal that gives a long
   page a reading rhythm. No ambient loops, no sheens, no parallax.
   Every rule is additive and scoped to body.mkt, and every one is switched off under
   prefers-reduced-motion at the foot of this block. */

/* PRESS FEEDBACK. The marketing buttons lifted on hover and did nothing on tap — and a phone has
   no hover, so the primary CTA of a booking flow gave the finger no reply at all. On a slow
   connection that reads as "it didn't work", and people tap again. 100ms because :active fires on
   pointer-DOWN, and anything slower there is latency on the input path. */
:where(body.mkt) .btn:active,
:where(body.mkt) .btn-solid:active,
:where(body.mkt) .btn-outline:active,
:where(body.mkt) .btn-light:active,
:where(body.mkt) .btn-gold:active{transform:scale(.97);transition:transform .1s var(--ease)}
/* STICKY HOVER. A tap fires :hover on touch and it latches until something else is tapped,
   leaving a button hanging above the page with no pointer near it. Hover lift is a mouse
   affordance; where there is no mouse there is nothing to afford. */
@media(hover:none){
  :where(body.mkt) .btn:hover,:where(body.mkt) .btn-solid:hover,:where(body.mkt) .btn-outline:hover,
  :where(body.mkt) .card:hover,:where(body.mkt) .masonry .mph:hover{transform:none}
}

/* SCROLL REVEAL. 14px and opacity, once per element, transform+opacity only so it never touches
   layout. The content is in the DOM and clickable whether or not the observer ever fires — the
   .reveal class is only added BY the script, so a page with no JS shows everything immediately.
   That ordering is the whole safety story: decoration can never hide a page. */
/* THE ARMING CLASS IS COMPOUNDED ONTO body, NOT NESTED INSIDE IT. Shipped 2026-08-15 as
   `:where(body.mkt) .rv-on .rv` — a DESCENDANT combinator — while the script put `rv-on` on
   <html>, body's ancestor. The selector could never match anything, so nothing was ever hidden and
   nothing ever animated. It failed safe (the page rendered exactly as before, which is the whole
   point of arming from JS) and therefore failed SILENTLY: no error, no console warning, no failing
   test, and a homepage that simply looked unchanged after a deploy.
   Pinned now by homepage-contract.test.js, which matches this selector against the real shipped
   markup — a rule that matches zero elements is the bug, and only a real DOM can see it. */
:where(body.mkt).rv-on .rv{opacity:0;transform:translateY(14px)}
:where(body.mkt).rv-on .rv.rv-in{opacity:1;transform:none;
  transition:opacity .5s var(--ease),transform .5s var(--ease);transition-delay:var(--rv-d,0ms)}

@media(prefers-reduced-motion:reduce){
  :where(body.mkt).rv-on .rv{opacity:1;transform:none}
  :where(body.mkt) .btn:active,:where(body.mkt) .btn-solid:active,:where(body.mkt) .btn-outline:active,
  :where(body.mkt) .btn-light:active,:where(body.mkt) .btn-gold:active{transform:none}
}

/* ═══ THE VISUAL PASS — all sections, 2026-08-15 ══════════════════════════════════════════════
   Ported from docs/design/homepage-next.html after inspecting the deployed page and MEASURING what
   was actually wrong, rather than restyling by eye. Additive and scoped; nothing above is deleted.
   Everything here is either a proportion or a material — no font-size declarations, so the
   size-token audit in design-refresh-phase2 stays exactly where it is. */

/* ── 1. OPTICAL TYPOGRAPHY ────────────────────────────────────────────────────────────────────
   MEASURED ON PROD: h1 52px / lh 1.12 / tracking -0.4px, h2 34px / lh 1.12 / tracking -0.4px.
   One fixed -0.4px for every size is the defect. As type grows the same absolute value becomes a
   smaller proportion, so display text reads progressively looser exactly where it should tighten:
   -0.4px is -0.008em at 52px but -0.012em at 34px — backwards. Tracking has to be expressed in em
   so it scales, and leading has to close as size opens. Fraunces carries a real optical-size axis;
   ask it for the drawing meant for this size instead of a scaled-up small one. */
:where(body.mkt) h1,:where(body.mkt) h2,:where(body.mkt) h3{font-optical-sizing:auto}
:where(body.mkt) h1{letter-spacing:-.028em;line-height:1.03}
:where(body.mkt) h2{letter-spacing:-.022em;line-height:1.08}
:where(body.mkt) h3{letter-spacing:-.012em;line-height:1.22}
:where(body.mkt) .lead{letter-spacing:-.004em}

/* ── 2. EQUAL CARDS ───────────────────────────────────────────────────────────────────────────
   MEASURED: .grid.g2 holding the two plans came back 340px and 299px — a 41px step between two
   cards offered as a CHOICE. A height difference reads as one of them mattering more, which is not
   the message when the whole point is "two simple ways to stay". Same for the g3 rows.
   The bento is deliberately excluded: its asymmetry IS the layout. */
:where(body.mkt) .grid.g2,:where(body.mkt) .grid.g3,:where(body.mkt) .grid.g4{align-items:stretch}
/* `:not([hidden])` IS THE WHOLE POINT OF THIS LINE. Shipped without it on 2026-08-15 and it put an
   empty "Peak-season dates" card on the live /pricing page advertising a surcharge the owner had
   switched OFF. The HTML `hidden` attribute is enforced by the UA sheet's `[hidden]{display:none}`,
   which carries a specificity of (0,1,0) — this selector is (0,2,1) and simply outranked it, so
   every deliberately-hidden card in a grid came back. web-pricing.js was behaving perfectly; a
   layout rule resurrected what it had correctly hidden.
   Any rule that sets `display` on a class which is ALSO toggled by [hidden] has to say so. */
:where(body.mkt) .grid.g2>.card:not([hidden]),:where(body.mkt) .grid.g3>.card:not([hidden]),
:where(body.mkt) .grid.g4>.card:not([hidden]),
:where(body.mkt) .grid>.plan:not([hidden]){display:flex;flex-direction:column}
/* Belt to those braces: no future rule in this file can un-hide an element either. */
:where(body.mkt) [hidden]{display:none !important}
/* Pin the action to the foot so both buttons sit on one baseline however the copy changes later. */
:where(body.mkt) .grid>.plan>.btn,:where(body.mkt) .grid>.card>.btn{margin-top:auto}
:where(body.mkt) .grid>.plan>ul{flex:1 0 auto}

/* ── 3. THE CLOSING BAND ──────────────────────────────────────────────────────────────────────
   MEASURED: 1072px wide inside a 1120px container — effectively full-bleed. The quietest message
   on the page was its physically largest object. A closing line should read as a held breath, not
   a billboard. Colours and content untouched; only the frame. */
:where(body.mkt) .band{max-width:940px;margin-inline:auto}

/* ── 4. THE HERO PRICE PLATE ──────────────────────────────────────────────────────────────────
   MEASURED: rgba(253,251,247,.94) — a near-opaque PALE card over the brightest part of the window.
   Light material on a busy light backdrop is the one combination that always loses legibility, and
   this plate carries the headline price.
   IT DOES NOT MOVE, and that is a deliberate reversal. The mockup relocated it to the foot of the
   photo; on the real page the badge is a SIBLING of .hl-photo, and .hl-photo carries role="img".
   Nesting the link inside an element with role="img" makes it a leaf to assistive tech — the price
   and the Member Pass link would vanish for a screen reader. A prettier plate is not worth that.
   So it stays put and the actual defect is fixed in place: dark glass instead of pale, with a short
   scrim behind it. Dark material over a photograph stays readable where pale material cannot. */
:where(body.mkt) .hero-luxe .hl-photo{position:relative;isolation:isolate}
:where(body.mkt) .hero-luxe .hl-photo::after{content:"";position:absolute;inset:0 0 auto 0;height:42%;
  z-index:0;pointer-events:none;border-radius:var(--r-lg) var(--r-lg) 0 0;
  background:linear-gradient(to bottom,rgba(15,29,23,.5),rgba(15,29,23,0))}
:where(body.mkt) .hero-luxe .hl-badge{z-index:1;
  background:rgba(21,39,32,.55);border:1px solid rgba(255,255,255,.16);
  border-top-color:rgba(255,255,255,.3);color:#fff;
  -webkit-backdrop-filter:blur(24px) saturate(170%);backdrop-filter:blur(24px) saturate(170%);
  box-shadow:0 8px 30px rgba(15,29,23,.32)}
/* Vibrancy: text over a translucent surface needs more contrast and a little more weight than text
   over paper, or it dissolves into whatever is behind it. */
:where(body.mkt) .hero-luxe .hl-badge .big{color:#fff}
:where(body.mkt) .hero-luxe .hl-badge .big .per,
:where(body.mkt) .hero-luxe .hl-badge .s{color:rgba(255,255,255,.88);font-weight:500}
:where(body.mkt) .hero-luxe .hl-badge .up{color:var(--clay-200);border-top-color:rgba(255,255,255,.22)}
@media(prefers-reduced-transparency:reduce){
  :where(body.mkt) .hero-luxe .hl-badge{background:rgba(15,29,23,.95);
    -webkit-backdrop-filter:none;backdrop-filter:none}
}
/* Below the hero breakpoint the plate is already static and stacked on paper — restore it fully so
   none of the dark-material rules leak into a layout with no photograph behind them. */
@media(max-width:900px){
  :where(body.mkt) .hero-luxe .hl-badge{background:var(--surface);border-color:var(--line);
    color:var(--ink);-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:var(--shadow-sm)}
  :where(body.mkt) .hero-luxe .hl-badge .big{color:var(--ink)}
  :where(body.mkt) .hero-luxe .hl-badge .big .per,
  :where(body.mkt) .hero-luxe .hl-badge .s{color:var(--muted)}
  :where(body.mkt) .hero-luxe .hl-badge .up{color:var(--clay-700);border-top-color:var(--clay-200)}
  :where(body.mkt) .hero-luxe .hl-photo::after{content:none}
}

/* ── 5. WHAT IS DELIBERATELY LEFT UNEQUAL ─────────────────────────────────────────────────────
   The reviews grid measures a 211px spread across nine cards and that is CORRECT. They are
   verbatim Google text of wildly unequal length (24 words next to 180), not one word edited, and
   the raggedness is the trust asset — the existing clamp + <details> disclosure already handles the
   long ones without cutting anyone's words. The equal-height rule above targets .card and .plan
   only, so .review is untouched; this note exists so nobody "fixes" it later.
   The bento is excluded for the same kind of reason: its asymmetry IS the layout. */

/* ── 1b. LEADING AND TRACKING TRACK THE SIZE (mobile audit, 2026-08-15) ───────────────────────
   MEASURED IN A 386px VIEWPORT: h1 renders 34px, and at the display leading of 1.03 the rendered
   line boxes OVERLAPPED by 7.2px. Which is the exact mistake this block was written to fix, made
   again in the other direction: I replaced one fixed tracking value with one fixed LEADING value.
   Tight leading is right for 52px display type and wrong for a 34px headline on a phone — leading
   has to open as size closes, the same way tracking has to relax.
   1.13 is not a guess: the page shipped at 1.12 for months with no complaint, so this restores a
   proven value on small screens while keeping the tighter setting where the type is genuinely
   large. Tracking eases back too — smaller text needs less tightening than display text. */
@media(max-width:860px){
  :where(body.mkt) h1{line-height:1.13;letter-spacing:-.02em}
  :where(body.mkt) h2{line-height:1.16;letter-spacing:-.016em}
  :where(body.mkt) h3{line-height:1.28;letter-spacing:-.008em}
}

/* ===== 2026-08-25 · SMOOTHER PAGE-TO-PAGE NAVIGATION ============================================
   Every marketing link is a full document load: white flash, header redrawn from scratch, scroll
   snapped to the top. Cross-document view transitions turn that into one short cross-fade, and
   because the header and footer are NAMED, the browser matches them across the two documents and
   carries them over instead of repainting them. The chrome appears to stay still while only the
   content changes, which is the whole feeling of "it didn't reload".

   Why this and not a router: it is nine lines of CSS with no JavaScript, no build step and no new
   dependency (rule 1). Browsers without support ignore the at-rule entirely and navigate exactly
   as they do today, so there is no fallback to maintain and nothing to break.

   Names must be unique per document — .nav-wrap and .footer each appear exactly once on all eight
   pages (checked before naming them; a duplicate makes the browser skip the transition and log).
   Their default group animation is deliberately left alone: the two snapshots look near-identical,
   so the built-in morph reads as "the header never moved", while any real difference (the active
   pill moving to another link) eases across rather than jumping. */
@view-transition { navigation: auto; }
.nav-wrap { view-transition-name: site-header; }
.footer   { view-transition-name: site-footer; }
::view-transition-old(root) { animation: mh-nav-out 140ms ease-in both; }
::view-transition-new(root) { animation: mh-nav-in 280ms var(--ease, cubic-bezier(.2,.7,.2,1)) both; }
@keyframes mh-nav-out { to { opacity: 0 } }
@keyframes mh-nav-in { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }
/* The whole point of the transition is motion, so reduced-motion gets none of it: the page simply
   swaps, exactly as it does today. Covers the named elements too, hence the universal selector. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important }
}
