/* ==========================================================================
   STEAM AND POOL - design system
   Direction: cinematic dark + warm gold, editorial serif display.
   No framework. No build step. Deployable to any static host.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Surfaces - layered near-blacks, warm-shifted so photos sit naturally */
  --ink:        #0b0a09;
  --ink-2:      #121110;
  --ink-3:      #1a1817;
  --ink-4:      #232120;

  /* Warm gold. --gold for text/lines on dark (7.7:1 on --ink).
     --gold-deep for filled surfaces that carry white text (5.5:1). */
  --gold:       #d3a24e;
  --gold-soft:  #e6c68d;
  --gold-deep:  #a16207;

  /* Type */
  --bone:       #f5f1ea;
  --bone-dim:   #a8a29e;
  --bone-faint: #8b857f;   /* 4.86:1 on the --ink-3 card backgrounds, where it is actually used.
                              #6b6663 measured 3.33:1 and failed body-text contrast. */

  /* Hairlines - never a 1px solid grey */
  --line:       rgba(245, 241, 234, .09);
  --line-strong:rgba(245, 241, 234, .16);
  --gold-line:  rgba(211, 162, 78, .28);

  /* Fonts */
  /* Marcellus is a classical Roman capital - the same lettering family as the
     logo wordmark. It ships in a single weight, so hierarchy comes from size,
     tracking and colour rather than from weight. */
  --display: "Marcellus", "Trajan Pro", "Cormorant Garamond", Georgia, serif;
  --body:    "Onest", ui-sans-serif, system-ui, sans-serif;

  /* Spacious rhythm (density 3/10) */
  --s-1: .5rem;  --s-2: .75rem; --s-3: 1rem;   --s-4: 1.5rem;
  --s-5: 2rem;   --s-6: 3rem;   --s-7: 4rem;   --s-8: 6rem;
  /* Measured 387px of dead space between sections at the old value. This
     keeps the page generous without it reading as unfinished. */
  --section: clamp(3.5rem, 6vw, 7rem);

  /* Concentric radii for the double-bezel */
  --r-out: 2rem;
  --r-in:  calc(2rem - .375rem);
  --r-sm:  .875rem;

  /* Motion - real mass, never linear or ease-in-out */
  --ease:      cubic-bezier(.32, .72, 0, 1);
  --ease-out:  cubic-bezier(.16, 1, .3, 1);
  --ease-soft: cubic-bezier(.4, .1, .2, 1);

  --shell: 1280px;
  --gutter: clamp(1.25rem, 5vw, 4rem);

  /* Layer discipline - no arbitrary z-index anywhere else */
  --z-grain: 90;
  --z-nav: 100;
  --z-overlay: 110;
  /* The nav pill has to sit ABOVE the full-screen menu overlay, because the
     burger inside it is the only way to close that menu. At --z-nav the
     overlay covered it, and on a phone -- no Escape key, no area outside a
     full-screen overlay to tap -- the menu could not be dismissed at all. */
  --z-nav-over-menu: 115;
  --z-lightbox: 120;
  --z-curtain: 130;
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--body);
  font-size: clamp(1rem, .4vw + .9rem, 1.075rem);
  line-height: 1.7;
  font-weight: 400;
  letter-spacing: -.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

img, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; }
::selection { background: var(--gold-deep); color: #fff; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Marcellus: one weight only. Size, tracking and colour carry hierarchy. */
h1, h2, h3, h4, .display {
  font-family: var(--display);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: -.005em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; }

/* --------------------------------------------------------------------------
   3. Film grain - fixed layer only, never attached to scrolling content
   -------------------------------------------------------------------------- */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: .035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.shell--narrow { max-width: 820px; }

/* Optically balanced, not mathematically equal. */
section { padding-block: var(--section) calc(var(--section) * 1.12); position: relative; }
section.flush { padding-block: 0; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .34rem .8rem;
  border: 1px solid var(--gold-line);
  border-radius: 999px;
  color: var(--gold);
  font-size: .625rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  line-height: 1;
}
.eyebrow::before {
  content: "";
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--gold);
}

.section-head { max-width: 44ch; margin-bottom: var(--s-7); }
.section-head .eyebrow { margin-bottom: var(--s-4); }
.section-head h2 { font-size: clamp(2.1rem, 5.4vw, 4.25rem); }
.section-head p {
  color: var(--bone-dim);
  margin-top: var(--s-4);
  max-width: 46ch;
  font-size: 1.0625rem;
}

.rule {
  height: 1px;
  background: linear-gradient(90deg, var(--gold-line), transparent);
  border: 0;
  margin: 0;
}

/* --------------------------------------------------------------------------
   5. Double-bezel (Doppelrand) - cards read as machined hardware
   -------------------------------------------------------------------------- */
.bezel {
  padding: .4375rem;
  border: 1px solid var(--line);
  border-radius: var(--r-out);
  background: linear-gradient(180deg, rgba(245,241,234,.045), rgba(245,241,234,.012));
}
.bezel-in {
  border-radius: var(--r-in);
  background: var(--ink-2);
  box-shadow: inset 0 1px 0 rgba(245,241,234,.07);
  overflow: hidden;
}
.bezel-in--pad { padding: clamp(1.35rem, 3vw, 2.25rem); }

/* --------------------------------------------------------------------------
   6. Buttons - pill, nested trailing icon, physical press
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .875rem;
  padding: .6rem .6rem .6rem 1.5rem;
  border-radius: 999px;
  font-size: .9375rem;
  font-weight: 600;
  letter-spacing: -.01em;
  cursor: pointer;
  /* transform is deliberately absent: GSAP writes an inline transform on
     [data-magnet] buttons every pointermove, and a CSS transition on the same
     property makes the magnet trail the cursor instead of tracking it. */
  transition: background-color .18s var(--ease),
              border-color .18s var(--ease),
              color .18s var(--ease);
}
/* Magnet buttons get their press from GSAP (see site.js) - an inline transform
   beats this rule, so scope it to the buttons GSAP does not touch. */
.btn:not([data-magnet]):active { transform: scale(.98); }

.btn__ico {
  width: 2rem; height: 2rem;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 999px;
  transition: transform .24s var(--ease-out), background-color .18s var(--ease);
}
.btn:hover .btn__ico { transform: translate(3px, -2px) scale(1.06); }
.btn__ico svg { width: 15px; height: 15px; }

.btn--gold { background: var(--gold-deep); color: #fff; }
.btn--gold .btn__ico { background: rgba(255,255,255,.18); }
.btn--gold:hover { background: #b06d09; }

.btn--ghost {
  border: 1px solid var(--line-strong);
  color: var(--bone);
  padding-left: 1.5rem;
}
.btn--ghost .btn__ico { background: rgba(245,241,234,.08); }
.btn--ghost:hover { border-color: var(--gold-line); color: var(--gold-soft); }

/* Underline link that wipes in from the left */
.link-wipe {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--gold);
  font-size: .9375rem;
  font-weight: 600;
  position: relative;
  padding-bottom: 3px;
}
.link-wipe::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 1px; width: 100%;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .26s var(--ease-out);
}
.link-wipe:hover::after { transform: scaleX(1); }
.link-wipe svg { width: 14px; height: 14px; transition: transform .5s var(--ease-out); }
.link-wipe:hover svg { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   7. Floating island nav
   -------------------------------------------------------------------------- */
.nav {
  position: fixed;
  top: clamp(.85rem, 2vw, 1.5rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-nav-over-menu);
  width: max-content;
  max-width: calc(100% - 2 * var(--gutter));
  display: flex;
  align-items: center;
  gap: clamp(.75rem, 2vw, 1.75rem);
  padding: .5rem .5rem .5rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(11, 10, 9, .58);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: 0 24px 60px -24px rgba(0,0,0,.9), inset 0 1px 0 rgba(245,241,234,.06);
  transition: transform .7s var(--ease), background-color .7s var(--ease);
}
.nav.is-hidden { transform: translate(-50%, calc(-100% - 2rem)); }

.nav__brand {
  font-family: var(--display);
  font-size: .9375rem;
  letter-spacing: .01em;
  white-space: nowrap;
}
.nav__brand span { color: var(--gold); }

.nav__links { display: flex; gap: clamp(.75rem, 1.6vw, 1.5rem); }
.nav__links a {
  font-size: .8125rem;
  font-weight: 500;
  color: var(--bone-dim);
  letter-spacing: .02em;
  transition: color .16s var(--ease);
  white-space: nowrap;
}
.nav__links a:hover, .nav__links a[aria-current="page"] { color: var(--bone); }

.nav .btn { padding: .5rem .5rem .5rem 1.1rem; font-size: .8125rem; }
.nav .btn__ico { width: 1.75rem; height: 1.75rem; }

/* Hamburger - lines physically rotate into an X */
.burger {
  width: 2.5rem; height: 2.5rem;
  border-radius: 999px;
  display: none;
  place-items: center;
  position: relative;
  cursor: pointer;
  background: rgba(245,241,234,.06);
  flex: none;
}
.burger i {
  position: absolute;
  left: 50%; top: 50%;
  width: 15px; height: 1.5px;
  background: var(--bone);
  border-radius: 2px;
  transition: transform .6s var(--ease), opacity .3s var(--ease);
}
.burger i:nth-child(1) { transform: translate(-50%, -5px); }
.burger i:nth-child(2) { transform: translate(-50%, 0); }
.burger i:nth-child(3) { transform: translate(-50%, 5px); }
.burger[aria-expanded="true"] i:nth-child(1) { transform: translate(-50%, 0) rotate(45deg); }
.burger[aria-expanded="true"] i:nth-child(2) { opacity: 0; transform: translate(-50%,0) scaleX(.2); }
.burger[aria-expanded="true"] i:nth-child(3) { transform: translate(-50%, 0) rotate(-45deg); }

@media (max-width: 940px) {
  .nav__links, .nav > .btn { display: none; }
  .burger { display: grid; }
  .nav { padding-left: 1.25rem; }
}

/* Full-screen menu with staggered mask reveal */
.menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: rgba(11,10,9,.92);
  -webkit-backdrop-filter: blur(28px);
  backdrop-filter: blur(28px);
  -webkit-backdrop-filter: blur(28px);
  display: grid;
  align-content: center;
  padding: 6rem var(--gutter) 3rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity .6s var(--ease), visibility .6s var(--ease);
}
.menu.is-open { opacity: 1; visibility: visible; }

