/* ============================================================================
   BARK INN BOUTIQUE BOARDING — Birch Hills, Saskatchewan
   ----------------------------------------------------------------------------
   STRUCTURE: conventional and on purpose. A sticky top header, a photographic
   hero, then a single column of sections. An earlier version tried a fixed left
   brand rail to be "different" — it was rejected, and it deserved to be: it was
   worse on a phone, which is where nearly all of this traffic lands.

   The thing that makes this site is THEIR PHOTOGRAPHS. Eleven of them were
   already in their Book'n Buddy landing-page record and the first build shipped
   without any of them. Everything below is a frame for the pictures.

   PHONE FIRST. Base styles ARE the phone styles; desktop is the media query.
   Written that way deliberately after "it sucks on a phone".

   PALETTE sampled from their logo file, not guessed:
     #7f6e9a lavender · #51746d teal · #f1d5d2 blush · #d4cfb1 sand · #708f82 sage

   CONTRAST: none of the painted colours can carry text (blush ≈1.2:1 on white),
   and three of them failed when measured in the page — --muted at 4.13:1 across
   32 elements, the 11px kickers at 4.29:1, and display numerals in blush at
   1.30:1. The corrected values are below. DO NOT lighten them back toward the
   painted originals: painted colours belong on backgrounds, never under type.
   ========================================================================= */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Poppins:wght@300;400;500&display=swap');

:root {
  /* painted — BACKGROUNDS AND DECORATION ONLY, never under text */
  --blush:#f1d5d2; --sand:#d4cfb1; --sage:#708f82; --teal:#51746d;
  /* readable — measured on white AND on --linen, the palest ground on the page */
  --lavender:#6d5b89;   /* 5.98:1 white / 5.61:1 linen */
  --sage-deep:#5f7d70;  /* 4.24:1 linen — large display type only */
  --muted:#6a6a75;      /* 5.42:1 white / 5.09:1 linen */
  --teal-deep:#2f6470; --plum:#5b4a7a; --ink:#4a4a52;
  --hair:#e8e4ec; --paper:#ffffff; --mist:#faf8fb; --linen:#fbf7f4;
  --display:'Cormorant Garamond', Georgia, serif;
  --sans:'Poppins', system-ui, -apple-system, sans-serif;
  --ease:cubic-bezier(.22,.61,.36,1);
  --gut:1.35rem;                     /* the phone gutter, used everywhere */
  --bar:76px;                        /* the sticky phone book bar — body reserves it */
}

*,*::before,*::after { box-sizing:border-box; }
* { margin:0; }
html { -webkit-text-size-adjust:100%; scroll-behavior:smooth; overflow-x:clip; }
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important} }

body { font-family:var(--sans); font-weight:300; color:var(--ink); background:var(--paper);
       line-height:1.8; font-size:16.5px; -webkit-font-smoothing:antialiased;
       overflow-x:clip; padding-bottom:var(--bar); }

/* height:auto is load-bearing — images carry width/height attributes, and without
   it the ATTRIBUTE height is used literally and aspect-ratio cannot win. */
img, svg, iframe { display:block; max-width:100%; }
img { height:auto; }

.skip { position:absolute; left:-9999px; }
.skip:focus { left:1rem; top:1rem; z-index:200; background:var(--teal-deep); color:#fff;
              padding:.7rem 1rem; border-radius:4px; }
:focus-visible { outline:2px solid var(--lavender); outline-offset:3px; }

h1,h2,h3 { font-weight:400; letter-spacing:-.005em; }
h1 { font-family:var(--display); font-size:clamp(2.5rem,10vw,4.6rem); line-height:1.02; }
h2 { font-family:var(--display); font-size:clamp(2rem,6.4vw,3rem); line-height:1.08; color:var(--teal-deep); }
h3 { font-family:var(--sans); font-weight:500; font-size:1.04rem; color:var(--ink); line-height:1.4; }
p { text-wrap:pretty; }
em { font-family:var(--display); font-style:italic; color:var(--plum); }

/* ── header ──────────────────────────────────────────────────────────────── */
/* The frost lives on ::before. On the header itself, backdrop-filter would make
   it a containing block for any position:fixed descendant. */
.site-header { position:sticky; top:0; z-index:60; border-bottom:1px solid var(--hair); }
.site-header::before { content:''; position:absolute; inset:0; background:rgba(255,255,255,.92);
                       -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px); z-index:-1; }
.hbar { display:flex; align-items:center; gap:.7rem; padding:.6rem var(--gut);
        max-width:1180px; margin:0 auto; }
.brand { display:flex; align-items:center; gap:.6rem; text-decoration:none; margin-right:auto; min-width:0; }
.brand img { width:40px; border-radius:50%; flex:none; transition:width .25s var(--ease); }
.brand b { font-family:var(--display); font-weight:500; font-size:1.22rem; color:var(--teal-deep);
           line-height:1; white-space:nowrap; }
.brand small { display:block; font-size:.5rem; letter-spacing:.24em; text-transform:uppercase;
               color:var(--lavender); margin-top:.28rem; }
.scrolled .brand img { width:32px; }

/* Nav is a horizontally SCROLLING TAB STRIP, never a position:fixed off-canvas
   drawer — a closed drawer parks off-screen right and creates horizontal scroll
   that overflow-x on body cannot clip. */