.menu__list { display: grid; gap: .35rem; }
.menu__item { overflow: hidden; }
.menu__item a {
  display: block;
  font-family: var(--display);
  font-size: clamp(2.25rem, 11vw, 4.25rem);
  line-height: 1.05;
  letter-spacing: -.03em;
  transform: translateY(105%);
  transition: transform .8s var(--ease-out), color .4s var(--ease);
}
.menu.is-open .menu__item a { transform: translateY(0); }
.menu__item:nth-child(1) a { transition-delay: .06s; }
.menu__item:nth-child(2) a { transition-delay: .11s; }
.menu__item:nth-child(3) a { transition-delay: .16s; }
.menu__item:nth-child(4) a { transition-delay: .21s; }
.menu__item:nth-child(5) a { transition-delay: .26s; }
.menu__item a:hover { color: var(--gold); }

/* On a phone 11vw put "Experiences" at 41px, filling the width edge to edge and
   turning the menu into five slabs of type. 8.5vw lands it near 32px at 375px:
   still the display face, still the dominant thing on screen, but it reads as a
   menu rather than a headline. Tracking eases off because -.03em is tuned for
   the large desktop size and looks cramped smaller. Desktop is unchanged.
   padding-block keeps each row past the 44px tap-target floor now that the line
   box alone is under it. */
@media (max-width: 700px) {
  .menu__list { gap: .55rem; }
  .menu__item a {
    font-size: clamp(1.75rem, 8.5vw, 2.5rem);
    line-height: 1.14;
    letter-spacing: -.015em;
    padding-block: .3rem;
  }
}

.menu__foot {
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  color: var(--bone-dim);
  font-size: .875rem;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .7s var(--ease) .3s, transform .7s var(--ease-out) .3s;
}
.menu.is-open .menu__foot { opacity: 1; transform: none; }
.menu__foot a { color: var(--gold); font-weight: 600; }

/* --------------------------------------------------------------------------
   8. Page-load curtain
   -------------------------------------------------------------------------- */
.curtain {
  position: fixed;
  inset: 0;
  z-index: var(--z-curtain);
  background: var(--ink);
  display: grid;
  place-items: center;
  pointer-events: none;
}
.curtain__mark {
  font-family: var(--display);
  font-size: .8125rem;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--gold);
}
.curtain.is-gone { display: none; }

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100vh;   /* fallback: dvh/svh need Safari 15.4+ */
  min-height: 100dvh;
  display: grid;
  align-items: end;
  overflow: hidden;   /* fallback: clip needs Safari 16+ */
  overflow: clip;
  padding-block: 0;
}
.hero__stage { position: absolute; inset: 0; overflow: hidden; }
.hero__layer {
  position: absolute;
  inset: -8% 0 -8% 0;      /* overscan so parallax never reveals an edge */
  opacity: 0;
  transition: opacity 1.6s var(--ease-soft);
}
.hero__layer.is-on { opacity: 1; }
.hero__layer img { width: 100%; height: 100%; object-fit: cover; }

.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(11,10,9,.72) 0%, rgba(11,10,9,.22) 34%, rgba(11,10,9,.9) 100%),
    radial-gradient(120% 80% at 50% 100%, rgba(11,10,9,.75), transparent 70%);
}

.hero__body {
  position: relative;
  padding-bottom: clamp(2.5rem, 7vw, 5rem);
  padding-top: 9rem;
}
.hero__title {
  font-size: clamp(2.5rem, 8.2vw, 7rem);
  line-height: 1.0;
  margin-block: var(--s-4) var(--s-5);
  max-width: 18ch;
}
.hero__title em { font-style: normal; color: var(--gold-soft); }
.hero__lead {
  color: rgba(245,241,234,.86);
  max-width: 42ch;
  font-size: clamp(1rem, .5vw + .9rem, 1.1875rem);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  margin-top: var(--s-5);
  color: var(--bone-dim);
  font-size: .8125rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 500;
}
.hero__meta span { display: inline-flex; align-items: center; gap: .5rem; }
.hero__meta svg { width: 16px; height: 16px; color: var(--gold); flex: none; }

/* On a phone the four amenities overflow the gutter as a wrapping flex row;
   a fixed two-column grid keeps them inside it. The extra bottom padding on
   the hero body lifts them clear of the fixed WhatsApp/call dock. */
@media (max-width: 560px) {
  .hero__meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .65rem 1rem;
    font-size: .6875rem;
    letter-spacing: .06em;
  }
  .hero__meta svg { width: 14px; height: 14px; }
  .hero__body { padding-bottom: 8rem; }
  .phead__body { padding-bottom: 5rem; }
}

/* Split-word reveal: each word rides up out of its own mask.
   Two constraints, both learned the hard way:
   - the mask must be inline-block, not block, or every word takes its own line;
   - an inline-block taller than its line box overflows and gets clipped, so the
     mask carries no extra padding and the heading supplies enough leading to
     contain the glyphs instead. */
.split-line {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.split-word { display: inline-block; }

/* Split headings need leading >= the glyph height, or ascenders and descenders
   are cut off by the masks. */
[data-split] { line-height: 1.08; }

.scroll-cue {
  position: absolute;
  left: var(--gutter);
  bottom: clamp(2.5rem, 7vw, 5rem);
  display: grid;
  gap: .75rem;
  justify-items: center;
  color: var(--bone-faint);
  font-size: .625rem;
  letter-spacing: .25em;
  text-transform: uppercase;
}
.scroll-cue i {
  width: 1px; height: 56px;
  background: linear-gradient(180deg, var(--gold), transparent);
  transform-origin: top;
  animation: cue 2.4s var(--ease-soft) infinite;
}
@keyframes cue { 0%,100% { transform: scaleY(.25); opacity: .4 } 50% { transform: scaleY(1); opacity: 1 } }
@media (max-width: 700px) { .scroll-cue { display: none; } }

/* --------------------------------------------------------------------------
   10. Booking bar
   -------------------------------------------------------------------------- */
.booking { margin-top: calc(var(--section) * -.42); position: relative; }
.booking__grid {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(min(9.5rem, 100%), 1fr));
  align-items: end;
}
.booking__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-5);
}
.booking__head h2 { font-size: clamp(1.35rem, 2.6vw, 1.85rem); }
.booking__head p { color: var(--bone-dim); font-size: .875rem; }

.field label {
  display: block;
  font-size: .82rem;   /* was .625rem = 10px, too small on any screen */
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone-faint);
  margin-bottom: .5rem;
}
.field input, .field select {
  width: 100%;
  padding: .8rem .9rem;
  font: inherit;
  font-size: .9375rem;
  color: var(--bone);
  background: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color .4s var(--ease), background-color .4s var(--ease);
  min-height: 44px;
}
.field input:hover, .field select:hover { border-color: var(--line-strong); }
.field input:focus, .field select:focus { border-color: var(--gold-line); outline: none; background: var(--ink-4); }
.field select { appearance: none; cursor: pointer; }
.field--select { position: relative; }
.field--select::after {
  content: "";
  position: absolute;
  right: 1rem; bottom: 1.15rem;
  width: 7px; height: 7px;
  border-right: 1.25px solid var(--gold);
  border-bottom: 1.25px solid var(--gold);
  transform: rotate(45deg);
  pointer-events: none;
}
/* Force the native date picker icon to be visible on a dark field */
input[type="date"] { position: relative; }
input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1) sepia(1) saturate(4) hue-rotate(3deg);
  opacity: .7; cursor: pointer;
}

.booking__submit { display: grid; }
.booking__submit .btn { justify-content: center; }
.booking__err {
  grid-column: 1 / -1;
  color: #f0a5a5;
  font-size: .875rem;
  display: none;
  margin-top: var(--s-2);
}
.booking__err.is-on { display: block; }
.booking__note {
  margin-top: var(--s-4);
  color: var(--bone-faint);
  font-size: .85rem;   /* was .75rem = 12px; this carries the Zotel disclosure
                          and the lifeguard warning, so it has to be readable */
  display: flex;
  align-items: center;
  gap: .5rem;
}
.booking__note svg { width: 13px; height: 13px; color: var(--gold); flex: none; }

/* --------------------------------------------------------------------------
   11. Marquee
   -------------------------------------------------------------------------- */
.marquee {
  overflow: hidden;
  border-block: 1px solid var(--gold-line);
  padding-block: clamp(1rem, 2vw, 1.5rem);
  display: flex;
  gap: 0;
  background: linear-gradient(180deg, rgba(211,162,78,.05), transparent 60%);
}
.marquee__track {
  display: flex;
  flex: none;
  /* No container gap and no trailing padding on purpose. -50% only lands on
     the seam if the two duplicated halves are EXACTLY equal width, and a
     shared `gap` leaves half a gap unaccounted for. Spacing lives on each
     item instead, so width = n items and half = n/2 items exactly. */
}
.marquee__track > span { margin-right: 2.25rem; }
.marquee__track span {
  font-family: var(--display);
  font-size: clamp(1.05rem, 1.8vw, 1.6rem);
  /* --bone-faint on near-black was effectively invisible. The fix is contrast,
     not decoration: a solid warm bone at ~8:1. */
  color: var(--bone-dim);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 3.5rem;
  letter-spacing: -.015em;
}
.marquee__track span::after {
  content: "";
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--gold);
  flex: none;
}
.marquee:hover .marquee__track { animation-play-state: paused; }

/* --------------------------------------------------------------------------
   12. Editorial split (story)
   -------------------------------------------------------------------------- */
.split {
  display: grid;
  grid-template-columns: 1.18fr .82fr;
  gap: clamp(2rem, 6vw, 6rem);
  /* Stretch, not start. The media column used to run 250px taller than the
     copy, and the difference showed as dead black space under the text. Now
     both columns are exactly the same height by construction. */
  align-items: stretch;
}

.split__media img { width: 100%; height: 100%; object-fit: cover; }
.split { min-height: 28rem; }

/* A second plate, offset rather than overlapped. The previous version was
   absolutely positioned, rotated and hanging off the corner of the first
   image, which read as pasted on rather than composed. */
/* Two frames of EQUAL height filling the column, rather than a big plate with
   a small one tacked under it. The undersized second image was the thing that
   looked cheap, and it was also what made the column overshoot. */
.split__media {
  display: grid;
  grid-template-rows: auto auto;
  gap: clamp(.6rem, 1.4vw, 1rem);
  /* No min-height: the copy column sets the row height and the media stretches
     to fill it, so the second image ends exactly level with the last line of
     text. A min-height here made the media overshoot the copy. */
  align-self: stretch;
  min-height: 0;
}
.split__media > .bezel { display: grid; min-height: 0; }
.split__inset { width: 100%; justify-self: stretch; }
.split__media .bezel-in,
.split__inset .bezel-in { aspect-ratio: 16 / 11; height: auto; min-height: 0; }
.split__media img { width: 100%; height: 100%; object-fit: cover; }
.split { min-height: 28rem; }
/* The column stretches to match the media, so the last block is pushed to the
   bottom edge. Without this the copy stopped ~40px above the second image and
   the two columns visibly failed to line up. */
/* The copy distributes to the media column's height so its last block lands
   level with the bottom of the second image. space-between rather than an auto
   margin: auto put the slack above the first child instead of below the last. */
.split__copy {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s-3);
}
.split__copy p { color: var(--bone-dim); }
.split__copy p + p { margin-top: var(--s-4); }
.split__copy .lead {
  color: var(--bone);
  font-size: clamp(1.0625rem, .7vw + .95rem, 1.35rem);
  line-height: 1.55;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(8.5rem, 100%), 1fr));
  gap: var(--s-3);
  margin-top: var(--s-6);
}
.stat { padding: 1.1rem 1.2rem; }
.stat b {
  display: block;
  font-variant-numeric: tabular-nums;
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 400;
  color: var(--gold-soft);
  line-height: 1;
  margin-bottom: .4rem;
}
/* Direct child only - a bare `.stat span` also caught the animated counter
   span nested inside `.stat b` and shrank it to label size. */
.stat > span { font-size: .8125rem; color: var(--bone-dim); line-height: 1.4; display: block; }
.stat b span { font: inherit; color: inherit; }

/* With an odd number of stats the last one would sit alone in a ragged row;
   letting it span the full width reads as intentional instead. */
.stats > .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }

@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; }
  .split__media { min-height: 0; grid-template-rows: auto auto; }
  .split__media .bezel-in, .split__inset .bezel-in { aspect-ratio: 16 / 10; height: auto; }
  .split__inset { width: 100%; }
}

/* --------------------------------------------------------------------------
   13. Pinned scrub chapter
   -------------------------------------------------------------------------- */
.chapter { padding-block: 0; }
/* Exactly one viewport tall. Was pinned; the pin added 55vh of scroll on top
   of this height to deliver one headline, which is why the section outlived
   its own content. Class name kept so the three pages using it need no edit. */
.chapter__pin {
  min-height: 100vh;   /* fallback: dvh/svh need Safari 15.4+ */
  min-height: 100dvh;
  display: grid;
  place-items: center;
  position: relative;
  overflow: hidden;   /* fallback: clip needs Safari 16+ */
  overflow: clip;
  text-align: center;
}
.chapter__bg { position: absolute; inset: -10% 0; }
.chapter__bg img { width: 100%; height: 100%; object-fit: cover; }
.chapter__bg::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(80% 70% at 50% 50%, rgba(11,10,9,.55), rgba(11,10,9,.9));
}
/* 24ch against a 6rem headline put one word on each line. Wider measure and
   a smaller ceiling let the line actually breathe. */
.chapter__copy { position: relative; padding-inline: var(--gutter); max-width: 46ch; margin-inline: auto; }
.chapter__copy h2 { font-size: clamp(1.9rem, 5vw, 4rem); }
.chapter__copy h2 em {
  font-style: normal;
  color: var(--gold-soft);
  letter-spacing: .015em;
}
.chapter__copy p { color: rgba(245,241,234,.86); margin-top: var(--s-4); max-width: 48ch; margin-inline: auto; font-size: 1.0625rem; }

/* --------------------------------------------------------------------------
   14. Facilities
   -------------------------------------------------------------------------- */
/* Categorised list, not a grid of identical tiles. Twelve equal cards in three
   columns is the single most recognisable AI layout; a hairline-ruled list
   reads as editorial and mirrors how the property actually groups amenities. */
.amenities { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); gap: 0 clamp(2rem, 6vw, 5rem); }
.amenity-group { padding-block: clamp(1.35rem, 3vw, 2rem); border-top: 1px solid var(--line); }
.amenity-group:first-child, .amenities > :nth-child(2) { border-top-color: var(--gold-line); }
.amenity-group h3 {
  font-family: var(--body);
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: var(--s-4);
}
.amenity-group ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.amenity-group li {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: .875rem;
  align-items: baseline;
  font-size: 1rem;
}
.amenity-group svg { width: 18px; height: 18px; color: var(--gold); align-self: start; }
.amenity-group em {
  display: block;
  font-style: normal;
  color: var(--bone-faint);
  font-size: .8125rem;
  margin-top: .1rem;
}

/* --------------------------------------------------------------------------
   15. Horizontal gallery rail
   -------------------------------------------------------------------------- */
.rail { padding-block: var(--section) 0; overflow: hidden;   /* fallback: clip needs Safari 16+ */
 overflow: clip; }
.rail__viewport { overflow: hidden; }
.rail__track {
  display: flex;
  gap: clamp(.75rem, 1.6vw, 1.25rem);
  padding-inline: var(--gutter);
}
.rail__item {
  flex: none;
  width: clamp(18rem, 40vw, 32rem);
  cursor: pointer;
  padding: 0;
  text-align: left;
}
/* One landscape shape at every width. Every photograph in this rail is
   landscape (1.33 to 1.79), so a portrait box crops the subject out rather than
   framing it. Measured loss averaged over the eight photographs: 4:5 = 46%,
   1:1 = 33%, 4:3 = 11%, 3:2 = 8%. Three of the eight now show their full frame.
   This used to alternate 4:5 / 4:3 for rhythm, which cost 46% on every odd
   card; re-measure before reintroducing that. */
.rail__item .bezel-in { aspect-ratio: 3 / 2; }
.rail-hint-touch { display: none; }
.rail__item img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.1s var(--ease-out), filter .8s var(--ease);
  filter: saturate(.9) brightness(.92);
}
.rail__item:hover img { transform: scale(1.06); filter: saturate(1.05) brightness(1); }
.rail__cap {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: .875rem;
  font-size: .75rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bone-faint);
}
.rail__cap b { color: var(--gold); font-weight: 600; }
.rail__hint {
  display: flex; align-items: center; gap: .6rem;
  color: var(--bone-faint); font-size: .75rem;
  letter-spacing: .18em; text-transform: uppercase;
  margin-top: var(--s-5);
}
.rail__hint svg { width: 22px; height: 10px; color: var(--gold); }

/* Fallback when JS-driven horizontal scrub is off (mobile / reduced motion) */
.rail--native .rail__viewport {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.rail--native .rail__viewport::-webkit-scrollbar { display: none; }
.rail--native .rail__item { scroll-snap-align: center; }

/* --------------------------------------------------------------------------
   16. Bento grid
   -------------------------------------------------------------------------- */
.bento {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(.75rem, 1.4vw, 1.1rem);
  grid-auto-rows: minmax(11rem, auto);
}
.bento > * { grid-column: span 4; }
.bento .b-8 { grid-column: span 8; }
.bento .b-6 { grid-column: span 6; }
.bento .b-r2 { grid-row: span 2; }
.bento figure { margin: 0; height: 100%; }
.bento .bezel-in { height: 100%; }
.bento img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.2s var(--ease-out); }
.bento button:hover img { transform: scale(1.05); }
.bento button { padding: 0; cursor: pointer; height: 100%; width: 100%; }