.tabs { display:flex; gap:0; overflow-x:auto; scrollbar-width:none; border-top:1px solid var(--hair);
        -webkit-mask-image:linear-gradient(to right,#000 90%,transparent);
                mask-image:linear-gradient(to right,#000 90%,transparent); }
.tabs::-webkit-scrollbar { display:none; }
.tabs a { white-space:nowrap; padding:.85rem .8rem; font-size:.8rem; color:var(--ink);
          text-decoration:none; border-bottom:2px solid transparent; }
.tabs a:first-child { padding-left:var(--gut); }
.tabs a:hover, .tabs a.here { color:var(--teal-deep); border-bottom-color:var(--teal-deep); }
.hbar .btn { display:none; }

/* ── buttons — 48px min target, thumbs are not mice ──────────────────────── */
.btn { display:inline-flex; align-items:center; justify-content:center; min-height:48px;
       padding:.8rem 1.5rem; font-family:var(--sans); font-size:.76rem; font-weight:400;
       letter-spacing:.16em; text-transform:uppercase; text-decoration:none;
       color:var(--teal-deep); border:1px solid var(--teal-deep); background:transparent;
       transition:background .2s, color .2s; cursor:pointer; }
.btn:hover { background:var(--teal-deep); color:#fff; }
.btn-solid { background:var(--teal-deep); color:#fff; }
.btn-solid:hover { background:var(--plum); border-color:var(--plum); color:#fff; }
.btn-light { color:#fff; border-color:rgba(255,255,255,.75); }
.btn-light:hover { background:#fff; color:var(--teal-deep); border-color:#fff; }
.actions { display:flex; flex-wrap:wrap; gap:.6rem; margin-top:1.8rem; }
.actions .btn { flex:1 1 auto; }

/* ── layout ──────────────────────────────────────────────────────────────── */
section { padding:clamp(3rem,9vw,5.5rem) 0; }
/* Tinted bands carry a soft wash of the logo's painted colours — blush at one
   corner, sage at the other, at an opacity low enough that the measured text
   contrast on --linen is unchanged (re-swept: 0 failures). It is the one nod to
   the watercolour wreath anywhere in the layout; without it the page is a very
   clean grid with no relationship to their brand at all. Decoration only —
   pointer-events:none, aria-hidden by virtue of being a pseudo-element. */
section.tint { background:var(--linen); position:relative; isolation:isolate; overflow:hidden; }
section.tint::before, section.tint::after {
  content:''; position:absolute; z-index:-1; pointer-events:none; border-radius:50%;
  width:min(56vw,620px); aspect-ratio:1; filter:blur(6px); }
section.tint::before { top:-16%; right:-14%; opacity:.30;
  background:radial-gradient(closest-side,var(--blush),transparent 70%); }
section.tint::after { bottom:-20%; left:-16%; opacity:.22;
  background:radial-gradient(closest-side,var(--sand),transparent 70%); }
section.tint:nth-of-type(even)::before { background:radial-gradient(closest-side,var(--sage),transparent 70%); opacity:.16; }
/* The header is sticky, so an anchor jump parks the section heading UNDERNEATH
   it. scroll-margin-top is the whole fix — measured header height plus a little
   air. Applies to smooth scrolling and to a pasted #hash on first load alike. */
section[id], .hero[id] { scroll-margin-top:106px; }

/* THE CLOSING BAND IS INVERTED. Every page ran cream → linen → cream → linen
   from the header to the footer, which is the whole of "it feels
   monochromatic": there was no point on any page where the value changed. The
   hero panel was the only dark surface on the entire site, and it is above the
   fold on one page out of seven.

   The CTA is the right block to invert. It is the most important thing on
   every page, it is the last thing before the footer, and it was previously
   indistinguishable from the three text sections above it. One inverted band
   per page gives the scroll a destination instead of a fade-out.

   White on --teal-deep measures 6.60:1 — comfortably past 4.5:1 for body text.
   Do NOT swap this for --teal (#51746d, 3.9:1) to "soften" it. */
#cta { background:var(--teal-deep); }
#cta::before, #cta::after { display:none; }          /* kill the .tint blooms */
/* `.narrow` is max-width + `margin:0 auto`, so the CTA copy sat centred in the
   band while every other block on the page starts at the same left edge — a
   left-aligned paragraph floating in the middle of a colour field. The page
   now has one left rail (page head, priorities, cols, suites); the closing
   band holds it too. Asymmetry needs an anchor. */
@media (min-width:820px){ #cta .narrow { margin-inline:0; } }
#cta .kicker { color:#fff; opacity:.78; }
#cta h2 { color:#fff; }
#cta .lede { color:#fff; opacity:.93; }
#cta .btn { color:#fff; border-color:rgba(255,255,255,.55); }
#cta .btn:hover { background:rgba(255,255,255,.12); border-color:#fff; }
#cta .btn-solid { background:#fff; color:var(--teal-deep); border-color:#fff; }
#cta .btn-solid:hover { background:transparent; color:#fff; border-color:#fff; }
.wrap { width:100%; max-width:1180px; margin:0 auto; padding:0 var(--gut); }
.narrow { max-width:660px; }
.kicker { font-size:.66rem; letter-spacing:.24em; text-transform:uppercase;
          color:var(--lavender); display:block; margin-bottom:.85rem; }

/* Links inside the content. Without this they fall back to browser default blue
   with a full underline — which is exactly what the address in the Visit block
   and the sign-in link under the calendar were doing. Nothing else on the page
   is that colour, so it reads as a mistake rather than a link.
   teal-deep is 6.6:1 on white and 6.2:1 on --linen; :not(.btn) keeps it off the
   buttons, which own their own colour. */
section a:not(.btn), .site-foot a {
  color:var(--teal-deep); text-decoration:underline; text-underline-offset:.18em;
  text-decoration-thickness:1px; text-decoration-color:rgba(47,100,112,.38);
  transition:text-decoration-color .25s var(--ease), color .25s var(--ease); }
section a:not(.btn):hover, .site-foot a:hover {
  color:var(--plum); text-decoration-color:currentColor; }
.lede { font-size:1.06rem; color:var(--muted); margin-top:1.1rem; }

/* ── hero — a photograph, not a colour block ─────────────────────────────── */
/* PHONE: text sits on a solid dark panel BELOW the photo, not over it.
   Their hero is a sunlit lawn. Measured over the photo, the white kicker came out
   at 1.58:1 and the headline at 1.84:1 — a scrim heavy enough to fix that (~70%
   coverage over bright grass) would have to cover most of the frame, which throws
   away the picture that is the entire reason the page works. On a phone the text
   block is simply too tall to fit the dark bottom third. So it doesn't overlay:
   photo, then panel. Guaranteed 12:1, photograph untouched.
   The overlay returns at >=900px where the frame is wide and the text is short. */
/* overflow:hidden is load-bearing — the hero photo animates in from scale(1.07)
   and would otherwise widen the document for the duration. */
.hero { position:relative; isolation:isolate; padding:0; overflow:hidden; }
/* PHONE HERO SHOWS THE WHOLE PHOTOGRAPH. This was aspect-ratio:4/5, which took
   a 1.78 landscape and displayed the middle 45% of it — both dogs survived,
   but their bodies and the whole yard did not, on the device most of this
   site's traffic uses.

   Natural is safe here in a way it would not be on desktop: below 900px the
   hero copy is a solid navy block in normal flow BELOW the image (`.veil` is
   display:none, `.say` has its own background), so the band's height and the
   text layout are independent. At >=900px `.say` becomes an absolutely
   positioned overlay on the photograph, which is why the wider bands at the
   620px and 900px breakpoints below are kept.

   heroSettle animates scale(1.07) -> none and `.hero` is overflow:hidden, so
   the drift-in still works: it overscans during the animation and settles to
   the whole frame. object-fit/object-position are retained for the cropped
   bands at the breakpoints below; at this width nothing is cropped. */
.hero img { width:100%; height:auto; aspect-ratio:auto;
            object-fit:cover; object-position:center 42%; }
.hero .veil { display:none; }
.hero .say { background:#1e3a40; padding:1.9rem var(--gut) 2.2rem; color:#fff; }
.hero .kicker { color:#fff; opacity:.82; }
.hero h1 { color:#fff; }
.hero h1 em { color:#fff; opacity:.9; display:block; }
.hero p { color:#fff; opacity:.94; margin-top:.9rem; max-width:34ch; font-size:1.02rem; }
.hero .actions .btn:first-child { background:#fff; color:#1e3a40; border-color:#fff; }
.hero .actions .btn:first-child:hover { background:transparent; color:#fff; border-color:#fff; }

/* ── the facts strip ─────────────────────────────────────────────────────── */
.facts { display:grid; grid-template-columns:repeat(2,1fr); gap:1px; background:var(--hair);
         border-block:1px solid var(--hair); }
.facts div { background:var(--paper); padding:1.15rem var(--gut); }
.facts b { display:block; font-family:var(--display); font-size:1.6rem; color:var(--teal-deep); line-height:1.1; }
.facts span { font-size:.74rem; letter-spacing:.05em; color:var(--muted); }

/* ── plain lists of claims, ticked ───────────────────────────────────────── */
.ticks { list-style:none; padding:0; margin:1.6rem 0 0; display:grid; gap:.8rem; }
.ticks li { display:grid; grid-template-columns:1.4rem 1fr; gap:.7rem; align-items:start; font-size:1rem; }
.ticks svg { width:16px; height:16px; margin-top:.42rem; color:var(--sage-deep); flex:none; }

/* ── figures ─────────────────────────────────────────────────────────────── */
figure { margin:0; }

/* PHOTOGRAPHS ARE SHOWN WHOLE. This used to be
       figure img { width:100%; aspect-ratio:4/3; object-fit:cover }
   with .wide-shot overriding to 4/3, then 3/4 on desktop, and .head-photo to
   4/3 then 5/6. Every one of those is a centre-crop, and they were measured on
   2026-08-12 against the client's own originals:

       yard-open, yard-play, daily-routine   42% of the frame visible
       astrid, fenced-1                      42%
       about-head, trial-head                47%
       facility-head                         57%
       trial, why-started, yard-wide         75%

   The files themselves are faithful to her originals — near enough 100% in
   every case. The loss was ALL here, at render time. The worst cases are the
   ratio inversions: astrid and fenced-1 are 0.56 portraits forced into a 1.33
   landscape, and the three yard shots are 1.78 landscapes forced into a 0.75
   portrait. More than half of each picture, gone.

   A previous pass found this on exactly one photo — the owners' shot, cut in
   half down Jayde's face — and added an opt-in `.natural` class rather than
   changing the default. Eleven more photos had the same disease. So the
   default is now natural and there is nothing to opt into.

   `max-height` is paired with `width:auto`, NOT `width:100%`: with width:100%
   and height:auto a max-height clamps the height while the width stays at
   100%, which stretches the picture. width:auto + max-width:100% + max-height
   lets the browser scale both axes, so the ratio survives. */
figure img {
  width:auto; height:auto;
  max-width:100%; max-height:620px;
  aspect-ratio:auto; object-fit:contain;
  margin-inline:auto;
}
figcaption { font-size:.78rem; color:var(--muted); margin-top:.55rem; }
.wide-shot { margin-top:2.2rem; }
/* `.natural` is now the default behaviour. Kept as a no-op so the one existing
   use of it in the HTML does not become a dangling class. */
.wide-shot.natural img { aspect-ratio:auto; height:auto; }

/* SUITE CARDS SHOW THE WHOLE PHOTOGRAPH — no forced ratio, no cover-crop.
   These are pictures of a ROOM. A 4/3 cover-crop threw away whatever fell
   outside the middle band, so the framing depended on the source ratio rather
   than on anything anyone chose, and a portrait taken inside a pen lost its
   floor and its ceiling.

   `max-height` is paired with `width:auto`, NOT `width:100%`. With width:100%
   and height:auto a max-height clamps the height while the width stays at
   100% — which stretches the picture. width:auto + max-width:100% +
   max-height lets the browser scale BOTH axes and the ratio survives. */
.suite figure img {
  width:auto; height:auto;
  max-width:100%; max-height:520px;
  aspect-ratio:auto; object-fit:contain;
  margin-inline:auto;
}

/* two photos side by side on a phone is two thumbnails; stack them */
.pair { display:grid; gap:1rem; margin-top:2.2rem; }

/* ── the day ─────────────────────────────────────────────────────────────── */
.hours-list { margin-top:2rem; display:grid; gap:0; }
.hour { display:grid; grid-template-columns:2.7rem 1fr; gap:1rem; align-items:start;
        padding:1.3rem 0; border-top:1px solid var(--hair); }
.hour:last-child { border-bottom:1px solid var(--hair); }
.hour .num { font-family:var(--display); font-size:1.9rem; font-weight:300; line-height:1;
             color:var(--sage-deep); }
.hour .when { font-family:var(--display); font-style:italic; font-size:1rem; color:var(--plum);
              display:block; margin-bottom:.1rem; }
.hour p { color:var(--muted); font-size:.95rem; margin-top:.2rem; }

/* ── suites ──────────────────────────────────────────────────────────────── */
.suite { border-top:1px solid var(--hair); padding:1.8rem 0; }
.suite:last-of-type { border-bottom:1px solid var(--hair); }
.suite .head { display:flex; align-items:baseline; gap:.8rem; flex-wrap:wrap; }
.suite .size { font-family:var(--display); font-size:2rem; color:var(--teal-deep); line-height:1; }
.suite .rate { font-family:var(--display); font-size:1.5rem; color:var(--plum); margin-left:auto; }
.suite .rate small { font-family:var(--sans); font-size:.6rem; letter-spacing:.16em;
                     text-transform:uppercase; color:var(--muted); margin-left:.4rem; }
.suite .name { font-size:.68rem; letter-spacing:.2em; text-transform:uppercase;
               color:var(--lavender); margin-top:.5rem; }
.suite p { color:var(--muted); font-size:.96rem; margin-top:.6rem; }

/* ── COLUMNED BLOCKS ──────────────────────────────────────────────────────
   THE PROBLEM, 2026-08-13, in the owner's words: "these last four headings
   are stacked on one another... it feels like a cell phone view in a desktop
   ... a shit ton of empty space ... monochromatic."

   He was right, and it was structural. About and Facility both END with a run
   of `.wrap narrow` text-only sections — philosophy, foster, part-of-our-home,
   CTA — each one a ~60ch column of prose in the middle of a full-width band.
   On a phone that is correct. On a 1400px desktop it is the phone layout with
   half a screen of cream either side, four times in a row. That is the
   "eyebrow / centred heading / paragraph, repeat" tell.

   THE FIX is not decoration, it is showing structure the content already had.
   One device, used everywhere: `gap:1px` over `--hair` with the children on
   `--paper`. Real rules, no borders declared, no cards, no drop shadows.

   `.cols`       two prose blocks of equal weight, side by side, ruled apart.
   `.priorities` a content-derived count — her FOUR stated priorities, set
                 large. Four cells because there are four, not because four
                 fills a row.

   Both collapse to one column below their breakpoint, so the phone keeps the
   single-column reading order it should have. */
.cols { display:grid; gap:1px; background:var(--hair);
        border-block:1px solid var(--hair); margin-top:2.4rem; }
.cols > * { background:var(--paper); padding:clamp(1.5rem,3.5vw,2.4rem) var(--gut); }
/* h2 is kept (each column is a real section of the page, not a sub-point), but
   set at sub-head scale so the two columns read as a pair rather than as two
   competing page headings. */
.cols h2, .cols h3 { font-family:var(--display); font-weight:400;
           font-size:clamp(1.35rem,3.4vw,1.6rem); color:var(--teal-deep);
           line-height:1.15; margin:.35rem 0 .7rem; }
.cols .ticks { margin-top:.9rem; }
.cols .ticks li { font-size:.95rem; }
.cols .lede { font-size:1rem; }
.cols > * > :last-child { margin-bottom:0; }
.tint .cols > * { background:var(--linen); }

.priorities { display:grid; gap:1px; background:var(--hair);
              border-block:1px solid var(--hair); margin:1.8rem 0 2rem; }
.priorities > div { background:var(--paper); padding:1.25rem var(--gut) 1.4rem; }
.tint .priorities > div { background:var(--linen); }
.priorities b { font-family:var(--display); font-weight:400; font-size:1.75rem;
                color:var(--teal-deep); line-height:1.05; display:block; }

/* Prose with its call to action set BESIDE it rather than under it. A 60ch
   column in a 1180px band leaves 500px of nothing to its right, which is the
   same "phone layout on a desktop" complaint one level down. Putting the
   button in that space anchors the row on the left edge the priorities row
   already establishes, and the button stops being an afterthought at the
   bottom of a column. */
.ask { display:grid; gap:clamp(1.2rem,3vw,3rem); }
.ask > div:first-child > :first-child { margin-top:0; }
.ask .actions { margin-top:0; }
@media (min-width:820px){
  .ask { grid-template-columns:minmax(0,60ch) auto; align-items:center; }
}
@media (min-width:620px){ .priorities { grid-template-columns:repeat(2,1fr); } }
@media (min-width:820px){
  .cols { grid-template-columns:1fr 1fr; }
  .priorities { grid-template-columns:repeat(4,1fr); }
}
.fits { list-style:none; padding:0; margin:.9rem 0 0; display:flex; flex-wrap:wrap; gap:.4rem; }
.fits li { font-size:.76rem; color:var(--ink); border:1px solid var(--hair);
           padding:.32rem .65rem; background:var(--paper); }

.note { border-left:2px solid var(--sage); padding:.8rem 0 .8rem 1.1rem; margin-top:1.8rem;
        font-size:.95rem; color:var(--muted); }
.note b { color:var(--ink); font-weight:400; }

/* ── the required trial night — this is a rule, so it gets a frame ──────── */
.rule { border:1px solid var(--hair); background:var(--paper); padding:1.6rem var(--gut); margin-top:2rem; }
.rule .tag { display:inline-block; font-size:.62rem; letter-spacing:.18em; text-transform:uppercase;
             background:var(--teal-deep); color:#fff; padding:.3rem .6rem; margin-bottom:.9rem; }
.rule h3 { font-family:var(--display); font-weight:400; font-size:1.6rem; color:var(--teal-deep); }
.rule p { color:var(--muted); font-size:.96rem; margin-top:.6rem; }
.rule .price { font-family:var(--display); font-size:1.7rem; color:var(--plum); margin-top:.9rem; }

/* ── rates ───────────────────────────────────────────────────────────────── */
.rates { margin-top:1.8rem; }
.rates div { display:flex; justify-content:space-between; gap:1rem; align-items:baseline;
             padding:.85rem 0; border-bottom:1px solid var(--hair); }
.rates dt { font-size:.98rem; }
.rates dd { font-family:var(--display); font-size:1.32rem; color:var(--teal-deep); white-space:nowrap; }

/* ── gallery: a scroll-snap strip on a phone, a grid on a desktop ────────── */
.strip { display:grid; grid-auto-flow:column; grid-auto-columns:78%; gap:.7rem; margin-top:2rem;
         overflow-x:auto; scroll-snap-type:x mandatory; scrollbar-width:none;
         padding-inline:var(--gut); margin-inline:calc(var(--gut) * -1); }
.strip::-webkit-scrollbar { display:none; }
.strip img { scroll-snap-align:center; width:100%; aspect-ratio:1/1; object-fit:cover; }
.swipe { font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
         margin-top:.7rem; }

/* ── inner-page head (every page except Home, which has the photo hero) ──── */
/* These were a narrow column of text and nothing else. On a wide screen that is
   a small block top-left and two-thirds empty white — the owner's words were
   "they feel like they're missing something". They were: a picture. Each head is
   now text beside a photograph, and the padding is tighter so the section below
   is visible without scrolling. */
.page-head { padding-top:clamp(1.8rem,4vw,3rem); padding-bottom:clamp(2rem,5vw,3.4rem); }
.page-head h1 { font-size:clamp(2.2rem,7vw,3.6rem); color:var(--teal-deep); }
.page-head .lede { margin-top:1.2rem; }
.page-head .actions { margin-top:1.6rem; }

/* Two ways to open a page, and the choice is about whether a photograph says
   anything. On About, Trial Stay and Facility it does — the group shot, the dog
   mid-leap, the yard in snow all carry the page's argument. On Boarding, New
   Clients and FAQ it did not: a dog's face beside "here is how to register"
   is filler, and three of them in a row read as padding.
   Those three fill the width with TYPE instead — heading left, the supporting
   line and the call to action right, aligned along the bottom. */
.head-cols { display:grid; gap:.9rem; }
.head-aside .lede { margin-top:0; }
.head-aside .lede + .lede { margin-top:.9rem; }
@media (min-width:900px){
  /* align-items:start, not end. Bottom-aligning looked right on paper and wrong
     on screen: the aside is taller than the heading, so the h1 got pushed down
     and left a band of white above it — the exact problem this was fixing. */
  .head-cols { grid-template-columns:1.15fr .85fr; gap:clamp(2.2rem,6vw,5rem);
               align-items:start; }
  .head-aside { padding-top:.5rem; }
}

.head-split { display:grid; gap:1.6rem; }
.head-text { max-width:38rem; }
.head-photo { margin:0; }
/* was aspect-ratio:4/3, and 5/6 on desktop — 47% of about-head and trial-head
   reached the visitor, 57% of facility-head. Natural, capped. */
.head-photo img { max-height:560px; }
@media (min-width:900px){
  .head-split { grid-template-columns:1.02fr .98fr; gap:clamp(2rem,5vw,4rem);
                align-items:center; }
  .head-photo img { max-height:620px; }
}

/* ── rule boxes: policy / requirement blocks ─────────────────────────────── */
/* gap + background = hairline rules with no borders to keep in sync */
.rulebox { display:grid; gap:1px; background:var(--hair); border:1px solid var(--hair);
           margin-top:2rem; }
.rulebox > div { background:var(--paper); padding:1.3rem var(--gut); }
.rulebox h3 { font-family:var(--display); font-weight:500; font-size:1.22rem;
              color:var(--teal-deep); }
.rulebox p { color:var(--muted); font-size:.96rem; margin-top:.45rem; }
.rulebox.one { max-width:34rem; }
@media (min-width:760px){ .rulebox { grid-template-columns:1fr 1fr; }
  .rulebox.one { grid-template-columns:1fr; } }

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
/* <details> so it works with no JS and is findable by browser search */
.faq { border-top:1px solid var(--hair); }
.faq:last-of-type { border-bottom:1px solid var(--hair); }
.faq summary { cursor:pointer; list-style:none; padding:1.1rem 2.4rem 1.1rem 0;
               position:relative; font-size:1.02rem; color:var(--ink);
               transition:color .2s var(--ease); }
.faq summary::-webkit-details-marker { display:none; }
.faq summary::after { content:''; position:absolute; right:.5rem; top:1.55rem;
  width:9px; height:9px; border-right:1.5px solid var(--lavender);
  border-bottom:1.5px solid var(--lavender); transform:rotate(45deg);
  transition:transform .25s var(--ease); }
.faq[open] summary::after { transform:rotate(-135deg); }
.faq summary:hover { color:var(--teal-deep); }
.faq p { color:var(--muted); font-size:.97rem; padding:0 0 1.2rem; max-width:62ch; }

/* ── footer, now that there is a nav in it ───────────────────────────────── */
.foot-grid { display:grid; gap:1.6rem; padding-bottom:1.4rem; }
.foot-name { font-family:var(--display); font-size:1.35rem; color:var(--teal-deep); }
.site-foot p { font-size:.9rem; color:var(--muted); margin-top:.5rem; }
.foot-hours { font-size:.84rem; }
.site-foot nav { display:grid; gap:.35rem; align-content:start; }
.site-foot nav a { font-size:.9rem; text-decoration:none; }
.site-foot nav a:hover { text-decoration:underline; }
.foot-base { display:grid; gap:1rem; padding-top:1.2rem; border-top:1px solid var(--hair); }
@media (min-width:620px){
  .foot-grid { grid-template-columns:1.4fr 1fr; }
  .foot-base { grid-template-columns:1fr auto; align-items:center; }
}

/* A photo we know is a placeholder. Marked so it is obvious in review which
   images are still waiting on the owner rather than chosen. */
.photo-pending figcaption { color:var(--plum); font-style:italic; }

/* ── reviews ─────────────────────────────────────────────────────────────── */
/* Transcribed rather than screenshotted: a screenshot of a review is an image
   of text — unreadable to search engines and screen readers, unsearchable, and
   it carries someone's phone status bar and the Google chrome with it. */
.quotes { display:grid; gap:1rem; margin-top:2rem; }
.quotes blockquote { margin:0; background:var(--paper); border:1px solid var(--hair);
                     padding:1.4rem var(--gut); break-inside:avoid; }
.quotes p { font-size:.97rem; color:var(--ink); }
.quotes cite { display:block; margin-top:.9rem; font-style:normal; font-size:.78rem;
               letter-spacing:.14em; text-transform:uppercase; color:var(--lavender); }
.quotes cite::before { content:''; display:block; width:22px; height:1px;
                       background:var(--sage); margin-bottom:.7rem; }
@media (min-width:760px){ .quotes { columns:2; column-gap:1rem; gap:0; }
  .quotes blockquote { margin-bottom:1rem; } }
@media (min-width:1100px){ .quotes { columns:3; } }

/* ── gallery ─────────────────────────────────────────────────────────────── */
/* Two-up on a phone deliberately: one-up turns a gallery into a slideshow you
   have to scroll for ever, and these are square crops of a small facility. */
/* THE GALLERY IS A MASONRY, NOT A GRID OF SQUARES.
   This grid is fed remotely from her Book'n Buddy landing gallery
   (live-gallery.js), so the photographs arrive in whatever shape her phone
   took them — portrait, landscape, the occasional panorama. The old
   `aspect-ratio:1/1; object-fit:cover` centre-cropped every one of them to a
   square, which is why a photo could look fine in the app and lose a dog's
   head on the website.

   CSS columns give each picture its own height, so nothing is cropped and
   there are no letterbox gaps either. `break-inside:avoid` keeps a photo from
   being split across two columns. No JS, no library. */
/* `columns:N` is the pre-JavaScript state and the no-JavaScript fallback: it
   renders every photo at the right size immediately, it just leaves the last
   column short. gallery.js then adds .packed and swaps in flex columns it has
   filled shortest-first, which ends them level. See the long note above pack()
   — the short version is that multicol balances HEIGHT in document order, so a
   tall portrait at the wrong moment leaves a photo-sized hole on a phone. */
.gallery-grid { columns:2; column-gap:.6rem; margin-top:2rem; }
.gshot { padding:0; border:0; background:none; cursor:zoom-in; display:block;
         width:100%; break-inside:avoid; margin:0 0 .6rem; overflow:hidden; }
.gshot img { width:100%; height:auto; aspect-ratio:auto; object-fit:contain;
             transition:transform .6s var(--ease), opacity .3s var(--ease); }
.gallery-empty { color:var(--muted); grid-column:1/-1; }
@media (hover:hover) and (pointer:fine){ .gshot:hover img { transform:scale(1.05); } }
@media (min-width:620px){ .gallery-grid{columns:3; column-gap:.8rem} .gshot{margin-bottom:.8rem} }
@media (min-width:900px){ .gallery-grid{columns:4; column-gap:1rem}  .gshot{margin-bottom:1rem} }

/* Packed. `align-items:start` matters: without it the flex columns stretch to
   the tallest one and the hole comes straight back, just drawn differently. */
.gallery-grid.packed { columns:auto; display:flex; align-items:flex-start; gap:.6rem; }
.gallery-grid.packed .gcol { flex:1 1 0; min-width:0; }
@media (min-width:620px){ .gallery-grid.packed{gap:.8rem} }
@media (min-width:900px){ .gallery-grid.packed{gap:1rem} }

/* lightbox */
.glight { position:fixed; inset:0; z-index:200; background:rgba(16,22,24,.94);
          display:grid; place-items:center; padding:3.2rem 1rem; }
.glight img { max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; }
.glight button { position:absolute; background:rgba(255,255,255,.10); color:#fff;
                 border:0; width:52px; height:52px; font-size:1.9rem; line-height:1;
                 cursor:pointer; border-radius:50%; display:grid; place-items:center; }
.glight button:hover { background:rgba(255,255,255,.22); }
.glight .gclose { top:max(1rem,env(safe-area-inset-top)); right:1rem; }
.glight .gprev { left:.6rem; top:50%; transform:translateY(-50%); }
.glight .gnext { right:.6rem; top:50%; transform:translateY(-50%); }
.glight .gcount { position:absolute; bottom:calc(1rem + env(safe-area-inset-bottom));
                  left:0; right:0; text-align:center; color:rgba(255,255,255,.8);
                  font-size:.78rem; letter-spacing:.14em; }

/* hidden photo panel — five taps on the logo */
.gedit { position:fixed; inset:0; z-index:210; background:rgba(16,22,24,.6);
         display:grid; place-items:end center; }
.gedit-card { position:relative; width:100%; max-width:640px; max-height:88dvh;
              overflow-y:auto; background:var(--paper); border-radius:14px 14px 0 0;
              padding:1.6rem var(--gut) calc(1.6rem + env(safe-area-inset-bottom)); }
.gedit-card .gclose { position:absolute; top:.5rem; right:.6rem; width:44px; height:44px;
                      background:none; border:0; font-size:1.8rem; color:var(--ink); cursor:pointer; }
.gedit-card h3 { font-family:var(--display); font-size:1.5rem; color:var(--teal-deep); }
.gedit-note { font-size:.86rem; color:var(--muted); margin:.5rem 0 1.1rem; }
.gedit-note code { font-size:.82rem; background:var(--linen); padding:.1rem .3rem; }
/* A REAL label+input pair. An <input type="file" hidden> clicked from JS is
   silently ignored on iOS Safari — that exact bug shipped once already. */
#gedit-file { position:absolute; width:1px; height:1px; opacity:0; overflow:hidden; }
#gedit-file:focus + label { outline:2px solid var(--lavender); outline-offset:3px; }
/* the photo she is about to replace, so there is no doubt which one it is */
.gswap-now { width:100%; max-height:34dvh; object-fit:cover; margin:.9rem 0 0; }
.gedit-row { display:grid; grid-template-columns:56px 1fr auto; gap:.7rem;
             align-items:center; padding:.6rem 0; border-top:1px solid var(--hair); }
.gedit-row img { width:56px; height:56px; object-fit:cover; }
.gedit-row span { font-size:.82rem; color:var(--muted); }
.gedit-actions { display:flex; flex-wrap:wrap; gap:.5rem; margin-top:1.1rem; }
.gedit-actions .gedit-note { flex:1 1 100%; margin:.4rem 0 0; }
@media (min-width:620px){ .gedit { place-items:center; }
  .gedit-card { border-radius:14px; } }

/* ── chips ───────────────────────────────────────────────────────────────── */
.chips { list-style:none; padding:0; margin:1.6rem 0 0; display:flex; flex-wrap:wrap; gap:.45rem; }
.chips li { font-size:.8rem; border:1px solid var(--hair); padding:.45rem .8rem; background:var(--paper); }

/* ── booking ─────────────────────────────────────────────────────────────── */
/* ── availability embed ──────────────────────────────────────────────────
   This replaced `.embed { display:none }`, whose comment read "a calendar in
   a phone iframe is miserable". That was true of what was being embedded — the
   WHOLE booking app at a fixed 1050px. The card here is built for this job:
   about 480px, sized by its own height message, readable down to 320px. So it
   is shown on the phone rather than hidden. */
.avail-frame { max-width:32.5rem; margin:2.2rem auto 0; }
.avail-frame iframe { width:100%; height:480px; border:0; display:block; }
.avail-key { list-style:none; margin:2.4rem auto 0; max-width:34rem;
             display:grid; gap:1px; background:var(--hair);
             border:1px solid var(--hair); }
.avail-key li { background:var(--paper); padding:.95rem var(--gut);
                display:flex; align-items:flex-start; gap:.85rem;
                font-size:.94rem; color:var(--muted); line-height:1.5; }
.avail-key b { color:var(--ink); font-weight:600; }
/* The swatch repeats the widget's own mark so the key is read against the
   thing it explains, not against a legend invented for the page. */
.avail-key i { flex:0 0 auto; width:20px; height:3px; border-radius:2px;
               margin-top:.55rem; background:var(--muted); }
.avail-key .k-open i { background:#1f8a52; }
.avail-key .k-tight i { background:#b7791f; }
.avail-key .k-full i { background:#c2c9c6; }
.avail-key .k-shut i { height:0; background:none; border-top:1px dashed var(--hair); }
.embed-cta { margin-top:1.8rem; }
.embed-cta .btn { width:100%; }

/* ── visit ───────────────────────────────────────────────────────────────── */
.open { margin-top:1.6rem; }
.open div { display:flex; justify-content:space-between; gap:1rem; padding:.7rem 0;
            border-bottom:1px solid var(--hair); font-size:.95rem; }
.open .shut { color:var(--muted); }
.map { margin-top:1.8rem; border:1px solid var(--hair); }
.map iframe { width:100%; height:280px; border:0; }

/* ── footer ──────────────────────────────────────────────────────────────── */
.site-foot { border-top:1px solid var(--hair); padding:2rem 0 2.4rem; background:var(--linen); }
.site-foot .wrap { display:grid; gap:1.2rem; }
.site-foot small { font-size:.78rem; color:var(--muted); }
.site-foot a { color:var(--ink); }
/* The badge is the one link on this site that helps Book'n Buddy rank.
   Real anchor, rel="noopener", NEVER nofollow. */
.badge img { height:34px; width:auto; }

/* ── sticky phone book bar ───────────────────────────────────────────────── */
/* Not fixed off-canvas; it is on-screen at all times and full width, so it
   cannot create horizontal scroll. body padding-bottom keeps it off the footer. */
.bookbar { position:fixed; left:0; right:0; bottom:0; z-index:70; display:flex; gap:.6rem;
           padding:.6rem var(--gut) calc(.6rem + env(safe-area-inset-bottom));
           background:rgba(255,255,255,.96); border-top:1px solid var(--hair);
           -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px); }
.bookbar .btn { flex:1; min-height:46px; }
.bookbar .call { flex:0 0 46%; }

/* ══ MOTION ══════════════════════════════════════════════════════════════ */
/* Everything here is gated on `.js-anim`, which site.js puts on <html> only when
   the script runs AND the reader has not asked for reduced motion. Without that
   class nothing is hidden and nothing moves — so a JS failure costs the
   animation, never the content. Never write a bare `.reveal { opacity:0 }`.
   Only opacity and transform are animated: they are the two properties the
   compositor can handle without re-laying-out the page on every frame. */

/* TIMING — read this before changing a number, it has been round the loop twice.

   Attempt 1: .65s, 70ms stagger, fired once an element was 12% INTO view. A
   flick outran it and left the screen blank for about a second.
   Attempt 2: over-corrected — .45s and a trigger 220px BEFORE the element
   arrived. Safe, and invisible: everything had finished animating before it was
   ever on screen. "You can barely see it was improved."

   What actually resolves the two: the `.now` escape hatch below. Fast scrolling
   is handled by revealing instantly, NOT by making the animation too quick to
   see. So the animation can be as slow as it wants — anything the reader flicks
   past appears immediately, and anything they arrive at normally plays in full.
   Trigger is back to just inside the viewport (site.js, rootMargin -30px). */
.js-anim .reveal { opacity:0; transform:translateY(26px);
  transition:opacity .75s var(--ease), transform .75s var(--ease);
  transition-delay:calc(var(--i, 0) * 90ms); }
.js-anim .reveal.in { opacity:1; transform:none; }

/* Scrolled clean past it — no entrance, just be there. This is what lets every
   duration above be generous without ever showing a blank screen. */
.js-anim .reveal.now, .js-anim .reveal.now * { transition:none !important; }

/* Photographs WIPE open from the bottom while easing out of a slight over-scale.
   The wipe is what makes it feel deliberate rather than a plain fade — the frame
   arrives, then the picture fills it. clip-path animates on the compositor, so
   this is no more expensive than the fade it replaces.
   The FRAME MUST CLIP: an image scaled past its box with nothing to clip it
   pushes the page wider, which is how a flourish turns into horizontal scroll. */
.js-anim figure.reveal, .js-anim .suite.reveal figure { overflow:hidden; }
.js-anim figure.reveal img,
.js-anim .suite.reveal figure img {
  transform:scale(1.12); clip-path:inset(0 0 100% 0);
  transition:transform 1.9s var(--ease), clip-path 1.25s cubic-bezier(.16,.84,.3,1); }
.js-anim figure.reveal.in img,
.js-anim .suite.reveal.in figure img { transform:none; clip-path:inset(0 0 0 0); }

/* Headings rise out of a clip instead of fading — the single biggest lift in the
   whole motion layer, and it costs one wrapper span that site.js adds.
   The padding/margin pair is load-bearing: without it overflow:hidden shears the
   descenders off a Cormorant 'g' or 'y' while the line is at rest. */
/* A heading gets .reveal so the observer tracks it, but NOT the generic fade —
   the clip-rise is the whole effect and layering a translate on top of it makes
   the words arrive twice. */
.js-anim h1.reveal, .js-anim h2.reveal { opacity:1; transform:none; }
.js-anim h2 .ln, .js-anim h1 .ln { display:block; overflow:hidden;
                                   padding-bottom:.14em; margin-bottom:-.14em; }
.js-anim h2 .ln > span, .js-anim h1 .ln > span {
  display:block; transform:translateY(105%); transition:transform .95s cubic-bezier(.16,.84,.3,1); }
.js-anim h2.in .ln > span, .js-anim h1.in .ln > span { transform:none; }

/* Directional entrances. In a two-column split the words come from the left and
   the photograph from the right, so the pair reads as one movement opening out
   rather than two things bobbing up in parallel. Phones stack, so this is
   desktop-only — sideways travel in a 390px column is just jitter. */
@media (min-width:900px){
  .js-anim .split > *:first-child.reveal { transform:translate(-40px,14px); }
  .js-anim .split > *:last-child.reveal  { transform:translate(40px,14px); }
  .js-anim .split > *.reveal.in { transform:none; }
  .js-anim .suite.reveal figure { transform:translateX(-38px); transition:transform 1.1s var(--ease); }
  .js-anim .suite.reveal.in figure { transform:none; }
}

/* Hero parallax: the photograph drifts slower than the page. site.js writes
   --py; the wrapper owns the load animation so the two never collide. */
.hero .shot { display:block; }
.js-anim .hero .shot { animation:heroSettle 1.6s var(--ease) both; }
/* The scale is what MAKES the parallax possible, not decoration. The image
   exactly fills its box, so translating it would drag a blank strip in behind.
   1.10 leaves roughly 25px of overhang top and bottom on a 500px hero, and
   site.js caps the travel at 22px to stay inside it. Change one, change both. */
.js-anim .hero .shot img { will-change:transform;
                           transform:translate3d(0,var(--py,0px),0) scale(1.10); }

/* Scroll progress. Two pixels under the header — on a page this tall it is the
   only cue for how much is left. */
.progress { position:absolute; left:0; right:0; bottom:-1px; height:2px; background:transparent; }
.progress i { display:block; height:100%; width:100%; background:var(--teal-deep);
              transform:scaleX(var(--p,0)); transform-origin:0 50%; }

/* The hero is the one thing above the fold, so it plays on load, not on scroll:
   the photograph drifts in over a second and a half while the panel rises under
   it. `both` holds the from-state so there is no flash of the end position. */
@keyframes heroSettle { from { transform:scale(1.07); } to { transform:none; } }
@keyframes riseIn { from { opacity:0; transform:translateY(20px); } to { opacity:1; transform:none; } }
.js-anim .hero .say { animation:riseIn .8s var(--ease) .25s both; }

/* Header: the border alone is invisible once a photograph scrolls under it. */
.site-header { transition:box-shadow .3s var(--ease), border-color .3s var(--ease); }
.site-header.scrolled { box-shadow:0 1px 18px rgba(30,58,64,.09); }
.brand img { transition:width .3s var(--ease); }

/* Tabs: the underline grows from the centre instead of appearing. */
.tabs a { position:relative; transition:color .25s var(--ease); }
.tabs a::after { content:''; position:absolute; left:50%; right:50%; bottom:0; height:2px;
                 background:var(--teal-deep); transition:left .28s var(--ease), right .28s var(--ease); }
.tabs a:hover::after, .tabs a.here::after { left:0; right:0; }
.tabs a:hover, .tabs a.here { border-bottom-color:transparent; }

/* Buttons lift slightly. 1px — enough to feel answered, not enough to jump. */
.btn { transition:background .25s var(--ease), color .25s var(--ease),
                  border-color .25s var(--ease), transform .18s var(--ease),
                  box-shadow .25s var(--ease); }
.btn:hover { transform:translateY(-1px); box-shadow:0 6px 16px rgba(30,58,64,.14); }
.btn:active { transform:translateY(0); box-shadow:none; }

/* Counters must not reflow the row as the digits change. */
.facts b { font-variant-numeric:tabular-nums; }

/* Rows and chips answer the pointer quietly. */
.hour, .suite, .rates div { transition:background .3s var(--ease); }
.fits li, .chips li { transition:border-color .25s var(--ease), background .25s var(--ease); }
.rule { transition:box-shadow .3s var(--ease); }

/* Hover effects belong to devices that actually hover. On a touch screen these
   fire on tap and then STICK until you touch something else, which reads as a
   stuck, broken card. */
@media (hover:hover) and (pointer:fine){
  figure { overflow:hidden; }
  figure img { transition:transform .7s var(--ease); }
  figure:hover img { transform:scale(1.035); }
  /* The gallery images are NOT wrapped in figures — they are grid/scroll items,
     so a scale would spill into the gap with nothing to clip it. They get
     opacity instead. */
  .strip img { transition:opacity .3s var(--ease); }
  .strip:hover img { opacity:.72; }
  .strip img:hover { opacity:1; }
  .rates div:hover { background:var(--linen); }
  .fits li:hover, .chips li:hover { border-color:var(--sage); background:var(--linen); }
  .rule:hover { box-shadow:0 10px 34px rgba(30,58,64,.08); }
}

/* prefers-reduced-motion: site.js withholds .js-anim entirely, so the reveal and
   hero animations never exist. This kills the hover and header transitions too. */
@media (prefers-reduced-motion:reduce){
  .btn:hover { transform:none; box-shadow:none; }
  .tabs a::after { transition:none; }
}

/* ══ TABLET ══════════════════════════════════════════════════════════════ */
@media (min-width:620px){
  :root { --gut:2rem; }
  /* was aspect-ratio:16/10 (90% of the frame). The rule across the whole hero
     is now: BELOW 900px the copy is a block under the photograph, so the
     photograph is shown whole; AT 900px and up the copy becomes an overlay on
     it and the band has to be wide enough to sit on. Nothing to force here. */
  .hero .say { padding-bottom:3rem; }
  .facts { grid-template-columns:repeat(4,1fr); }
  .pair { grid-template-columns:1fr 1fr; }
  .actions .btn { flex:0 0 auto; }
  .strip { grid-auto-columns:46%; }
  .site-foot .wrap { grid-template-columns:1fr auto; align-items:center; }
}

/* ══ DESKTOP ═════════════════════════════════════════════════════════════ */
@media (min-width:900px){
  :root { --gut:2.6rem; }
  body { padding-bottom:0; }
  .bookbar { display:none; }
  section[id], .hero[id] { scroll-margin-top:92px; }
  .hbar { padding:.9rem var(--gut); }
  .brand img { width:52px; }
  .scrolled .brand img { width:40px; }
  .brand b { font-size:1.4rem; }
  .tabs { border-top:0; overflow:visible; mask-image:none; -webkit-mask-image:none; }
  .tabs a { padding:.4rem .85rem; font-size:.82rem; border-bottom:0; border-top:2px solid transparent; }
  .tabs a:first-child { padding-left:.85rem; }
  .tabs a:hover, .tabs a.here { border-bottom:0; color:var(--teal-deep); }
  .hbar .btn { display:inline-flex; min-height:40px; padding:.5rem 1.1rem; margin-left:.6rem; }
  /* Desktop keeps the same solid panel, now as a CARD sitting on the photo's
     bottom-left rather than a band beneath it. Free text over the frame was
     measured here too and failed harder than on the phone — headline 1.31:1 —
     because at 21:9 the frame is only ~500px tall and the text block fills a
     third of it, up where any scrim light enough to keep the photo has to be.
     The card is 10.9:1 and leaves most of the picture showing. */
  .hero img { aspect-ratio:21/9; max-height:78vh; }
  .hero .veil { display:block; position:absolute; inset:0;
     background:linear-gradient(180deg,rgba(18,26,28,.10) 0%,rgba(18,26,28,0) 35%,rgba(18,26,28,.34) 100%); }
  .hero .say { position:absolute; left:0; bottom:0; right:auto;
               margin:0 0 clamp(1.6rem,4vh,3rem) var(--gut);
               max-width:min(44rem,58%); background:rgba(30,58,64,.95);
               padding:2rem 2.2rem 2.2rem; }
  .hero h1 { font-size:clamp(2.6rem,4.4vw,3.9rem); }
  .hero p { font-size:1.06rem; }
  .split { display:grid; grid-template-columns:1fr 1fr; gap:clamp(2rem,5vw,4.5rem); align-items:center; }
  .split.wide-text { grid-template-columns:1.05fr .95fr; }
  .wide-shot { margin-top:0; }
  /* was aspect-ratio:3/4 — this single line was the worst offender on the
     site, forcing three 1.78 landscape photographs into a portrait box and
     discarding 58% of each. */
  .strip { grid-auto-flow:row; grid-template-columns:repeat(4,1fr); overflow:visible;
           padding-inline:0; margin-inline:0; gap:1rem; }
  .swipe { display:none; }
  /* align-items was `start`, which pinned two lines of text to the top of a
     560px-tall photograph and left a half-metre of empty cream under it —
     the void in the owner's screenshot of 2026-08-13. The text is the shorter
     column, so it sits opposite the picture, not above the gap. */
  .suite { display:grid; grid-template-columns:.9fr 1.1fr;
           gap:clamp(1.5rem,3vw,3rem); align-items:center; }
  /* On the rates page the price IS the content. Let one thing be big. */
  .suite .size { font-size:2.6rem; }
  .suite .rate { font-size:2.1rem; }
  /* was aspect-ratio:4/3 — the desktop half of the cover-crop. The base rule
     above now shows the whole photograph at every width. */
  .suite figure img { max-height:560px; }
  .rates { columns:2; column-gap:3rem; }
  .rates div { break-inside:avoid; }
  .embed { display:block; margin-top:2rem; border:1px solid var(--hair); }
  .embed iframe { width:100%; height:820px; border:0; }
  .embed-cta { display:none; }
  .visit-grid { display:grid; grid-template-columns:1fr 1fr; gap:clamp(2rem,5vw,4rem); align-items:start; }
  .map { margin-top:0; }
  .map iframe { height:400px; }
}