@media (max-width: 860px) {
  .bento { grid-template-columns: 1fr; grid-auto-rows: auto; gap: var(--s-3); }
  .bento > *, .bento .b-8, .bento .b-6 { grid-column: span 1; }
  .bento .b-r2 { grid-row: span 1; }
  .bento .bezel-in { aspect-ratio: 4 / 3; height: auto; }
}

/* --------------------------------------------------------------------------
   17. Room card
   -------------------------------------------------------------------------- */
.room { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center; }
.room__media .bezel-in { aspect-ratio: 3 / 2; }
.room__media img { width: 100%; height: 100%; object-fit: cover; }
.room__title { font-size: clamp(1.85rem, 4.2vw, 3rem); }
.room__price {
  display: flex; align-items: baseline; gap: .5rem;
  margin-top: var(--s-4); color: var(--bone-dim); font-size: .875rem;
}
.room__feats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr)); gap: .6rem 1.25rem; margin-block: var(--s-5); }
.room__feats li { display: flex; align-items: center; gap: .6rem; font-size: .875rem; color: var(--bone-dim); }
.room__feats svg { width: 15px; height: 15px; color: var(--gold); flex: none; }
.room__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }
ul.room__feats { list-style: none; padding: 0; margin-block: var(--s-5); }
@media (max-width: 860px) { .room { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   18. Nearby / cards
   -------------------------------------------------------------------------- */
/* Asymmetric on purpose: a wide lead card then narrower ones, rather than
   three identical columns. */
.cards { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--s-3); }
.cards > * { grid-column: span 2; }
.cards > :nth-child(1) { grid-column: span 4; }
.cards > :nth-child(4) { grid-column: span 4; }
.cards > :nth-child(4) .card__img { aspect-ratio: 21 / 9; }
.cards > :nth-child(1) .card__img { aspect-ratio: 21 / 9; }
@media (max-width: 900px) {
  .cards { grid-template-columns: 1fr 1fr; }
  .cards > *, .cards > :nth-child(1), .cards > :nth-child(4) { grid-column: span 1; }
  .cards > :nth-child(1) .card__img, .cards > :nth-child(4) .card__img { aspect-ratio: 16 / 10; }
}
@media (max-width: 560px) { .cards { grid-template-columns: 1fr; } }

/* Card CTAs sit on one line regardless of how long the copy above them is. */
.card .bezel-in { display: grid; grid-template-rows: auto 1fr; }
.card__body { display: grid; align-content: start; }
.card__dist { align-self: end; }
.card { overflow: hidden; }
.card .bezel-in { display: grid; }
.card__img { aspect-ratio: 16 / 10; overflow: hidden; }
.card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease-out); }
.card:hover .card__img img { transform: scale(1.06); }
.card__body { padding: 1.35rem 1.4rem 1.5rem; }
.card__body h3 { font-size: 1.2rem; letter-spacing: 0; }
.card__body p { color: var(--bone-faint); font-size: .875rem; margin-top: .5rem; }
.card__dist {
  font-variant-numeric: tabular-nums;
  display: inline-flex; align-items: center; gap: .4rem;
  margin-top: var(--s-3); color: var(--gold);
  font-size: .6875rem; letter-spacing: .16em; text-transform: uppercase; font-weight: 600;
}

/* --------------------------------------------------------------------------
   19. FAQ - a two-column list, not an accordion. Everything is visible, so
   nobody has to click eight times to read eight short answers.
   -------------------------------------------------------------------------- */
.qa { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); gap: 0 clamp(2rem, 6vw, 4.5rem); }
.qa__item { padding-block: clamp(1.25rem, 2.6vw, 1.75rem); border-top: 1px solid var(--line); }
.qa > :nth-child(1), .qa > :nth-child(2) { border-top-color: var(--gold-line); }
.qa__q {
  font-family: var(--display);
  font-size: 1.0625rem;
  line-height: 1.35;
  margin-bottom: .5rem;
}
.qa__a { color: var(--bone-dim); font-size: .9375rem; max-width: 46ch; }
.qa__a a { color: var(--gold-soft); }

/* --------------------------------------------------------------------------
   20. Contact
   -------------------------------------------------------------------------- */
.contact { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.5rem, 4vw, 3rem); }
@media (max-width: 860px) { .contact { grid-template-columns: 1fr; } }
.contact__rows { display: grid; gap: var(--s-4); }
.crow { display: grid; gap: .3rem; }
.crow dt {
  font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--bone-faint); font-weight: 600;
}
.crow dd { margin: 0; font-size: 1.0625rem; }
.crow dd a { color: var(--gold-soft); }
.contact__map { min-height: 22rem; }
/* No invert. Inverting the tiles also inverts Google's logo and the required
   attribution, which their terms do not allow you to alter, and it made the
   roads read as dark-on-dark anyway. The bezel frame does the styling work
   instead; a light desaturation is as far as this should go. */
.contact__map iframe {
  width: 100%; height: 100%; min-height: 22rem;
  border: 0;
  filter: saturate(.85) brightness(.95);
}

/* --------------------------------------------------------------------------
   21. CTA band
   -------------------------------------------------------------------------- */
.cta { position: relative; overflow: hidden;   /* fallback: clip needs Safari 16+ */
 overflow: clip; text-align: center; }
.cta__bg { position: absolute; inset: -12% 0; }
.cta__bg img { width: 100%; height: 100%; object-fit: cover; }
.cta__bg::after { content: ""; position: absolute; inset: 0; background: rgba(11,10,9,.78); }
.cta__inner { position: relative; display: grid; justify-items: center; gap: var(--s-5); }
.cta h2 { font-size: clamp(2.1rem, 7vw, 5rem); max-width: 20ch; }
.cta p { color: var(--bone-dim); max-width: 44ch; }

/* --------------------------------------------------------------------------
   22. Footer
   -------------------------------------------------------------------------- */
.foot { padding-block: var(--s-8) var(--s-5); border-top: 1px solid var(--line); }
.foot__grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, minmax(0, 1fr));
  gap: var(--s-6) var(--s-5);
}
@media (max-width: 860px) { .foot__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .foot__grid { grid-template-columns: 1fr; } }
.foot__mark { font-family: var(--display); font-size: 1.5rem; letter-spacing: .04em; }
.foot__mark span { color: var(--gold); }
.foot p { color: var(--bone-faint); font-size: .875rem; margin-top: var(--s-3); max-width: 32ch; }
.foot h3 {
  font-family: var(--body); font-size: .625rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--bone-faint);
  margin-bottom: var(--s-3);
}
.foot ul { list-style: none; padding: 0; margin: 0; display: grid; gap: .55rem; }
.foot li a, .foot address a { font-size: .9375rem; color: var(--bone-dim); transition: color .4s var(--ease); }
.foot li a:hover, .foot address a:hover { color: var(--gold-soft); }
.foot address { font-style: normal; font-size: .9375rem; color: var(--bone-dim); line-height: 1.6; }
/* The legal links measured 17px tall -- under half the 44px minimum for a
   reliable tap. Padding grows the hit area, the negative margin keeps the row
   looking exactly as it did. */
.foot__bottom a {
  display: inline-block;
  padding: 13px 6px;
  margin-block: -13px;
}

.foot__bottom {
  margin-top: var(--s-7); padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-3);
  color: var(--bone-faint); font-size: .75rem;
}
.foot__bottom a { color: var(--gold); }

/* --------------------------------------------------------------------------
   23. Floating contact dock
   -------------------------------------------------------------------------- */
.dock {
  position: fixed;
  right: var(--gutter);
  bottom: clamp(1rem, 3vw, 1.75rem);
  z-index: var(--z-nav);
  display: grid;
  gap: .7rem;
}
.dock a {
  width: 3.6rem; height: 3.6rem;
  border-radius: 999px;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong);
  background: rgba(11,10,9,.72);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--bone);
  transition: transform .24s var(--ease-out), border-color .2s var(--ease), color .2s var(--ease);
}
.dock a:hover { transform: translateY(-3px) scale(1.04); border-color: var(--gold-line); color: var(--gold); }
.dock svg { width: 25px; height: 25px; }
.dock a.is-wa:hover { color: #4ade80; border-color: rgba(74,222,128,.4); }

/* --------------------------------------------------------------------------
   24. Lightbox
   -------------------------------------------------------------------------- */
.lb {
  position: fixed; inset: 0; z-index: var(--z-lightbox);
  background: rgba(6,5,5,.96);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  display: grid; place-items: center;
  padding: clamp(1rem, 5vw, 4rem);
  opacity: 0; visibility: hidden;
  transition: opacity .5s var(--ease), visibility .5s var(--ease);
}
.lb.is-open { opacity: 1; visibility: visible; }
.lb__img {
  max-width: 100%; max-height: 78vh;
  object-fit: contain;
  border-radius: var(--r-sm);
  transform: scale(.965);
  transition: transform .7s var(--ease-out), opacity .35s var(--ease);
}
.lb.is-open .lb__img { transform: scale(1); }
.lb__bar {
  position: absolute; left: 0; right: 0; bottom: clamp(1rem, 4vw, 2.5rem);
  display: flex; align-items: center; justify-content: center; gap: var(--s-4);
  color: var(--bone-dim); font-size: .75rem; letter-spacing: .16em;
}
.lb__btn {
  width: 2.75rem; height: 2.75rem; border-radius: 999px;
  border: 1px solid var(--line-strong);
  display: grid; place-items: center; cursor: pointer;
  transition: border-color .2s var(--ease), transform .2s var(--ease-out), color .2s var(--ease);
}
.lb__btn:hover { border-color: var(--gold-line); color: var(--gold); transform: scale(1.06); }
.lb__btn svg { width: 16px; height: 16px; }
.lb__close { position: absolute; top: clamp(1rem, 4vw, 2.25rem); right: clamp(1rem, 4vw, 2.25rem); }

/* --------------------------------------------------------------------------
   25. Page header (inner pages)
   -------------------------------------------------------------------------- */
.phead {
  position: relative;
  min-height: 68vh;   /* fallback: dvh/svh need Safari 15.4+ */
  min-height: 68svh;
  display: grid;
  align-items: end;
  overflow: hidden;   /* fallback: clip needs Safari 16+ */
  overflow: clip;
  padding-block: 0;
}
.phead__bg { position: absolute; inset: -8% 0; }
/* Was an inline style on contact.html, which no media query could override. */
.phead--short { min-height: 56vh; min-height: 56svh; }
.phead__bg img { width: 100%; height: 100%; object-fit: cover; }
.phead__bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(11,10,9,.8), rgba(11,10,9,.35) 45%, rgba(11,10,9,.95));
}
.phead__body { position: relative; padding-block: 9rem clamp(2.5rem, 6vw, 4rem); }
.phead__body h1 { font-size: clamp(2.25rem, 7.4vw, 5.5rem); margin-block: var(--s-4) var(--s-4); max-width: 20ch; }
.phead__body p { color: rgba(245,241,234,.82); max-width: 48ch; }

/* Sits over photography, so --bone-faint is not readable here. */
.crumbs {
  display: flex; gap: .6rem; align-items: center;
  color: rgba(245, 241, 234, .74);
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
}
.crumbs span:last-child { color: var(--gold); }
.crumbs a:hover { color: var(--gold); }

/* A small subhead between the landmark cards and the practical list below
   them. Sized between h2 and body so it reads as a divider inside the section
   rather than a new section of its own. */
.near__more {
  margin: var(--s-6) 0 var(--s-4);
  font-family: var(--display);
  font-size: clamp(1.15rem, 2.4vw, 1.5rem);
  font-weight: 400;
  letter-spacing: -.01em;
  color: var(--bone);
}

/* --------------------------------------------------------------------------
   26. Scroll reveal - JS toggles .is-in
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(2.5rem);
  filter: blur(6px);
  transition: opacity .9s var(--ease), transform .9s var(--ease-out), filter .9s var(--ease);
}
[data-reveal].is-in { opacity: 1; transform: none; filter: blur(0); }
[data-reveal-delay="1"] { transition-delay: .08s; }
[data-reveal-delay="2"] { transition-delay: .16s; }
[data-reveal-delay="3"] { transition-delay: .24s; }
[data-reveal-delay="4"] { transition-delay: .32s; }

/* --------------------------------------------------------------------------
   27. Reduced motion - strip everything kinetic, keep everything legible
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; filter: none !important; }
  .menu__item a { transform: none !important; }
  .split-word { transform: none !important; }
  .hero__layer { opacity: 1; }
  .hero__layer ~ .hero__layer { display: none; }
  .scroll-cue i { animation: none; transform: scaleY(1); }
}

/* ==========================================================================
   28. Advanced motion layer
   Everything here is transform / opacity / clip-path only, gated on both
   prefers-reduced-motion and pointer type.
   ========================================================================== */

/* Lenis owns scrolling when it loads; native smooth scroll would fight it. */
html.has-lenis { scroll-behavior: auto; }
html.has-lenis body { overscroll-behavior-y: none; }

/* --- Nav: condenses once the page has moved ------------------------------- */
/* No padding change here: it is a layout property, so it would either snap
   (untransitioned) or thrash layout (transitioned). Colour and shadow carry
   the state change on their own. */
.nav.is-compact {
  background: rgba(11, 10, 9, .74);
  box-shadow: 0 20px 50px -26px rgba(0, 0, 0, .95), inset 0 1px 0 rgba(245, 241, 234, .08);
}
.nav__logo {
  height: 30px; width: auto;
  transform-origin: left center;
  transition: transform .7s var(--ease);   /* scale, never height */
}
.nav.is-compact .nav__logo { transform: scale(.84); }

/* --- Clip-path reveal ---------------------------------------------------- */

/* Visible by default. JS adds .is-clipped only to frames below the fold, then
   removes it to play the reveal. A CSS transition cannot get stuck part-way
   the way an abandoned tween can - an earlier GSAP fromTo left frames pinned
   at inset(100%) permanently, which hid photographs outright. */
[data-clip] {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1.1s var(--ease-out);
}
[data-clip].is-clipped { clip-path: inset(0 0 100% 0); }
[data-clip] img {
  transform-origin: center;
  transition: transform 1.3s var(--ease-out);
}
[data-clip].is-clipped img { transform: scale(1.2); }

/* --- Cursor ring (desktop fine pointers only) ---------------------------- */
/* Two elements on purpose. GSAP writes `transform` on the outer ring to
   follow the pointer, so the grow-on-hover has to live on a child - and it
   scales rather than animating width/height/margin, which would thrash
   layout on every pointer move. */
.cursor-ring {
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  pointer-events: none;
  z-index: var(--z-lightbox);
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.cursor-ring.is-on { opacity: 1; }
.cursor-ring__dot {
  position: absolute;
  top: 0; left: 0;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid var(--gold-line);
  border-radius: 999px;
  transform: scale(.76);
  transition: transform .5s var(--ease-out),
              background-color .5s var(--ease),
              border-color .5s var(--ease);
}
.cursor-ring.is-big .cursor-ring__dot {
  transform: scale(1.7);
  background: rgba(211, 162, 78, .1);
  border-color: rgba(211, 162, 78, .5);
}
@media (hover: none), (pointer: coarse) { .cursor-ring { display: none; } }

/* --- Cursor-tracked image drift ------------------------------------------ */
[data-tilt] { overflow: hidden; }
[data-tilt] img { transform: scale(1.04); }

/* --- Sticky stacking cards ---------------------------------------------- */
/* Plain block flow, NOT grid. A sticky item's containing block is its grid
   area, so in a grid each card would stick only within its own row and never
   stack over the one before it. */
.stack { position: relative; }
.stack__card {
  /* Opaque base. The outer bezel ring is a translucent gradient, so without
     this the card stacked behind shows through the 7px border while pinned. */
  background: var(--ink);
  border-radius: var(--r-out);
  position: sticky;
  top: clamp(4.5rem, 11vh, 7rem);
  margin-bottom: clamp(1.25rem, 4vh, 2.5rem);
  transform-origin: center top;
}
.stack__card:last-child { margin-bottom: 0; }
@media (max-width: 860px) {
  .stack__card {
    position: static; top: auto; will-change: auto;
    margin-bottom: var(--s-4);
  }
}

/* A short sticky card leaves the rest of the viewport empty while it is
   pinned, which is what read as a large black gap. The card now fills most of
   the screen and the image stretches with it. */
.stack__card > .bezel-in {
  min-height: min(70vh, 640px);
  display: grid;
  align-content: center;
}
.stack__inner {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: stretch;
}
.stack__inner > div:first-child {
  display: grid;
  align-content: center;
}
.stack__inner .bezel { display: grid; }
.stack__inner .bezel-in {
  aspect-ratio: auto;
  min-height: min(52vh, 460px);
}
.stack__inner img { width: 100%; height: 100%; object-fit: cover; }
.stack__num {
  font-family: var(--display);
  font-size: .8125rem;
  letter-spacing: .22em;
  color: var(--gold);
  display: block;
  margin-bottom: var(--s-3);
}
.stack__card h3 { font-size: clamp(1.6rem, 3.6vw, 2.6rem); }
.stack__card p { color: var(--bone-dim); margin-top: var(--s-4); }
@media (max-width: 860px) {
  .stack__inner { grid-template-columns: 1fr; align-items: start; }
  .stack__card > .bezel-in { min-height: 0; }
  .stack__inner .bezel-in { min-height: 0; aspect-ratio: 16 / 10; }
}

/* --- Marquee now driven by GSAP (scroll velocity), not a CSS keyframe ---- */


/* --- Line-reveal paragraphs --------------------------------------------- */
[data-lines] { overflow: hidden; }

/* --- Reduced motion: strip the new layer too ---------------------------- */
@media (prefers-reduced-motion: reduce) {
  .cursor-ring { display: none; }
  [data-clip], [data-clip].is-clipped { clip-path: none !important; transition: none; }
  [data-clip] img { transform: none !important; }
  [data-tilt] img { transform: none; }
  .stack__card { position: static; transform: none !important; opacity: 1 !important; }
  .marquee__track { transform: none !important; }
  .nav__logo { transition: none; }
}

/* ==========================================================================
   29. Logo, quick-bar, editorial type scale
   ========================================================================== */

/* The logo now carries real alpha (keyed from the supplied artwork), so it
   needs no blend mode and sits correctly on any background. */
.nav__brand { display: flex; align-items: center; }
.nav__logo,
.foot__logo {
  width: auto;
  transform-origin: left center;
}
.nav__logo {
  height: 52px;
  transition: transform .7s var(--ease);
}
.nav.is-compact .nav__logo { transform: scale(.82); }
.foot__logo { height: 108px; margin-bottom: var(--s-4); }

@media (max-width: 560px) {
  .nav__logo { height: 42px; }
  .foot__logo { height: 88px; }
}

/* --- Slim booking bar, arrives once the hero is behind you --------------- */
.quickbar {
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  z-index: var(--z-nav);
  padding-block: .7rem;
  background: rgba(11, 10, 9, .82);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border-top: 1px solid var(--line);
  transform: translateY(105%);
  transition: transform .8s var(--ease);
  visibility: hidden;
}
.quickbar.is-on { transform: translateY(0); visibility: visible; }
.quickbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}
.quickbar__label {
  font-size: .8125rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bone-dim);
}
/* The contact icons live in the bar on a phone and in the floating dock on
   larger screens -- never both. */
.quickbar__ico { display: none; }

/* On a phone the floating dock is removed altogether. It is a fixed circle in
   the bottom-right, so at some scroll position it always ends up sitting on
   top of body copy -- it covered the hero facts and mid-paragraph text at
   375px. Its two actions move into the bar instead, which occupies a strip
   nothing else competes for. */
@media (max-width: 700px) {
  .quickbar { padding-bottom: max(.7rem, env(safe-area-inset-bottom, 0px)); }
  .quickbar__label { display: none; }
  .quickbar__inner { justify-content: stretch; gap: .55rem; }
  .quickbar .btn { flex: 1 1 auto; min-width: 0; justify-content: center; }

  .dock { display: none; }

  .quickbar__ico {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 3rem; height: 3rem;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    background: rgba(11, 10, 9, .72);
    color: var(--bone);
    transition: border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease-out);
  }
  .quickbar__ico svg { width: 21px; height: 21px; }
  .quickbar__ico:active { transform: scale(.94); }
}

/* The booking label never wraps: a two-line button in a fixed bar makes the
   bar grow and eat the page. */
.quickbar .btn { white-space: nowrap; }
.qb-tight { display: none; }

/* Below 380px three controls on one line squeeze the label, so the icons
   shrink and the label drops to its short form rather than wrapping. Same
   destination, same tap target. */
@media (max-width: 380px) {
  .quickbar__inner { gap: .5rem; }        /* 8px: the minimum gap between adjacent tap targets */
  .quickbar__ico { width: 2.75rem; height: 2.75rem; }   /* 44px: the tap-target floor, not below it */
  .quickbar__ico svg { width: 19px; height: 19px; }
  .quickbar .btn { padding-left: 1.1rem; font-size: .9375rem; }
  .quickbar .btn__ico { width: 2rem; height: 2rem; }
  .quickbar .qb-wide { display: none; }
  .quickbar .qb-tight { display: inline; }
}

/* --- Larger primary buttons for the hero and closing CTA ---------------- */
.btn--lg {
  padding: .8rem .8rem .8rem 1.9rem;
  font-size: 1rem;
}
.btn--lg .btn__ico { width: 2.35rem; height: 2.35rem; }
.btn--lg .btn__ico svg { width: 17px; height: 17px; }

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}
@media (max-width: 560px) {
  .hero__cta { gap: .6rem; }
  .hero__cta .btn { flex: 1 1 100%; justify-content: center; }
}

/* --- Editorial headline, one step bigger than a section head ------------ */
.h-editorial {
  font-size: clamp(2.1rem, 5.6vw, 4rem);
  margin-block: var(--s-4) var(--s-4);
}

/* --- Mobile image scaling ----------------------------------------------- */
/* Portrait hero art is taller than it is wide, so the stage has to give it
   room rather than cropping the pool out of frame. */
/* Portrait only. A landscape phone is still under 860px wide, and there the
   stacked 3:4 hero came out 1105px tall on a 390px-tall screen - 2.83 times
   the viewport, so the visitor scrolled three screens of photograph before
   reaching the headline. Rotated phones keep the full-bleed treatment, which
   suits a wide short screen: a landscape clip in a landscape window barely
   crops at all. */
@media (max-width: 860px) and (orientation: portrait) {
  /* THE WHOLE PICTURE, NOT A SLICE OF IT.
     A 3:4 portrait frame inside a 390x844 window has to lose 38% of its width
     to fill the screen, and no amount of object-position fixes that - there is
     simply more picture than window. So on a phone the hero stops being a
     full-screen backdrop: the media gets its own block at the artwork's true
     3:4 ratio, and the words sit underneath it on the dark ground.

     Nothing is cropped, nothing is letterboxed, nothing is enlarged. The
     desktop hero is untouched, because there a 16:9-ish window and a landscape
     clip already agree. */
  .hero {
    min-height: 0;
    height: auto;
    display: block;
    overflow: visible;
  }
  .hero__stage {
    position: relative;
    inset: auto;
    width: 100%;
    aspect-ratio: 3 / 4;          /* the clip is 1080x1440, the still 896x1200 */
  }
  /* The overscan exists so desktop parallax never reveals an edge. There is no
     parallax here, and overscan is just more cropping. */
  .hero__layer { inset: 0; }
  .hero__layer img { object-position: center center; }

  /* The scrim was there to keep white text legible over the picture. The text
     is no longer over the picture, so it only has to soften the bottom edge
     where the image meets the copy. */
  .hero__scrim {
    background: linear-gradient(180deg, rgba(11,10,9,.28) 0%, rgba(11,10,9,0) 30%, rgba(11,10,9,.85) 100%);
  }
  .hero__body {
    padding-top: clamp(1.5rem, 5vw, 2.25rem);
    padding-bottom: clamp(2rem, 7vw, 3rem);
  }
  /* Same treatment for the page headers. Their photographs are landscape 3:2,
     and a 0.59-ratio banner was cutting 61% of each one. The picture now gets a
     3:2 block of its own with the title underneath. */
  .phead,
  .phead--short {
    min-height: 0;
    display: block;
    overflow: visible;
  }
  .phead__bg {
    position: relative;
    inset: auto;
    width: 100%;
    aspect-ratio: 3 / 2;
  }

  .phead__bg::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 7rem;
    background: linear-gradient(180deg, rgba(11,10,9,.62), transparent);
    pointer-events: none;
  }
  .phead__body {
    padding-top: clamp(1.25rem, 4vw, 2rem);
    padding-bottom: clamp(1.5rem, 5vw, 2.5rem);
  }

  /* The nav floats over the top of the image, so the eyebrow needs to clear it. */
  .hero__stage::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 7rem;
    background: linear-gradient(180deg, rgba(11,10,9,.62), transparent);
    pointer-events: none;
  }

  .rail__item { width: min(78vw, 20rem); }

  /* One aspect ratio per card below 860px. The alternating 4:5 / 4:3 reads as
     rhythm on a wide screen where several cards are in view at once, but a
     phone shows barely more than one card, so a full-height card next to a
     half-height one just looks unfinished. */
  /* The scroll-linked horizontal pan is deliberately off on touch (it fights
     the finger), so the rail is a swipe carousel here and the hint has to say
     so. */
  .rail-hint-wide { display: none; }
  .rail-hint-touch { display: inline; }
  .room__media .bezel-in { aspect-ratio: 4 / 3; }
  .chapter__pin { min-height: 78vh;   /* fallback: dvh/svh need Safari 15.4+ */
 min-height: 78svh; }
}
@media (max-width: 560px) {
  .hero__lead { font-size: 1rem; }
  /* minmax(0,1fr) not 1fr: a bare 1fr cannot shrink below the date input's
     own min-content width, which forced 360px of columns into a 221px card. */
  .booking__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .booking__grid .field, .booking__grid input, .booking__grid select { min-width: 0; }
  .booking__submit { grid-column: 1 / -1; }
  .facilities { grid-template-columns: 1fr; }
  .stat b { font-size: 1.3rem; }
}

@media (prefers-reduced-motion: reduce) {
  .quickbar { transition: none; }
  .nav__logo { transition: none; }
}

/* Disclosure note under generated/visualised imagery */
.img-note {
  margin-top: var(--s-5);
  max-width: 62ch;
  font-size: .8125rem;
  line-height: 1.6;
  color: var(--bone-faint);
  padding-left: 1rem;
  border-left: 1px solid var(--gold-line);
}

/* ==========================================================================
   30. Hero video (Remotion-rendered loop)
   Layered over the still, which remains the poster and the fallback.
   ========================================================================== */
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.2s var(--ease);
  pointer-events: none;
}
.hero__video.is-on { opacity: 1; }

/* Reduced motion, or no JS: the still carries the hero on its own. */
@media (prefers-reduced-motion: reduce) {
  .hero__video { display: none; }
}

/* Skip link - keyboard users should not have to tab the whole nav. */
.skip {
  position: absolute;
  left: var(--gutter);
  top: -4rem;
  z-index: var(--z-curtain);
  padding: .7rem 1.2rem;
  border-radius: 999px;
  background: var(--gold-deep);
  color: #fff;
  font-size: .875rem;
  font-weight: 600;
  transition: top .2s var(--ease-out);
}
.skip:focus { top: var(--gutter); }

/* Hero secondary action is a text link, not a second button. One filled button
   plus one ghost button is the default AI hero pairing. */
.hero__alt { color: var(--bone); font-size: 1rem; }
.hero__alt:hover { color: var(--gold-soft); }
@media (max-width: 560px) { .hero__cta .hero__alt { flex: 1 1 100%; justify-content: center; } }

/* Composition variety: three stacked ritual cards all reading text-left /
   image-right made the sequence feel like one repeated template. The middle
   card mirrors, so the eye alternates down the stack. Grid order is flipped
   rather than using direction:rtl, which would also reverse the text. */
.stack__card:nth-child(even) .stack__inner > div:first-child { order: 2; }
.stack__card:nth-child(even) .stack__inner > .bezel { order: 1; }
@media (max-width: 860px) {
  .stack__card:nth-child(even) .stack__inner > div:first-child,
  .stack__card:nth-child(even) .stack__inner > .bezel { order: 0; }
}

/* #room previously repeated #story's media-left anchor. Flip it so the two
   editorial blocks do not read as the same layout twice. */
.room { grid-template-columns: .85fr 1.15fr; }
.room__media { order: 2; }
@media (max-width: 860px) {
  .room { grid-template-columns: 1fr; }
  .room__media { order: 0; }
}

/* ==========================================================================
   31. Mobile legibility and touch targets
   Audited at 375px: 19 elements were rendering at 10–11px and several
   controls were under the 44px minimum touch size.
   ========================================================================== */
@media (max-width: 700px) {
  /* Uppercase micro-labels are the worst offenders - small size plus wide
     tracking plus low contrast. Raise the floor and tighten the tracking. */
  .eyebrow          { font-size: .72rem; letter-spacing: .14em; padding: .42rem .9rem; }
  .amenity-group h3 { font-size: .72rem; letter-spacing: .14em; }
  .crumbs           { font-size: .78rem; letter-spacing: .07em; }
  .hero__meta       { font-size: .78rem; letter-spacing: .05em; }
  .rail__cap        { font-size: .78rem; letter-spacing: .07em; }
  .rail__hint       { font-size: .78rem; letter-spacing: .1em; }
  .field label      { font-size: .82rem; letter-spacing: .1em; }
  .crow dt          { font-size: .72rem; letter-spacing: .1em; }
  .foot h3          { font-size: .72rem; letter-spacing: .14em; }
  .stack__num       { font-size: .82rem; letter-spacing: .14em; }
  .card__dist       { font-size: .78rem; letter-spacing: .08em; }
  .quickbar__label  { font-size: .78rem; }
  .foot__bottom     { font-size: .82rem; }
  .amenity-group em { font-size: .875rem; }
  .booking__note    { font-size: .82rem; }
  .img-note         { font-size: .875rem; }

  /* 44px is the minimum reliable touch target. */
  .burger { width: 2.75rem; height: 2.75rem; }
  .menu__item a { min-height: 48px; display: flex; align-items: center; }
  .menu__foot a, .foot li a, .foot address a {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  .qa__q { font-size: 1.0625rem; }

  /* Standalone text links act as buttons here, so they need button-sized
     hit areas even though they render as text. */
  .link-wipe { min-height: 44px; align-items: center; }
  .nav__brand { min-height: 44px; }
}

/* ==========================================================================
   32. Facility row - restored
   I removed this when the homepage's 12-tile three-column grid was replaced
   by the categorised amenity list, but room.html and contact.html still use
   it for their 4-item rows. Unstyled, the icon rendered at full size and the
   label ran into the description. Four items in a row was never the generic
   pattern; twelve identical tiles was.
   ========================================================================== */
.facilities {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: var(--s-3);
}
.facility {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: .9rem;
  align-items: start;
  padding: 1.25rem 1.35rem;
  transition: border-color .24s var(--ease);
}
.facility:hover.bezel { border-color: var(--gold-line); }
.facility svg {
  width: 20px; height: 20px;
  color: var(--gold);
  margin-top: .15rem;
}
.facility b {
  display: block;
  font-weight: 600;
  font-size: .9375rem;
  line-height: 1.3;
  margin-bottom: .2rem;
}
.facility span {
  display: block;
  color: var(--bone-faint);
  font-size: .8125rem;
  line-height: 1.45;
}
@media (max-width: 700px) {
  .facilities { grid-template-columns: 1fr; }
  .facility span { font-size: .875rem; }
}

/* <picture> is inline by default. Every picture here wraps a cover image
   inside an already-sized frame, so it must be a block that fills its parent -
   otherwise the inline baseline adds a few px of gap under each photo and any
   height:100% on the img resolves against nothing. */
picture { display: block; height: 100%; }

/* Clip reveal variants. The default wipes upward; these two alternate with it
   so a page full of framed photographs does not reveal identically 48 times. */
[data-clip].is-clipped.clip-l { clip-path: inset(0 100% 0 0); }   /* wipe in from the left */
[data-clip].is-clipped.clip-s { clip-path: inset(6% 6% 6% 6%); }  /* opens outward from centre */
[data-clip].is-clipped.clip-s img { transform: scale(1.1); }

@media (prefers-reduced-motion: reduce) {
  [data-clip].clip-l, [data-clip].clip-s { clip-path: none !important; }
}

/* ==========================================================================
   33. Static full-bleed band
   Same visual language as the pinned chapter but without the pin. Three
   pinned sections on one page was the same device three times; this gives the
   experiences page a second rhythm.
   ========================================================================== */
.band {
  position: relative;
  overflow: hidden;   /* fallback: clip needs Safari 16+ */
  overflow: clip;
  min-height: 58vh;   /* fallback: dvh/svh need Safari 15.4+ */
  min-height: 58svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding-block: 0;
}
.band__inner {
  position: relative;
  width: 100%;
  min-height: 58vh;   /* fallback: dvh/svh need Safari 15.4+ */
  min-height: 58svh;
  display: grid;
  place-items: center;
}
.band__bg { position: absolute; inset: -10% 0; }
.band__bg img { width: 100%; height: 100%; object-fit: cover; }
.band__bg::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(80% 70% at 50% 50%, rgba(11,10,9,.5), rgba(11,10,9,.88));
}
.band__copy {
  position: relative;
  padding-inline: var(--gutter);
  max-width: 46ch;
}
.band__copy h2 { font-size: clamp(1.75rem, 4.4vw, 3.25rem); }
.band__copy h2 em {
  font-style: italic;
  color: var(--gold-soft);
}
.band__copy p {
  color: rgba(245,241,234,.86);
  margin-top: var(--s-4);
  max-width: 46ch;
  margin-inline: auto;
}
@media (max-width: 700px) {
  .band, .band__inner { min-height: 48vh;   /* fallback: dvh/svh need Safari 15.4+ */
 min-height: 48svh; }
}

/* ==========================================================================
   34. Reviews - one anchor quote plus a ruled list
   Deliberately not a three-card carousel with dots: that is the pattern the
   design audit flags, and a carousel hides most of the proof behind a click.
   ========================================================================== */
.quote {
  margin: 0 0 var(--s-7);
  max-width: 34ch;
  position: relative;
  padding-left: clamp(2.5rem, 5vw, 4rem);
}
.quote__mark {
  position: absolute;
  left: 0; top: .35rem;
  width: 26px; height: 26px;
  color: var(--gold);
}
.quote blockquote { margin: 0; }
.quote blockquote p {
  font-family: var(--display);
  font-size: clamp(1.35rem, 2.6vw, 2rem);
  line-height: 1.35;
  color: var(--bone);
  max-width: 34ch;
}
.quote figcaption { margin-top: var(--s-4); display: grid; gap: .2rem; }
.quote__who { font-weight: 600; font-size: .9375rem; color: var(--bone); }
.quote__src {
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.qlist {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 0 clamp(2rem, 5vw, 4rem);
}
.qlist__item {
  padding-block: clamp(1.1rem, 2.4vw, 1.6rem);
  border-top: 1px solid var(--line);
  display: grid;
  gap: .45rem;
}
.qlist > :nth-child(1), .qlist > :nth-child(2) { border-top-color: var(--gold-line); }
.qlist__item p { color: var(--bone-dim); font-size: .9375rem; max-width: 42ch; }

/* The anchor quote sits beside the list on wide screens rather than above it,
   so the section does not read as one more stacked block. */
@media (min-width: 1100px) {
  #reviews .shell { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(2rem, 5vw, 5rem); align-items: start; }
  #reviews .section-head { grid-column: 1; margin-bottom: var(--s-5); }
  .quote { grid-column: 1; margin-bottom: 0; }
  .qlist { grid-column: 2; grid-row: 1 / span 3; grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   26. FAQ accordions
   These shipped with no CSS at all: the browser's default disclosure triangle
   was showing, the custom <i> indicator had no dimensions so it rendered
   nothing, summary had no pointer cursor, and neither summary nor answer had
   padding, so text sat against the bezel edge. A functional defect on two
   pages, not a polish item.

   Frequency: occasional (read once while deciding whether to book), so a
   standard-duration animation is the right budget. Entering, so ease-out.
   -------------------------------------------------------------------------- */
.faq { display: grid; gap: var(--s-4); }

details.bezel { padding: 0; overflow: hidden; }

details summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: clamp(1rem, 2.2vw, 1.4rem) clamp(1.1rem, 2.4vw, 1.6rem);
  cursor: pointer;
  font-family: var(--display);
  font-size: clamp(1rem, 1.6vw, 1.125rem);
  line-height: 1.35;
  color: var(--bone);
  /* Kill the native triangle in every engine. */
  list-style: none;
  transition: color .18s var(--ease);
}
details summary::-webkit-details-marker { display: none; }
details summary::marker { content: ""; }
details summary:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: -2px;
  border-radius: var(--r-sm);
}

/* The indicator: a plus that becomes a minus. Only the vertical bar is
   transformed, so this stays on the compositor and reads instantly. */
details summary i {
  position: relative;
  flex: none;
  width: 11px;
  height: 11px;
}
details summary i::before,
details summary i::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  background: var(--gold);
}
details summary i::before { height: 1.5px; }          /* horizontal, always */
details summary i::after {
  width: 1.5px;                                        /* vertical, collapses */
  transition: transform .24s var(--ease-out);
}
details[open] summary i::after { transform: scaleY(0); }

.faq__a {
  padding: 0 clamp(1.1rem, 2.4vw, 1.6rem) clamp(1.1rem, 2.4vw, 1.5rem);
  color: var(--bone-dim);
  font-size: .9375rem;
  line-height: 1.65;
  max-width: 62ch;
  opacity: 0;
  transform: translateY(-5px);
  transition: opacity .24s var(--ease-soft), transform .24s var(--ease-out);
}
details[open] .faq__a { opacity: 1; transform: none; }

@media (hover: hover) and (pointer: fine) {
  details summary:hover { color: var(--gold-soft); }
}

/* The box height still snaps, because smoothing it means animating a layout
   property. Where the browser supports it, do it properly instead. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
  details.bezel::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size .28s var(--ease-out), content-visibility .28s allow-discrete;
  }
  details.bezel[open]::details-content { block-size: auto; }
}

/* --------------------------------------------------------------------------
   27. Press feedback
   .btn already scales on :active. These are equally pressable and had only
   hover states, so on a touch device they gave no confirmation at all that the
   tap registered. Declared after the hover rules so :active wins.
   -------------------------------------------------------------------------- */
.rail__item, .lb__btn, .dock a {
  transition: transform .14s var(--ease-out), border-color .18s var(--ease), color .18s var(--ease);
}
.rail__item:active { transform: scale(.985); }
.lb__btn:active    { transform: scale(.94); }
.dock a:active     { transform: translateY(-1px) scale(.96); }

/* --------------------------------------------------------------------------
   28. Enquiry success
   Rare, and the one genuinely high-emotion moment on the site, so this is
   where the delight budget belongs. Previously the fields blanked instantly
   via form.reset() with only a line of text to explain it.
   -------------------------------------------------------------------------- */
.enquiry__fields {
  transition: opacity .32s var(--ease-soft), transform .32s var(--ease-out);
}
form.is-sent .enquiry__fields {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
}
#enquiry-status { transition: opacity .3s var(--ease-soft), transform .3s var(--ease-out); }
form.is-sent #enquiry-status {
  font-size: 1.0625rem;
  color: var(--gold-soft);
}

@media (prefers-reduced-motion: reduce) {
  /* Gentler, not zero: keep the fades that explain the state change, drop
     every positional move. */
  .faq__a, details[open] .faq__a,
  .enquiry__fields, form.is-sent .enquiry__fields { transform: none !important; }
  details summary i::after { transition-duration: .001ms; }
}

/* --------------------------------------------------------------------------
   29. Touch: neutralise hover MOVEMENT only
   A touch device fires :hover on tap and keeps it until the next tap
   elsewhere, so a tapped gallery tile stayed zoomed and a tapped dock button
   stayed lifted. Colour and border changes are harmless and still read as
   feedback; it is the movement that gets stuck. The :active rules above are
   what give touch its press confirmation.
   -------------------------------------------------------------------------- */
@media (hover: none), (pointer: coarse) {
  .btn:hover .btn__ico,
  .link-wipe:hover svg,
  .rail__item:hover img,
  .bento button:hover img,
  .card:hover .card__img img,
  .dock a:hover,
  .lb__btn:hover { transform: none; }
  .link-wipe:hover::after { transform: scaleX(0); }
}

/* --------------------------------------------------------------------------
   30. Very narrow phones (320-400px)
   Two date fields side by side leave about 150px each, which is not enough for
   a date picker. One column below 400px.
   -------------------------------------------------------------------------- */
@media (max-width: 400px) {
  .booking__grid { grid-template-columns: 1fr; }
  .booking__submit { grid-column: 1; }
}

/* --------------------------------------------------------------------------
   31. Touch target and label sizing on phones
   Measured across 320-414px: the breadcrumb link was 33x21, the two phone
   number links on the contact page were 134x21, and the enquiry form labels
   rendered at 11.5px. WCAG exempts links inline in a sentence, but these are
   standalone controls -- and the phone links are the primary action on the
   page. Padding with matching negative margin grows the hit area without
   moving anything visually.
   -------------------------------------------------------------------------- */
/* Every standalone link that a finger has to find. Measured at 320-768px:
   footer nav links 20px tall, .link-wipe call-to-actions 29px, the mobile
   menu's contact links 24px, breadcrumbs 21px. iPad portrait counts as touch
   too, so this is not phone-only. Links genuinely inside a sentence are left
   alone -- WCAG exempts those, and padding them would break the line box. */
/* Applied unconditionally rather than behind a touch query. (pointer: coarse)
   cannot be emulated by resizing, so it was impossible to verify, and a
   1024px laptop with a mouse still left these at 22px -- under the 24px
   minimum. The padding is invisible either way, so there is no reason to gate
   it. */
.crumbs a,
.contact__rows a,
.foot a,
.menu__foot a,
.nav__links a,
dd a[href^="tel:"],
dd a[href*="wa.me"] {
  display: inline-block;
  padding-block: 12px;
  margin-block: -12px;
}

/* .link-wipe is deliberately NOT in the group above. It is inline-flex so its
   label and trailing arrow stay on one line; inline-block dropped the arrow
   onto a second line at narrow widths ("See the rooftop" in the hero). It
   still gets the same padding, which is all the group was for. */
.link-wipe {
  display: inline-flex;
  padding-block: 12px;
  margin-block: -12px;
}

@media (max-width: 860px) {

  /* .72rem is 11.52px -- the value it already had, so that changed nothing.
     .82rem clears 13px, which is the floor for comfortable reading while
     filling in a form on a phone. Tracking eased off so the wider glyphs at
     the larger size still fit on one line. */
  .field label { font-size: .82rem; letter-spacing: .1em; }
}

/* .booking__note carries the Zotel disclosure and the lifeguard warning, so it
   has to be readable. It rendered at 12px on tablets. */
@media (max-width: 1100px) {
  .booking__note { font-size: .85rem; }
}


/* --------------------------------------------------------------------------
   32. Phone: one shape for every photograph tile
   --------------------------------------------------------------------------
   This block must stay at the END of the stylesheet. The base tile rules
   (.rail__item, .bento, .card__img, .room__media) live from line ~860 onward
   and the .band / .chapter definitions at ~1870; media queries carry no extra
   specificity, so anything placed above them is simply overridden.

   On a wide screen the bento's mixed shapes read as composition. On a 390px
   screen only one tile is really in view at a time, so four different box
   shapes (0.81, 0.82, 1.33 and 1.50 were all in play) just look inconsistent,
   and the portrait boxes were cutting up to 45% off a landscape photograph.

   Every photograph in the gallery, the rail, the bento and the section media is
   landscape, 1.33 to 1.79. Measured average loss for a single uniform box:

       3:2  6% average, 16% worst   <- in use
       7:5  7% average, 22% worst
       4:3  8% average, 26% worst

   So 3:2 is both the most consistent and the least destructive. Desktop keeps
   its composition untouched.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) and (orientation: portrait) {
  .bento .bezel-in,
  .rail__item .bezel-in,
  .room__media .bezel-in,
  .split__media .bezel-in,
  .split__inset .bezel-in,
  .stack__inner .bezel-in,
  .cards .card__img {
    aspect-ratio: 3 / 2 !important;
    height: auto !important;
    min-height: 0 !important;
    align-self: stretch !important;
  }
}

/* --------------------------------------------------------------------------
   33. Phone: the full-bleed panels stack
   --------------------------------------------------------------------------
   .chapter, .band and .cta put a heading over a landscape photograph inside a
   tall panel. At 390px that box is 0.49 to 0.82, so 45-67% of the picture was
   cut away. Below 860px the photograph takes a 3:2 block of its own and the
   words sit underneath it. Every photograph on the site is 3:2, so nothing is
   cropped and nothing is letterboxed.

   Desktop is untouched: there a wide window and a landscape photograph already
   agree, and the text-over-image treatment is the point.

   Four things this depends on, all learned the hard way:

   - This block must stay AFTER the base .band / .chapter / .cta rules at
     ~line 1870. Media queries carry no extra specificity; source order decides.
     Placed above them it silently does nothing.
   - The backdrop is nested inside .band__inner, not directly under .band.
   - transform must be cleared. These panels no longer clip, so the parallax
     yPercent would slide the picture over the next section. site.js also skips
     parallax for any layer that is no longer absolutely positioned.
   - .chapter__copy keeps a large padding-block. The word-by-word reveal is
     driven by the copy's distance from the middle of the screen, so it needs
     room to travel; without it the words sit at their end state the whole way.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) and (orientation: portrait) {
  .chapter__pin,
  .band,
  .band__inner,
  .cta {
    min-height: 0 !important;
    display: block !important;
    overflow: visible !important;
  }
  .chapter__bg,
  .band__bg,
  .cta__bg {
    position: relative !important;
    inset: auto !important;
    width: 100%;
    aspect-ratio: 3 / 2 !important;
    transform: none !important;
  }
  /* The scrims existed to keep white text readable over the picture. The text
     is no longer over the picture, so they would only mute the photograph. */
  .chapter__bg::after,
  .band__bg::after,
  .cta__bg::after,
  .chapter__bg::before,
  .band__bg::before,
  .cta__bg::before { display: none !important; }

  .chapter__copy { padding-block: clamp(4.5rem, 20vh, 9rem); }
  .band__inner,
  .cta__inner  { padding-block: clamp(1.75rem, 6vw, 3rem); }
}

