/* ==========================================================================
   SENSES HAVELOCK RESORT — COMPONENTS
   Consumes tokens from design-system.css. Load that first.

   Two rules this file never breaks:
   1. No text is ever set over a photograph. Copy sits on solid ground —
      a white card, a tinted panel, or the page itself.
   2. Every measurement comes from a token. No component invents a gap.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. HEADER + NAVIGATION
   Transparent over the full-height hero photograph, solid once the page is
   scrolled past it. JS adds .is-scrolled; without JS the bar stays solid
   from the start (see the no-js rule below), so the nav is never invisible.
   -------------------------------------------------------------------------- */

.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-header);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

/* Scrim: white nav copy needs a floor under it on bright skies. */
.site-header::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(7,43,51,0.45), rgba(7,43,51,0));
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}

/* --- Transparent state: everything reads white over the photograph ------ */
.site-header .nav__logo img { filter: brightness(0) invert(1); }
.site-header .nav__list > li > a,
.site-header .nav__tel { color: #fff; }
.site-header .nav__toggle span { background: #fff; }

/* Interpolate with the bar. The toggle bars are left out on purpose — their
   own transition drives the burger-to-cross animation. */
.site-header .nav__logo img {
  transition: filter var(--dur) var(--ease), height var(--dur) var(--ease);
}
.site-header .nav { transition: min-height var(--dur) var(--ease); }
.site-header .nav__list > li > a,
.site-header .nav__tel { transition: color var(--dur) var(--ease); }

/* --- Scrolled state: the original solid bar ---------------------------- */
.site-header.is-scrolled {
  background: var(--c-white);
  border-bottom-color: var(--c-line);
}
.site-header.is-scrolled::before { opacity: 0; }
.site-header.is-scrolled .nav__logo img { filter: brightness(1) invert(0); }

/* ...and condenses, so it takes less of the screen once you are reading. */
.site-header.is-scrolled .nav { min-height: var(--header-h-sm); }
.site-header.is-scrolled .nav__logo img { height: 56px; }
.site-header.is-scrolled .nav__list > li > a,
.site-header.is-scrolled .nav__tel { color: var(--c-brand); }
.site-header.is-scrolled .nav__toggle span { background: var(--c-brand); }

/* No JS: no .is-scrolled will ever be set, so start solid. */
html:not(.js) .site-header { background: var(--c-white); border-bottom-color: var(--c-line); }
html:not(.js) .site-header::before { opacity: 0; }
html:not(.js) .site-header .nav__logo img { filter: none; }
html:not(.js) .site-header .nav__list > li > a,
html:not(.js) .site-header .nav__tel { color: var(--c-brand); }
html:not(.js) .site-header .nav__toggle span { background: var(--c-brand); }

.nav {
  position: relative;   /* above the header's gradient scrim */
  z-index: 1;
  /* Three fixed tracks, not space-between. The logo shrinks 104 -> 72px on
     scroll; under space-between that slack was redistributed and dragged the
     centred menu ~16px to the left every time the bar condensed. With
     `1fr auto 1fr` the menu is centred on the container itself, so it holds
     its position no matter what the logo or the actions do. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--header-h);
}
.nav__logo { justify-self: start; }
.nav__actions { justify-self: end; }

.nav__logo { flex-shrink: 0; display: flex; align-items: center; }
.nav__logo img { height: 74px; width: auto; }

/* The burger is a member of .nav__list so that on desktop it sits after the
   last link. Hiding the whole list on phones would take the burger with it and
   leave no way to open the menu at all, so below 1024px the list dissolves to
   `contents` and only its LINKS are hidden — the button survives as a direct
   flex child of .nav and keeps its original position. See the max-width:1023px
   block in section 10 for the ordering. */
@media (max-width: 1023px) {
  .nav__list { display: contents; }
  .nav__list > li:not(.nav__more) { display: none; }
  /* The panel is a desktop affordance; on phones the drawer does this job. */
  .nav__more-panel { display: none !important; }
}

.nav__actions { display: flex; align-items: center; gap: 1rem; }

.nav__tel {
  display: none;
  font-size: var(--fs-small);
  color: var(--c-brand);
  white-space: nowrap;
}

.nav__toggle {
  width: 44px; height: 44px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-right: -8px;
}
.nav__toggle span {
  display: block;
  width: 22px; height: 1.5px;
  background: var(--c-brand);
  transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

/* Drawer — a mobile layout in its own right, not a folded desktop nav. */
.drawer-scrim {
  position: fixed; inset: 0;
  background: rgba(7, 43, 51, 0.5);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
  z-index: calc(var(--z-drawer) - 1);
}
.drawer-scrim.is-open { opacity: 1; visibility: visible; }

/* Anchored left and sliding in left-to-right, so the panel arrives from the
   same edge as the hamburger that opens it and the close button that shuts
   it. */
.drawer {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: min(88vw, 380px);
  background: var(--c-white);
  z-index: var(--z-drawer);
  transform: translateX(-100%);
  transition: transform var(--dur) var(--ease);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.drawer.is-open { transform: translateX(0); }

.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1rem 1.5rem;
  border-bottom: var(--border);
  min-height: var(--header-h);
}
.drawer__head img { height: 56px; width: auto; }
.drawer__close { width: 44px; height: 44px; display: grid; place-items: center; font-size: 1.5rem; color: var(--c-brand); }

.drawer__nav { padding: 1rem 1.5rem; }

/* The full-height drawer is the PHONE menu and still carries every page. On
   desktop the burger opens a small panel instead (.nav__more-panel), holding
   only the two pages that moved out of the bar. */
.drawer__nav a {
  display: flex;
  align-items: center;
  min-height: 52px;
  font-family: var(--ff-display);
  font-size: 1.375rem;
  font-weight: var(--fw-display);
  color: var(--c-brand);
  letter-spacing: var(--ls-heading);
  border-bottom: var(--border);
}
/* The drawer is the same navigation, so it carries the same mark: the row's
   own hairline becomes the two-colour rule on the current page. */
.drawer__nav a[aria-current="page"] {
  border-bottom: 2px solid transparent;
  border-image: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%) 1;
}
.drawer__foot {
  margin-top: auto;
  padding: 1.5rem 1.5rem 2.5rem;
  background: var(--c-bg-alt);
  display: grid;
  gap: 0.75rem;
}
.drawer__foot .btn { width: 100%; }

body.is-locked { overflow: hidden; }

/* --------------------------------------------------------------------------
   2. BUTTONS
   REF measured: square corners (radius 0), uppercase, 1px outline,
   padding 18px 50px @1407 / 10px 36px @991.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: clamp(0.75rem, 1.28vw, 1.125rem) clamp(1.75rem, 3.55vw, 3.125rem);
  min-height: 46px;
  font-family: var(--ff-text);
  font-size: var(--fs-btn);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  line-height: 1.2;
  border: 1px solid transparent;
  border-radius: var(--radius);
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}

/* REF's default is the outlined button — it is the house style. */
.btn--outline { border-color: var(--c-brand); color: var(--c-brand); background: transparent; }
.btn--outline:hover { background: var(--c-brand); color: #fff; }

.btn--primary { background: var(--c-accent); color: #fff; border-color: var(--c-accent); }
.btn--primary:hover { background: var(--c-accent-dark); border-color: var(--c-accent-dark); color: #fff; }

.btn--solid { background: var(--c-brand); color: #fff; border-color: var(--c-brand); }
.btn--solid:hover { background: var(--c-ocean-800); border-color: var(--c-ocean-800); color: #fff; }

.btn--light { border-color: rgba(255,255,255,0.7); color: #fff; }
.btn--light:hover { background: #fff; color: var(--c-brand); border-color: #fff; }

.btn-row { display: flex; flex-wrap: wrap; gap: 1rem; }
.btn-row--center { justify-content: center; }

/* --------------------------------------------------------------------------
   3. SLIDER — one component, used by the hero, the room cards and dining.
   Height is set by whoever uses it; everything else is shared.
   -------------------------------------------------------------------------- */

.slider__viewport { position: relative; overflow: hidden; background: var(--c-line); }
.slider__track { display: flex; height: 100%; transition: transform 600ms var(--ease); }
.slider__slide { flex: 0 0 100%; height: 100%; }
.slider__slide img { width: 100%; height: 100%; object-fit: cover; }

/* Counter pill — solid, so it never depends on the photograph for contrast. */
.slider__count {
  position: absolute;
  top: 0.75rem; right: 0.75rem;
  z-index: 3;
  background: rgba(7, 43, 51, 0.85);
  color: #fff;
  font-size: var(--fs-caption);
  letter-spacing: 0.06em;
  padding: 0.25rem 0.625rem;
  border-radius: var(--radius-pill);
}

/* --------------------------------------------------------------------------
   3b. HERO — images only, no text over photography
   -------------------------------------------------------------------------- */

.hero { position: relative; background: var(--c-bg-alt); }
/* Full viewport height. svh keeps mobile browser chrome from cropping it. */
.hero .slider__viewport { height: 100vh; height: 100svh; }
.hero .slider__track { transition-duration: 700ms; }

/* Video variant — the home hero.
   The file is 16:9 and the frame is a full viewport, so the video is CROPPED
   to fill it rather than letterboxed: cover scales it to the larger dimension
   and the viewport's own overflow trims the rest. The poster carries the same
   crop, so the swap from still to moving picture does not jump. */
.hero--video .slider__viewport { background: var(--c-ocean-800); }
.hero__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
/* A video that plays itself is motion the visitor did not ask for. With
   reduced motion set, the poster frame stands in for it. */
@media (prefers-reduced-motion: reduce) {
  .hero__video { display: none; }
  .hero--video .slider__viewport {
    background-image: url("https://assets.simplotel.com/simplotel/image/upload/senses-havelock/Main_Pic_5f77e283.JPG");
    background-size: cover;
    background-position: center;
  }
}

/* Band variant — a SLIDER hero on an inner page. The full viewport belongs to
   the home page; an inner page's hero is a band, and on a phone a 100svh
   banner meant you scrolled a whole screen before reaching the breadcrumb.
   Same ratios as .hero--still, so Experiences now matches Contact and the
   Rooms summary at every width. */
.hero--band .slider__viewport { height: auto; aspect-ratio: 3 / 2; }
@media (min-width: 600px)  { .hero--band .slider__viewport { aspect-ratio: 16 / 9; } }
@media (min-width: 1024px) { .hero--band .slider__viewport { aspect-ratio: 21 / 9; } }

/* Still variant — inner pages. Home takes the full viewport; every other page
   uses the ratios the library records: 3:2 mobile, 16:9 tablet, 21:9 desktop.
   No slider, so the viewport holds the image directly. */
.hero--still .slider__viewport { height: auto; aspect-ratio: 3 / 2; }
.hero--still .slider__viewport > img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 600px)  { .hero--still .slider__viewport { aspect-ratio: 16 / 9; } }
@media (min-width: 1024px) { .hero--still .slider__viewport { aspect-ratio: 21 / 9; } }

/* Circular arrows — REF pattern, solid discs so they never rely on the
   photograph behind them for contrast.

   The mark is DRAWN here, not typed in the markup. Every slider ships a
   ‹ / › character inside the button, but the glyph sat off its optical
   centre, changed weight with the font and rendered at a different size in
   every slider — which is what made the arrows look wrong. The character is
   hidden with font-size:0 and replaced by a shaft and a head, so the arrow
   is identical everywhere and scales with the button. The buttons carry
   aria-labels, so nothing is lost by hiding the character. */
.slider-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  /* The global coarse-pointer rule stretches EVERY button to a 44px minimum
     height. On a disc that is a declared width and height, that turned the
     circle into an oval on touch screens — the squeezed, half-cut look. The
     disc opts out and states its own size, so width and height can never
     disagree; the sizes chosen below all clear 44px on their own. */
  min-height: 0;
  background: rgba(255,255,255,0.92);
  border: 1px solid rgba(255,255,255,0.92);
  border-radius: var(--radius-pill);
  flex: 0 0 auto;
  color: var(--c-brand);
  font-size: 0;
  line-height: 0;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(7,43,51,0.16);
  z-index: 3;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.slider-arrow:hover { background: #fff; border-color: #fff; color: var(--c-lagoon); }

/* The arrow: a shaft with a chevron head at one end. */
.slider-arrow::before,
.slider-arrow::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
}
.slider-arrow::before {
  width: 17px; height: 1.5px;
  background: currentColor;
  transform: translate(-50%, -50%);
}
.slider-arrow::after {
  width: 7px; height: 7px;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
}
.slider-arrow--next::after { transform: translate(-50%, -50%) translateX(4px) rotate(45deg); }
.slider-arrow--prev::after { transform: translate(-50%, -50%) translateX(-4px) rotate(-135deg); }

.slider-arrow--prev { left: clamp(0.75rem, 1.6vw, 1.5rem); }
.slider-arrow--next { right: clamp(0.75rem, 1.6vw, 1.5rem); }

/* Smaller discs inside cards keep a proportionally smaller mark. */
.slider-arrow--sm { width: 38px; height: 38px; }
.slider-arrow--sm::before { width: 13px; }
.slider-arrow--sm::after { width: 6px; height: 6px; }
.slider-arrow--sm.slider-arrow--next::after { transform: translate(-50%, -50%) translateX(3px) rotate(45deg); }
.slider-arrow--sm.slider-arrow--prev::after { transform: translate(-50%, -50%) translateX(-3px) rotate(-135deg); }

/* ---- Photo strip -------------------------------------------------------
   Several photographs across the frame, stepping one photograph at a time
   rather than one frame. Same slider markup and same script as every other
   slider — only the width of a slide changes. */
.strip {
  --per: 1;
  --strip-gap: clamp(0.75rem, 1.4vw, 1.25rem);
}
@media (min-width: 600px)  { .strip { --per: 2.5; } }
/* Fractional on purpose: three photographs sit whole and the fourth half-shows
   at the right edge, which is what tells you the strip scrolls. */
@media (min-width: 1024px) { .strip { --per: 3.5; } }

.strip .slider__viewport { background: transparent; }
.strip .slider__track { gap: var(--strip-gap); }
/* Landscape, not square: at 3.5 across a square slide still stood taller than
   it needed to. 4:3 keeps the strip to a band rather than a wall. */
.strip .slider__slide {
  flex: 0 0 calc((100% - (var(--per) - 1) * var(--strip-gap)) / var(--per));
  height: auto;
  aspect-ratio: 4 / 3;
  background: var(--c-line);
}
.strip .slider__slide img { display: block; }

/* ---- Card strip -----------------------------------------------------------
   .strip carries bare photographs. .strip--cards carries a photograph WITH a
   name under it, which is what the experiences and packages sliders need.

   The slide stops being the picture and becomes the card: the aspect-ratio
   moves off the slide and onto the media inside it, so the caption below can
   take its natural height instead of eating into the frame. */
.strip--cards .slider__slide {
  aspect-ratio: auto;
  height: auto;
  background: transparent;
  display: flex;
  flex-direction: column;
}
.strip--cards .strip-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--c-white);
  border: var(--border);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
/* The whole card is the link where one is given, so the hover belongs to it. */
a.strip-card:hover { border-color: var(--c-lagoon); box-shadow: 0 14px 34px rgba(7, 43, 51, 0.1); }

.strip--cards .strip-card__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--c-line);
}
.strip--cards .strip-card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 600ms var(--ease);
}
a.strip-card:hover .strip-card__media img { transform: scale(1.04); }

/* The name, and only the name. Centre-set so a one-word label and a
   three-word one read as the same object. */
.strip-card__name {
  padding: 0.875rem 1rem;
  text-align: center;
  font-family: var(--ff-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-heading);
  line-height: 1.25;
  color: var(--c-brand);
}
/* A second line under the name for the one fact that earns its place — the
   duration on a package, the season on an experience. Never a paragraph. */
.strip-card__meta {
  margin-top: 0.25rem;
  display: block;
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-lagoon);
}

/* The system's heading rule at card scale, as every other card here carries. */
.strip--cards .strip-card__media::after {
  content: "";
  position: absolute;
  z-index: 1;
  top: 0; left: 0;
  width: 56px; height: 3px;
  background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
}

/* Three-and-a-bit bare photographs read fine, but a card carrying a name needs
   more width before the name starts wrapping awkwardly. */
@media (min-width: 1024px) { .strip--cards { --per: 3.25; } }
@media (min-width: 1440px) { .strip--cards { --per: 4.25; } }


.hero__dots {
  position: absolute;
  /* A plain inset from the foot of the photograph. This used to add
     --booking-overlap to clear the booking bar, but the only hero carrying
     these dots is the Experiences band, which has no booking bar over it — so
     the offset just pushed the dots ~48px up into the middle of the image and
     left them floating. If a booking bar is ever put over a slider hero, add
     the overlap back on that page's variant, not here. */
  left: 0; right: 0; bottom: 1.25rem;
  display: flex; justify-content: center; gap: 0.125rem;
  z-index: 3;
}
/* The dot is drawn as a pseudo-element and the button is an invisible
   44px touch target around it. Sized directly, the global coarse-pointer rule
   (`button { min-height: 44px }`) stretched each 9px dot into a vertical bar. */
.hero__dots button {
  width: 26px;
  height: 44px;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.hero__dots button::before {
  content: "";
  display: block;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 0 1px rgba(7, 43, 51, 0.28);
  transition: width var(--dur) var(--ease), background var(--dur) var(--ease);
}
.hero__dots button[aria-current="true"]::before {
  width: 22px;
  background: #fff;
}

/* --------------------------------------------------------------------------
   4. BOOKING BAR — lifted onto the bottom of the hero photograph
   -------------------------------------------------------------------------- */

/* The section carries the overlap, so the form itself stays a plain block.
   Bottom padding is the short section rhythm, not the tall one — the gap to
   About is a card-to-content step now, not a full section break. */
/* How far the bar climbs onto the photograph. It was 48-88px, which sat over
   the last line of lettering burnt into the hero video; dropped to ~16-28px
   so the bar still reads as lifted onto the hero, but gives that line back.
   This single value moves the bar — raise it to overlap more, set it to 0 to
   put the bar fully below the hero. */
:root { --booking-overlap: clamp(1rem, 2vw, 1.75rem); }

#booking {
  position: relative;
  z-index: 4;
  padding-top: 0;
  padding-bottom: var(--section-y-base);
  margin-top: calc(-1 * var(--booking-overlap));
}

/* Solid white panel — it sits on the foot of the hero photograph, and a
   translucent ground let the picture read through the fields. */
.booking {
  /* Capped and centred. The site container is fluid by design, but a form is
     not: at 1920 the bar stretched to ~1660px and four fields of ~150px of
     content sat marooned in 380px columns. */
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
  background: var(--c-white);
  border: var(--border);
  box-shadow: 0 18px 45px rgba(7, 43, 51, 0.22);
  padding: clamp(1.125rem, 1.7vw, 1.5rem) clamp(1.25rem, 2vw, 1.75rem);
  display: grid;
  gap: var(--gap-field);
}

/* About follows the booking bar directly, so it takes the short rhythm too —
   otherwise two full section paddings stack into a ~190px void. */
#about { padding-top: var(--section-y-base); }

.booking__field { display: grid; gap: 0.375rem; align-content: start; min-width: 0; }
/* Labels and the guests summary stay on one line; a long summary ("3 Rooms,
   6 Adults, 2 Children") ends in an ellipsis and shows in full when opened. */
.booking__field label,
.booking__label {
  white-space: nowrap;
  font-size: var(--fs-small);
  font-weight: var(--fw-body);
  color: var(--c-ink);
}

/* Fields are underlines, not boxes: an icon and the control sit on one rule
   that colours to brand while the field has focus inside it. */
.booking__control {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 36px;
  padding-bottom: 0.375rem;
  border-bottom: 1px solid var(--c-line);
  transition: border-color var(--dur-fast) var(--ease);
}
.booking__control:focus-within { border-bottom-color: var(--c-brand); }
.booking__icon { width: 18px; height: 18px; flex: 0 0 auto; color: var(--c-muted); }

.booking__control input,
.booking__control select {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  color: var(--c-ink);
}
.booking__control input::placeholder { color: var(--c-muted); }

/* The icon already says "date", so the native picker button is stretched
   invisibly over the whole control instead — the field opens on any click. */
.booking__control input[type="date"] { position: relative; }
.booking__control input[type="date"]::-webkit-calendar-picker-indicator {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

/* ---- Rooms & Guests: field + counter panel ------------------------------ */

.booking__field--guests { position: relative; }

.booking__guests-toggle {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  color: var(--c-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* No JavaScript: the panel is a plain block under the field and every count
   stays typeable. With JavaScript it becomes a dropdown off the underline. */
.booking-pop {
  margin-top: 0.875rem;
  display: grid;
  gap: 0.875rem;
  padding: 1rem;
  background: var(--c-white);
  border: var(--border);
}
html.js .booking-pop {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 5;
  display: none;
  min-width: 17rem;
  margin-top: 0.5rem;
  box-shadow: 0 18px 45px rgba(7, 43, 51, 0.22);
}
html.js .booking__field--guests.is-open .booking-pop { display: grid; }

.booking-pop__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}
.booking-pop__row label { font-size: var(--fs-small); color: var(--c-ink); }
.booking-pop__note {
  display: block;
  font-size: var(--fs-caption);
  color: var(--c-muted);
}

.booking-pop__done {
  justify-self: end;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-brand);
}
html:not(.js) .booking-pop__done { display: none; }

.stepper { display: flex; align-items: center; gap: 0.25rem; flex: 0 0 auto; }
.stepper__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--c-line);
  background: var(--c-white);
  color: var(--c-brand);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}
.stepper__btn:hover { border-color: var(--c-brand); background: var(--c-bg-alt); }
html:not(.js) .stepper__btn { display: none; }

.stepper__val {
  width: 2.75rem;
  min-height: 32px;
  padding: 0;
  border: 1px solid transparent;
  background: transparent;
  text-align: center;
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  color: var(--c-ink);
}
html:not(.js) .stepper__val { border-color: var(--c-line); }
/* The buttons are the affordance, so the native spinners are redundant. */
html.js .stepper__val { -moz-appearance: textfield; appearance: textfield; }
html.js .stepper__val::-webkit-outer-spin-button,
html.js .stepper__val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---- Per-room blocks (JavaScript) --------------------------------------
   site.js replaces the three fallback counters with one block per room:
   Room N heading, adults, children, then an age select per child. The panel
   can grow to five rooms, so it scrolls inside a max-height site.js sets
   from the space available, and opens upward when there is more room above. */
html.js .booking-pop {
  width: min(20rem, calc(100vw - 2 * var(--page-padding, 1rem)));
  overflow-y: auto;
  overscroll-behavior: contain;
  gap: 0;
  padding-block: 0.25rem 1rem;
}
html.js .booking-pop--up { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 0.5rem; }

.booking-pop__room { display: grid; gap: 0.75rem; padding-block: 0.875rem; }
.booking-pop__room + .booking-pop__room { border-top: var(--border); }
.booking-pop__room-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.booking-pop__room-title {
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-brand);
}
.booking-pop__label { font-size: var(--fs-small); color: var(--c-ink); }

.booking-pop__remove,
.booking-pop__add {
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  color: var(--c-accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.booking-pop__add { text-decoration: none; font-weight: var(--fw-semi); }
.booking-pop__remove:hover,
.booking-pop__add:hover { color: var(--c-accent-dark); }

.booking-pop__ages { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; }
.booking-pop__age { display: grid; gap: 0.25rem; }
.booking-pop__age label { font-size: var(--fs-caption); color: var(--c-muted); }
.booking-pop__age select {
  min-height: 36px;
  padding: 0 0.5rem;
  border: 1px solid var(--c-line);
  background: var(--c-white);
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  color: var(--c-ink);
}
.booking-pop__age select:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 1px; }

.booking-pop__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 0.875rem;
  border-top: var(--border);
}
.booking-pop__foot .booking-pop__done { margin-left: auto; }

/* The rendered value is an <output>, not an input: centre it in the same box. */
output.stepper__val { display: inline-flex; align-items: center; justify-content: center; }
.stepper__btn:disabled { opacity: 0.35; cursor: not-allowed; }
.stepper__btn:disabled:hover { border-color: var(--c-line); background: var(--c-white); }

/* Rate above the button, manage-booking link below it — the button stays the
   one heavy element in the bar. */
.booking__cta { display: grid; gap: 0.5rem; align-content: end; }

/* The rate sits under its own button. It used to head a separate strip above
   the fields with "Manage Booking" opposite — a row of furniture that read as
   a leftover toolbar. Manage Booking is a utility link and now lives in the
   footer menu. */
.booking__rate {
  font-size: var(--fs-caption);
  text-align: center;
  color: var(--c-muted);
}
.booking__rate strong {
  font-weight: var(--fw-semi);
  color: var(--c-accent);
}

.booking .btn { width: 100%; }

@media (min-width: 600px) {
  .booking { grid-template-columns: repeat(2, 1fr); gap: var(--gap-field) 1.5rem; }
  .booking__cta { grid-column: 1 / -1; }
}

/* Two rows from laptop up: the meta strip spans row 1, and the four fields and
   the button share row 2, bottom-aligned so the field underlines and the button
   foot sit on one line. Every position is pinned — left to auto-placement the
   grid cursor dropped the meta strip beneath the fields. */
@media (min-width: 1024px) {
  .booking {
    /* minmax(0, 1fr), not 1fr: the inputs' default ~20ch width held the date
       and promo columns open and Rooms & Guests, the only shrinkable one,
       took the whole squeeze — 69px at 1024, wrapping label and summary. */
    grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(10rem, auto);
    align-items: end;
    column-gap: 1.25rem;
    row-gap: 0.875rem;
  }
  /* One row now the meta strip is gone. The rows were pinned to 2 while a
     strip occupied row 1; left as they were, row 1 stays empty and its
     row-gap shows up as dead space at the top of the bar. */
  .booking__cta { grid-column: 5; }
}

/* --------------------------------------------------------------------------
   5. CARD — the reference's card component
   Image on top (16:10), tinted body block, serif H3, outlined button.
   Square corners, no shadow. Used identically wherever cards appear.
   -------------------------------------------------------------------------- */

.card { display: flex; flex-direction: column; background: var(--c-lagoon-tint); }

.card__media { aspect-ratio: 16 / 10; overflow: hidden; background: var(--c-line); }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 600ms var(--ease); }
.card:hover .card__media img { transform: scale(1.04); }

.card__body { padding: var(--card-pad); display: flex; flex-direction: column; flex: 1; }
.card__body > * + * { margin-top: var(--gap-heading-para); }
.card__body > h3 + p { margin-top: 0.75rem; }
.card__body > .btn { margin-top: var(--gap-para-btn); align-self: flex-start; }
.card__meta {
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-lagoon);
}
.card__text { font-size: var(--fs-body); }

/* Icon tile — facilities without photography */
.tile { background: var(--c-lagoon-tint); padding: var(--card-pad); height: 100%; }
.tile__icon { width: 40px; height: 40px; margin-bottom: var(--gap-icon-text); }
.tile__icon img { width: 100%; height: 100%; }
/* h2 as well as h3: a tile directly under a page's h1 takes h2 by hierarchy,
   and it is .t-h4 that sets its size. */
.tile > :is(h2, h3) + p { margin-top: 0.75rem; }
.tile p { font-size: var(--fs-body); }

/* --------------------------------------------------------------------------
   6. ROOMS — side-by-side comparison
   Both room types sit in one fold so they can be read against each other.
   The two columns share an identical row structure (rate, name, the same
   five spec rows, CTA), so every line aligns horizontally across the pair
   and the eye can compare down a column or across a row.

   Image height is viewport-relative, which is what keeps the whole section
   — heading included — inside a single screen.
   -------------------------------------------------------------------------- */

.rooms-compare { display: grid; gap: var(--gap-card); }

/* The section ground is plain white, so the card can no longer lean on a
   tinted band to separate itself. It holds its own edge with a hairline and
   lifts on hover — the only motion in the section, which is what tells you
   the card leads somewhere. */
.room {
  display: flex;
  flex-direction: column;
  background: var(--c-white);
  border: var(--border);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.room:hover {
  border-color: var(--c-lagoon);
  box-shadow: 0 14px 34px rgba(7, 43, 51, 0.12);
}

/* Rate above the name: the card's commercial line, and the one piece of
   information the comparison was missing. */
.room__rate {
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-lagoon);
}
.room__rate strong { font-weight: var(--fw-semi); color: var(--c-brand); }
.room__body > .room__rate + h2 { margin-top: 0.375rem; }

/* The name is the link to the room's own page. The card itself is not a
   link, so the two buttons under it stay independently clickable. */
.room__body h2 a { color: inherit; }
.room__body h2 a:hover { color: var(--c-lagoon); }

/* The image is the card's main subject: on small screens it runs the full card
   width at 3:2; from 1280px it fills the whole left column, matching the
   details column's height so no dead space is left under it. */
.room__media { display: flex; flex-direction: column; }
.room .slider__viewport { aspect-ratio: 3 / 2; }

/* Thumbnail strip. Fixed strip height with flexible cells, deliberately not
   a square-per-thumb grid: the two cards carry five and four photographs, and
   only a fixed height keeps both image columns the same depth. */
.slider__thumbs {
  display: flex;
  gap: 2px;
  margin-top: 2px;
  height: clamp(3.25rem, 5.5vw, 4.5rem);
  flex-shrink: 0;
}
.slider__thumbs button {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  background: var(--c-line);
}
.slider__thumbs img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Unselected thumbs sit back so the current one reads as current. */
.slider__thumbs img {
  opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease);
}
.slider__thumbs button:hover img,
.slider__thumbs button[aria-current="true"] img { opacity: 1; }

.slider__thumbs button[aria-current="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--c-accent);
  pointer-events: none;
}

/* Arrows are one component site-wide: the room sliders previously
   overrode them to 38px, so the homepage arrows differed from the ones
   on Dining and Experiences. Only the inset is room-specific now. */
.room .slider-arrow--prev { left: 0.625rem; }
.room .slider-arrow--next { right: 0.625rem; }

/* ---- Room detail gallery ------------------------------------------------
   16:9 across a fluid container is a tall frame: at 1920 the container is
   ~1740 wide, which makes the photograph alone ~980px — taller than the
   screen it is being viewed on, before the thumbnail strip is counted. The
   height is therefore VIEWPORT-relative and the ratio only sets the shape
   on narrow screens, so the gallery and its thumbs land in one screen.
   object-fit on the images does the cropping. */
.room-gallery .slider__viewport { aspect-ratio: 16 / 9; }

/* From laptop up the height is STATED, not capped. `aspect-ratio` paired with
   `max-height` does not do what it looks like it does: once the height is
   clamped, the ratio pulls the WIDTH in to match, so a 58vh cap on a 16:9
   frame left the photograph about 945px wide inside a 1656px container with
   dead white to its right. The same trap is documented on the location map.
   Stating the height outright and dropping the ratio keeps the frame full
   width; object-fit on the image does the cropping. */
@media (min-width: 1024px) {
  .room-gallery .slider__viewport {
    aspect-ratio: auto;
    height: min(58vh, 34rem);
  }
}

/* One photograph fills the frame, edge to edge.
   A centre-stage variant was tried here — the current photograph held at 60%
   width in the middle with its neighbours flanking it, dimmed — and it has
   been withdrawn. The gallery opens on slide 1, where there is no previous
   photograph to fill the left flank, so the frame opened with a band of dead
   grey down its left side. A peek carousel only works when it can never rest
   on its first or last item. */
.room-gallery .slider__track {
  gap: 0;
  align-items: stretch;
}
.room-gallery .slider__slide {
  flex: 0 0 100%;
  height: 100%;
  opacity: 1;
}

.room__body {
  padding: var(--card-pad);
  display: flex;
  flex-direction: column;
  flex: 1;
}

.room__body > h3 + p,
.room__body > h2 + p { margin-top: 0.625rem; }
.room__body > p { font-size: var(--fs-body); }

/* Spec table — the comparison itself. Identical rows in both columns. */
.room__specs { margin-top: 0.875rem; border-top: var(--border); }
.room__specs > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.3125rem 0;
  border-bottom: var(--border);
}
/* Tighter leading than body copy: these are scanned, not read. */
.room__specs dt {
  font-size: var(--fs-caption);
  line-height: 1.45;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-muted);
  flex-shrink: 0;
}
.room__specs dd {
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.45;
  font-weight: var(--fw-medium);
  color: var(--c-ink);
  text-align: right;
}
/* The row that actually distinguishes the two rooms. */
.room__specs .is-distinct dd { color: var(--c-accent); }

/* Both actions live inside the card. Two equal columns rather than an
   inline row: at card width there is not enough room for two default-padded
   buttons side by side, and equal halves keep the pair aligned across both
   cards. */
.room .btn-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-top: auto;
  padding-top: 1.25rem;
}
.room .btn { padding-inline: 0.75rem; }

/* Two columns from tablet up — comparison is the point, so they stay
   side by side rather than stacking at 600px. */
@media (min-width: 600px) {
  .rooms-compare { grid-template-columns: repeat(2, 1fr); }
}

/* From 1280px each card turns its details alongside its image instead of
   below it. Card height becomes max(image, details) rather than their sum,
   which is what keeps the comparison inside one fold — and the image
   stretches to the full card height, so no dead space is left beneath it. */
@media (min-width: 1280px) {
  .room {
    display: grid;
    grid-template-columns: 45% 1fr;
    align-items: stretch;
  }
  /* The media column, not the viewport, is what stretches now — the viewport
     takes what the thumbnail strip leaves. */
  .room__media { height: 100%; min-height: clamp(15rem, 33vh, 24rem); }
  .room .slider__viewport {
    aspect-ratio: auto;
    flex: 1;
    min-height: 0;
  }
  .room__body { border-left: var(--border); }
}

/* Rooms carries no section-rhythm exception of its own — it inherits the
   global --section-y like every other section, so the home page and the
   inner pages step identically. Only the short-viewport rule below departs
   from it. */

/* Short viewports (1366x768 and similar laptops) are constrained by height,
   not width. The card's height is set by its details column, not its image,
   so the saving has to come from the section's own rhythm — retuned here by
   overriding the tokens rather than by hand-editing any component. */
@media (max-height: 780px) {
  .rooms { --section-y: 1.5rem; --gap-heading-cards: 2rem; --card-pad: 1rem; }
  /* A custom property set on .rooms cannot reach a previous sibling, so the
     section above it is tightened directly — otherwise the 100px it carries
     eats the height this rule is trying to save. */
  .section:has(+ .rooms) { padding-bottom: 1.5rem; }
  .rooms .room__specs > div { padding-block: 0.25rem; }
  .rooms .room .btn-row { padding-top: 0.875rem; }
  /* ...but not the gap BELOW the cards. The squeeze exists to fit both cards
     into the first screen, and that gap is off-screen at that moment, so
     tightening it saves nothing and only leaves the cards sitting on the
     footer. --section-y is shadowed above, hence the root token. */
  .rooms { padding-bottom: var(--section-y-root); }
}

/* --------------------------------------------------------------------------
   7. SPLIT MEDIA (dining, about) — REF: image one side, copy the other
   -------------------------------------------------------------------------- */

.split__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

/* Stat row — a tinted panel divided by hairlines, so the figures read as one
   deliberate band rather than four numbers floating on the page.
   The dividers are the 1px grid gap letting the container's line colour
   through: no per-cell borders to re-juggle when the column count changes. */
.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--c-line);
  border: var(--border);
}
.stat {
  background: var(--c-lagoon-tint);
  text-align: center;
  padding: clamp(1.25rem, 2.4vw, 2rem) 1rem;
}
.stat__value {
  display: block;
  font-family: var(--ff-display);
  font-size: var(--fs-h1);
  font-weight: var(--fw-display);
  color: var(--c-brand);
  line-height: 1;
  letter-spacing: var(--ls-heading);
}
/* Short terracotta rule between figure and label — the section rule's role,
   at stat scale. */
.stat__value::after {
  content: "";
  display: block;
  width: 28px;
  height: 2px;
  margin: 0.75rem auto 0;
  background: var(--c-accent);
}
.stat__label {
  display: block;
  margin-top: 0.625rem;
  font-size: var(--fs-caption);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-text);
}
@media (min-width: 600px) { .stats { grid-template-columns: repeat(4, 1fr); } }

/* Distances list */
.distances { border-top: var(--border); }
/* The rows are flex, which would win over the hidden attribute's own
   display:none, so a collapsed row has to be told again. */
.distances > div[hidden] { display: none; }
/* No script, no toggle — the full list is already on the page. */
html:not(.js) [data-reveal-toggle] { display: none; }
.distances > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem;
  padding: 0.875rem 0;
  border-bottom: var(--border);
}
.distances dt { font-size: var(--fs-small); color: var(--c-ink); }
/* A place name that opens its route LOOKS like a link. This was first tried
   as the row's own ink with a dotted rule in --c-line — the hairline grey for
   1px borders, about 1.3:1 on white — which meant the underline could not be
   seen and the name read as plain text. Linked rows now take the site's link
   colour and a real underline, so which rows are routable is obvious at rest,
   not on hover. */
/* Brand teal at rest, not lagoon. Lagoon is the site's link colour and it
   works on a white card, but these rows sit small on the pale page wash where
   it went milky and hard to read. Brand is the darkest teal in the palette
   (about 8:1 on white against lagoon's 4.6:1), so the name is legible first
   and a link second. Terracotta on hover — the only other interactive colour
   the site uses, and unmistakably a change of state. */
.distances dt a {
  color: var(--c-brand);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  text-decoration-color: rgba(20, 83, 95, 0.55);
  transition: color var(--dur-fast) var(--ease),
              text-decoration-color var(--dur-fast) var(--ease);
}
.distances dt a:hover,
.distances dt a:focus-visible {
  color: var(--c-accent);
  text-decoration-color: currentColor;
}
/* The mark says the route opens elsewhere, and only appears on rows that link.
   It takes the link's own colour: in muted grey it was fainter than the word
   it belonged to. */
.distances dt a::after {
  content: "\2197";
  margin-left: 0.3em;
  font-size: 0.85em;
  line-height: 1;
  text-decoration: none;
  color: currentColor;
}
.distances dd { margin: 0; font-size: var(--fs-small); font-weight: var(--fw-semi); color: var(--c-brand); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Testimonial card — stars, quote, attributed guest */
.testimonial {
  position: relative;
  background: var(--c-white);
  border: var(--border);
  border-top: 3px solid var(--c-accent);
  padding: var(--card-pad);
  padding-top: calc(var(--card-pad) + 0.25rem);
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* Decorative open quote, set in the display face */
.testimonial::before {
  content: "\201C";
  position: absolute;
  top: 0.15em;
  right: 0.4em;
  font-family: var(--ff-display);
  font-size: 5rem;
  line-height: 1;
  color: var(--c-lagoon-tint);
  pointer-events: none;
}

.testimonial__stars { display: flex; gap: 0.25rem; }
.testimonial__stars svg { width: 1rem; height: 1rem; fill: var(--c-accent); }

.testimonial__quote { margin: 1.25rem 0 0; border: 0; padding: 0; }
.testimonial__quote p {
  font-family: var(--ff-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-body);
  line-height: var(--lh-h4);
  letter-spacing: var(--ls-heading);
  color: var(--c-ink);
}

.testimonial__author {
  margin-top: auto;
  padding-top: var(--gap-content);
  display: flex;
  align-items: center;
  gap: var(--gap-icon-text);
}
.testimonial__avatar {
  flex: 0 0 auto;
  width: 2.5rem; height: 2.5rem;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--c-lagoon-tint);
  color: var(--c-brand);
  font-family: var(--ff-display);
  font-size: var(--fs-h4);
  line-height: 1;
}
.testimonial__who { display: flex; flex-direction: column; gap: 0.15rem; }
.testimonial__name {
  font-size: var(--fs-small);
  font-weight: var(--fw-semi);
  color: var(--c-brand);
}
.testimonial__meta {
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-muted);
}

/* --------------------------------------------------------------------------
   7b. PARALLAX BAND
   A photograph fixed behind the section, so it drifts as the page scrolls.
   A near-opaque scrim sits between the image and the copy: the text never
   depends on whatever the photograph happens to be doing behind it.
   -------------------------------------------------------------------------- */

.band-parallax {
  position: relative;
  background-color: var(--c-ocean-800);   /* shows if the image fails */
  background-image: url("https://assets.simplotel.com/simplotel/image/upload/senses-havelock/Exterior_View__(2)_5a5f57c3.JPG");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;           /* the parallax itself */
}

/* Scrim strength is set by contrast, not taste. The photograph contains
   bright sky, so against the brightest pixel a 0.84 scrim over #072B33
   still returns ~5:1 for pure white text — comfortably past AA. Lightening
   it further would show more of the image but drop the body copy below 4.5:1. */
.band-parallax::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(7, 43, 51, 0.88) 0%,
    rgba(7, 43, 51, 0.82) 45%,
    rgba(7, 43, 51, 0.88) 100%
  );
}

/* Content sits above the scrim. */
.band-parallax > * { position: relative; }

/* Full white rather than the 0.82 alpha used on flat dark grounds — the
   extra contrast is what buys the image its visibility. */
.band-parallax.on-dark p,
.band-parallax.on-dark li { color: #fff; }

/* background-attachment: fixed is unreliable on iOS and repaints badly on
   touch devices, so those get a static background instead. Same for anyone
   who has asked the system to reduce motion. */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .band-parallax { background-attachment: scroll; }
}

/* Experience cards — on the parallax band the bare heading + paragraph read
   as loose text. Each gets its own translucent panel: it defines the item,
   and the extra layer lifts the copy well clear of the photograph behind
   (roughly 7:1 against the brightest pixel in the image). */
.exp-card {
  position: relative;
  padding: var(--card-pad);
  background: rgba(7, 43, 51, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.16);
}

/* Same gradient rule the section headings use, at card scale. */
.exp-card::before {
  content: "";
  position: absolute;
  top: -1px; left: -1px;
  width: 56px; height: 3px;
  background: linear-gradient(90deg, #9FD3DC 0%, var(--c-accent) 100%);
}

.exp-card__num {
  display: block;
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-display);
  line-height: 1;
  letter-spacing: var(--ls-heading);
  color: #9FD3DC;
  margin-bottom: 0.625rem;
}
.exp-card > h3 + p { margin-top: 0.625rem; }
.exp-card p { font-size: var(--fs-body); }

/* Copy on a dark ground */
.on-dark, .on-dark p, .on-dark li { color: rgba(255,255,255,0.82); }
.on-dark :is(h1,h2,h3,h4,h5) { color: #fff; }
.on-dark .overline { color: #9FD3DC; }
.on-dark .rule { background: linear-gradient(90deg, #9FD3DC 0%, var(--c-accent) 100%); }

/* --------------------------------------------------------------------------
   8. FOOTER
   -------------------------------------------------------------------------- */

.site-footer {
  position: relative;
  overflow: hidden;
  background: var(--c-ocean-900);
  color: rgba(255,255,255,0.72);
}

/* Group.svg as a faint watermark across the footer ground.
   Carried on a pseudo-element rather than on .site-footer itself, so the
   opacity fades ONLY the artwork — set on the element, it would take the
   link list and the address down with it. aria-hidden by construction: a
   ::before has no place in the accessibility tree, and the artwork is
   decoration, so nothing here needs describing to a screen reader. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../Group.svg") center / cover no-repeat;
  opacity: 0.07;
  pointer-events: none;
}

/* Everything real sits above the watermark. */
.site-footer > .container { position: relative; z-index: 1; }

.site-footer a { color: rgba(255,255,255,0.72); }
.site-footer a:hover { color: #fff; }

/* The footer runs on its own compact rhythm: the page's --section-y-lg and
   --lh-body are sized for reading, and both are far too airy for a link list. */
.footer__top { padding-block: clamp(2.25rem, 3.5vw, 3.25rem); display: grid; gap: 2rem; }
.footer__brand img { height: 60px; width: auto; filter: brightness(0) invert(1); }
.footer__brand p { margin-top: 0.75rem; font-size: var(--fs-small); line-height: 1.65; max-width: 34ch; }

.footer__cols { display: grid; gap: 2rem; }
.footer__col h2 {
  font-family: var(--ff-text);
  font-size: var(--fs-h6);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-white);   /* white on both mobile and desktop */
  margin-bottom: 0.75rem;
}
.footer__col li + li { margin-top: 0.375rem; }
.footer__col a, .footer__col p { font-size: var(--fs-small); line-height: 1.6; }
.footer__col address p + ul { margin-top: 0.625rem; }

/* Social row. 44px discs so they clear the touch-target minimum without the
   pointer:coarse rule stretching them into ovals. */
.footer__social { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.25rem; }
.footer__social a {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,0.22);
  border-radius: var(--radius-pill);
  color: rgba(255,255,255,0.72);
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.footer__social a:hover {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: #fff;
}
.footer__social svg { width: 18px; height: 18px; }

/* Sitemap and the Simplotel credit sit together, centred — not pushed to
   opposite edges. They wrap onto two centred lines when the row runs out. */
.footer__bottom {
  border-top: 1px solid rgba(255,255,255,0.14);
  padding-block: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0.5rem 1.5rem;
  text-align: center;
  font-size: var(--fs-caption);
  color: rgba(255,255,255,0.55);
}

/* This is a horizontal bar, not stacked prose, so the global paragraph
   stacking margin must not apply. Left in place it gives the second <p> a
   20px top margin, making its flex box 44px against the first's 24px — and
   align-items then centres two differently sized boxes, which is what threw
   the two lines 10px out of alignment. Baseline alignment keeps the text
   sitting on one line regardless of what any item contains. */
.footer__bottom p { margin: 0; }
.footer__bottom p + p { margin-top: 0; }

@media (min-width: 600px) {
  .footer__cols { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .footer__top { grid-template-columns: 1fr 2fr; gap: var(--gap-card); }
  .footer__cols { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 768px) {
  body { padding-bottom: 0; }
}

/* --------------------------------------------------------------------------
   10. DESKTOP NAVIGATION
   -------------------------------------------------------------------------- */

@media (min-width: 1024px) {
  /* The burger stays at desktop, but as the LAST ITEM OF THE MENU rather than
     out in .nav__actions beside Book Now: it overflows the menu, so it belongs
     with the links it overflows. Behind it sit only Location and Contact. */
  .nav__more { position: relative; display: flex; align-items: center; }
  .nav__more .nav__toggle {
    display: inline-flex;
    width: 32px; height: 32px;
    gap: 4px;
    margin: 0;
  }
  .nav__more .nav__toggle span { width: 18px; }
  /* The burger-to-cross animation belongs to the drawer, which this button no
     longer opens at this width. Here it opens a panel that is plainly visible
     beneath it, so the icon stays a burger and does not claim to be a close
     button for something covering the screen. */
  .nav__more .nav__toggle[aria-expanded="true"] span:nth-child(1),
  .nav__more .nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: none; }
  .nav__more .nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 1; }

  /* The panel drops from the burger, right-aligned to it so it never runs off
     the edge of the bar. Opaque white with a hairline: it sits over the hero,
     so it cannot borrow contrast from whatever photograph is behind it. */
  .nav__more-panel {
    position: absolute;
    top: calc(100% + 1.25rem);
    right: -0.75rem;
    z-index: 40;
    min-width: 210px;
    display: flex;
    flex-direction: column;
    padding: 0.5rem 0;
    background: var(--c-white);
    border: var(--border);
    box-shadow: 0 18px 40px rgba(7, 43, 51, 0.16);
  }
  .nav__more-panel[hidden] { display: none; }
  .nav__more-panel a {
    display: block;
    padding: 0.75rem 1.5rem;
    text-align: right;
    font-size: var(--fs-nav);
    letter-spacing: var(--ls-nav);
    text-transform: uppercase;
    color: var(--c-brand);
    white-space: nowrap;
  }
  .nav__more-panel a:hover { background: var(--c-lagoon-tint); }
  /* The bar marks the current page with an underline; in the panel the same
     job is done by the two-colour rule the drawer uses, on the left edge. */
  .nav__more-panel a[aria-current="page"] {
    font-weight: var(--fw-semi);
    box-shadow: inset 3px 0 0 var(--c-accent);
  }

  .nav__logo img { height: 104px; }
  /* The nav is a flex row, and an <img> with width:auto is still a shrinkable
     flex item however hard its wrapper resists — flex-shrink:0 on .nav__logo
     protects the anchor, not the image inside it. Without this the logo can be
     squeezed to ZERO width rather than the row overflowing, which fails
     silently. min-width stops the compression at the logo's own ratio. */
  .nav__logo img { min-width: 96px; object-fit: contain; }
  .site-header.is-scrolled .nav__logo img { height: 72px; }
  .nav__list { display: flex; align-items: center; gap: clamp(1rem, 1.6vw, 1.75rem); }
  .nav__list a {
    position: relative;
    font-size: var(--fs-nav);
    letter-spacing: var(--ls-nav);
    text-transform: uppercase;
    color: var(--c-brand);
    white-space: nowrap;
    padding-block: 0.5rem;
  }
  .nav__list a::after {
    content: "";
    position: absolute; left: 0; right: 0; bottom: 0;
    height: 1px; background: var(--c-accent);
    transform: scaleX(0); transform-origin: left;
    transition: transform var(--dur) var(--ease);
  }
  .nav__list a:hover::after { transform: scaleX(1); }

  /* Current page. The hover underline is simply held open and given the
     system's heading rule — lagoon into terracotta — so the bar carries the
     same two-colour mark as every section heading on the site, rather than
     inventing a new signal. Weight carries it too, for anyone who cannot
     separate the two colours. */
  .nav__list a[aria-current="page"] { font-weight: var(--fw-semi); }
  .nav__list a[aria-current="page"]::after {
    height: 2px;
    background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
    transform: scaleX(1);
  }
}

@media (min-width: 1280px) { .nav__tel { display: inline-flex; } }

/* ==========================================================================
   11. PAGE-TYPE WIDGETS
   Added for the widget library. Every one of these is required by a page in
   the site map — none exist only to pad the library out.
   ========================================================================== */

/* ---- Breadcrumb (all inner pages) --------------------------------------- */
/* Breadcrumb band. It sits directly beneath the page hero, so it takes the
   short top rhythm — a full --section-y here reads as a gap, not a break. */
/* ---- Breadcrumb ----------------------------------------------------------
   The breadcrumb sits 30px under whatever is directly above it — the hero on
   pages that have one, the header on pages that do not. It used to take the
   short section rhythm here (up to 56px) and a FULL section step (up to 100px)
   on the header-less pages, so the same row of text sat at two very different
   distances depending on which page you were on. One value, both cases. */
.section--crumbs { padding-block: 1.875rem 0; }

/* Pages with no hero. The header is FIXED, so without a photograph beneath it
   the first section would slide under the bar — the wrapper carries the bar's
   height instead. Defined here rather than in a per-page <style> block, which
   is where it used to live on four pages and would have had to be pasted into
   six more. Works whether .page-body is <main> itself or wraps it. */
.page-body { padding-top: var(--header-h); }

/* The wrapper already carries the header's height, so the first section needs
   the 30px and nothing more. */
.page-body > .section:first-child,
.page-body main > .section:first-child,
.wl-page > .section:first-child { padding-top: 1.875rem; }

/* On those same pages the breadcrumb and the heading share one section, so
   the step between them is set here rather than by section padding. It was an
   inline 1.5rem on four pages — too tight once the crumbs moved up under the
   header, and four places to change. The overline is small and quiet, and it
   needs clear air above it or it reads as part of the breadcrumb row. */
.crumbs + .section-head,
.crumbs + .section-headRow { margin-top: var(--gap-content); }

.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.crumbs li {
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-muted);
}
.crumbs a { color: var(--c-lagoon); }
/* The separator was set in --c-line-strong, a token that does not exist, so it
   fell back to --c-line — the hairline grey meant for 1px rules, which against
   white is about 1.3:1 and simply cannot be seen. It takes the crumb text's own
   muted grey instead, and a touch more size so the chevron reads at caption
   scale. */
.crumbs li + li::before {
  content: "\203A";
  margin-right: 0.5rem;
  font-size: 1.1em;
  line-height: 1;
  color: var(--c-muted);
}
.crumbs [aria-current="page"] { color: var(--c-ink); }

/* ---- Accordion (FAQ, policies) ------------------------------------------ */
.accordion { border-top: var(--border); }
.accordion details { border-bottom: var(--border); }
.accordion summary {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: var(--card-pad) 0;
  min-height: 56px;
  cursor: pointer;
  list-style: none;
  font-family: var(--ff-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
.accordion summary::-webkit-details-marker { display: none; }
.accordion summary::after {
  content: "+";
  flex-shrink: 0;
  font-family: var(--ff-text);
  font-size: 1.25rem;
  font-weight: 400;
  color: var(--c-lagoon);
  transition: transform var(--dur) var(--ease);
}
.accordion details[open] summary::after { transform: rotate(45deg); }
.accordion__panel { padding-bottom: var(--card-pad); max-width: 70ch; }
.accordion__panel > * + * { margin-top: var(--gap-para-para); }
.accordion__panel li { position: relative; padding-left: 1.25rem; font-size: var(--fs-small); }
.accordion__panel li + li { margin-top: 0.5rem; }
.accordion__panel li::before {
  content: ""; position: absolute; left: 0; top: 0.7em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--c-lagoon);
}

/* ---- Narrow reading column -----------------------------------------------
   For pages that are text and accordion rows only, with no photography to
   fill a fluid width: FAQs, Terms & Conditions.

   The site's container is deliberately fluid with no max-width, which is
   right for photography but wrong here - an accordion row stretched across
   1600px strands a short question at the left with a large empty field
   beside it, and a paragraph at that width is unreadable. Capping and
   centring the column puts the content in the middle of the page and gives
   it a real left and right margin.

   A two-column layout was tried on the FAQ page first and withdrawn: it did
   not remove the emptiness, it moved it to the LEFT, because the heading ran
   out after three lines while ten questions carried on down the right. */
.container--narrow { max-width: 62rem; margin-inline: auto; }

/* ---- Form (contact, enquiry) -------------------------------------------- */
.form { display: grid; gap: var(--gap-field); }
.form__row { display: grid; gap: var(--gap-field); }
.field { display: grid; gap: 0.375rem; }
.field label { font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--c-ink); }
.field .req { color: var(--c-accent); }
.field input, .field select, .field textarea {
  width: 100%;
  min-height: 50px;
  padding: 0.625rem 0.875rem;
  border: var(--border);
  border-radius: var(--radius);
  background: var(--c-white);
  font-size: var(--fs-small);
  color: var(--c-ink);
}
.field textarea { min-height: 132px; resize: vertical; padding-top: 0.875rem; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--c-lagoon); }
.field__hint { font-size: var(--fs-small); color: var(--c-muted); }
@media (min-width: 600px) { .form__row--2 { grid-template-columns: repeat(2, 1fr); } }

/* Enquiry variant — the short fields run three across so the form is four
   rows deep, not six. Height was the whole problem: one field per row at a
   760px measure ran the enquiry well past a screen on its own. */
@media (min-width: 600px) {
  .form--enquiry { grid-template-columns: repeat(2, 1fr); }
  .form--enquiry .field--full { grid-column: 1 / -1; }
}
@media (min-width: 900px) {
  .form--enquiry { grid-template-columns: repeat(3, 1fr); }
}
/* Three rows of message is enough to start writing in; it drags to taller. */
.form--enquiry textarea { min-height: 92px; }
/* Submit sits at the left edge, aligned with the first field's left rule, so
   it reads as the end of the form rather than as a detached corner button. */
.form--enquiry .btn-row { justify-content: flex-start; }

/* ---- Table (tariff) ------------------------------------------------------ */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border: var(--border); background: var(--c-white); }
.table-scroll table { min-width: 620px; border-collapse: collapse; width: 100%; }
.table-scroll th, .table-scroll td { padding: 0.875rem 1.125rem; text-align: left; font-size: var(--fs-small); }
.table-scroll thead th {
  background: var(--c-lagoon-tint);
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-brand);
  white-space: nowrap;
}
.table-scroll tbody tr + tr { border-top: var(--border); }
.table-scroll tbody th { font-weight: var(--fw-medium); color: var(--c-ink); }
.table-scroll td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (min-width: 1024px) { .table-scroll table { min-width: 0; } }

/* ---- Gallery ------------------------------------------------------------- */
/* Filters read as a centred text row, not a row of buttons — the same tab
   treatment as .outlet-tabs, so the site has one filter language. The only
   marks are the active item's colour and its terracotta underline. */
.gallery-filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem clamp(1.25rem, 2.6vw, 2.5rem);
}
.gallery-filters button {
  padding: 0.5rem 0;
  border: 0;
  background: none;
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-text);
  border-bottom: 2px solid transparent;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.gallery-filters button:hover { color: var(--c-brand); }
.gallery-filters button[aria-pressed="true"] { color: var(--c-brand); border-bottom-color: var(--c-accent); }

.gallery-grid { display: grid; grid-template-columns: 1fr; gap: 0.75rem; }
.gallery-grid figure { margin: 0; }
.gallery-grid button { display: block; width: 100%; padding: 0; overflow: hidden; background: var(--c-line); aspect-ratio: 4 / 3; }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; transition: transform 600ms var(--ease); }
.gallery-grid button:hover img { transform: scale(1.05); }
.gallery-grid [hidden] { display: none; }
@media (min-width: 600px)  { .gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 1rem; } }
@media (min-width: 1024px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1440px) { .gallery-grid { grid-template-columns: repeat(4, 1fr); } }

/* Captioned variant — the Gallery page, where every photograph is named.
   Each cell becomes a card: the tinted body and --card-pad interior of the
   home page card, and the card-to-card gap, so the page reads as the same
   family as the room and facility grids. A named cell needs the room a
   caption line takes, so three columns is the ceiling here, not four. */
.gallery-grid--captioned { gap: var(--gap-card); }
.gallery-grid--captioned figure {
  display: flex;
  flex-direction: column;
  background: var(--c-lagoon-tint);
}
.gallery-grid--captioned button { aspect-ratio: 16 / 10; }   /* the card media ratio */
.gallery-grid--captioned figcaption {
  padding: var(--card-pad);
  font-family: var(--ff-display);
  font-weight: var(--fw-display);
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
@media (min-width: 1440px) { .gallery-grid--captioned { grid-template-columns: repeat(3, 1fr); } }

/* Caption, photograph and arrows are one group, centred in the screen. With
   the caption pinned to the top and the arrows to the bottom, a wide photo on
   a tall screen left large empty bands between it and its own caption. The
   photo's height is capped by the viewport (less the caption and arrows)
   rather than by a stretched middle row, so the group is only as tall as its
   contents. */
.lightbox {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: rgba(7, 43, 51, 0.95);
  display: grid; grid-template-rows: auto auto auto; align-content: center;
  padding-block: 4rem 0.5rem;
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
}
.lightbox.is-open { opacity: 1; visibility: visible; }
/* Caption centred over the photograph. The close button is pulled out to the
   screen's top-right corner, so it never moves with the caption's length. */
.lightbox__bar {
  padding: 0 1.25rem 0.875rem;
  color: #fff;
}
.lightbox__bar [data-lb="close"] { position: absolute; top: 1rem; right: 1.25rem; }
.lightbox__bar button { color: #fff; width: 44px; height: 44px; display: grid; place-items: center; font-size: 1.5rem; }
.lightbox__stage {
  display: flex;
  justify-content: center;
  padding: 0 1rem;
}
.lightbox__stage img {
  max-width: 100%;
  max-height: calc(100vh - 13rem);
  max-height: calc(100svh - 13rem);
  width: auto; height: auto; object-fit: contain;
}
.lightbox__foot { display: flex; justify-content: center; align-items: center; gap: 1.5rem; padding: 1.25rem; color: rgba(255,255,255,0.85); }
/* Same drawn mark as the slider arrows, so every arrow on the site is one
   shape. Only the ground changes — outlined on the lightbox's black. */
.lightbox__foot button {
  position: relative;
  color: #fff;
  width: 44px; height: 44px;
  font-size: 0;
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: var(--radius-pill);
  transition: border-color var(--dur-fast) var(--ease);
}
.lightbox__foot button:hover { border-color: #fff; }
.lightbox__foot button::before,
.lightbox__foot button::after { content: ""; position: absolute; top: 50%; left: 50%; }
.lightbox__foot button::before {
  width: 15px; height: 1.5px;
  background: currentColor;
  transform: translate(-50%, -50%);
}
.lightbox__foot button::after {
  width: 6px; height: 6px;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
}
.lightbox__foot button[data-lb="next"]::after { transform: translate(-50%, -50%) translateX(3px) rotate(45deg); }
.lightbox__foot button[data-lb="prev"]::after { transform: translate(-50%, -50%) translateX(-3px) rotate(-135deg); }
.lightbox__count { font-size: var(--fs-small); letter-spacing: 0.08em; }
.lightbox__caption { max-width: 60rem; margin: 0 auto; text-align: center; font-size: var(--fs-small); color: rgba(255,255,255,0.85); }

/* ---- Story collage ------------------------------------------------------- */
/* Seven photographs in a mosaic. Phone and tablet stack them into an even
   two-column grid; from 1024px they take the reference's arrangement on a
   12 x 6 grid. The grid carries a fixed 21:10 ratio rather than a row height
   per breakpoint, so every cell scales with the container and the mosaic keeps
   its proportions at any width. */
.collage { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.875rem; }
.collage > figure { margin: 0; overflow: hidden; background: var(--c-line); aspect-ratio: 1; }
.collage img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The lead photograph runs full width ONLY in the stacked layout. This is
   bounded to below 1024px on purpose: `.collage > figure:first-child` scores
   (0,2,1) and would otherwise outrank `.collage__a` at (0,1,0), holding the
   first tile at `grid-column: 1 / -1` across the whole 12-column mosaic —
   which laid it over tiles b, c, d and e. */
@media (max-width: 1023px) {
  .collage > figure:first-child { grid-column: 1 / -1; aspect-ratio: 3 / 2; }
}

@media (min-width: 1024px) {
  .collage {
    grid-template-columns: repeat(12, 1fr);
    grid-template-rows: repeat(6, 1fr);
    /* Both values measured off the reference collage rather than taken from
       the token set, at explicit request: 20px gutters throughout, and a
       1657x778 block. Those two together are what make the row heights land
       at ~113px and the tiles read as separate photographs. */
    aspect-ratio: 32 / 15;
    gap: 1.25rem;
  }
  .collage > figure,
  .collage > figure:first-child { aspect-ratio: auto; }
  .collage__a { grid-column: 1 / 6;  grid-row: 1 / 5; }
  .collage__b { grid-column: 6 / 9;  grid-row: 1 / 3; }
  .collage__c { grid-column: 6 / 9;  grid-row: 3 / 5; }
  .collage__d { grid-column: 9 / 13; grid-row: 1 / 4; }
  .collage__e { grid-column: 9 / 13; grid-row: 4 / 7; }
  .collage__f { grid-column: 1 / 5;  grid-row: 5 / 7; }
  .collage__g { grid-column: 5 / 9;  grid-row: 5 / 7; }
}

/* ---- Location panel ------------------------------------------------------ */
/* Tab strip on a tinted band, then the map with a detail card beside it.
   Below 1024px the card drops beneath the map rather than beside it.
   Every measurement is a token: --card-pad, --gap-*, --border, --fs-*. */
.locmap { border: var(--border); background: var(--c-lagoon-tint); }

.locmap__tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(1.5rem, 3vw, 3rem);
  padding: 1rem;
  border-bottom: var(--border);
}
.locmap__tabs button {
  position: relative;
  padding: 0.5rem 0.25rem;
  font-size: var(--fs-nav);
  letter-spacing: var(--ls-nav);
  text-transform: uppercase;
  color: var(--c-text);
}
.locmap__tabs button::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--c-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.locmap__tabs button[aria-selected="true"] { color: var(--c-brand); font-weight: var(--fw-semi); }
.locmap__tabs button[aria-selected="true"]::after { transform: scaleX(1); }

.locmap__stage { display: grid; background: var(--c-white); }
.locmap__map { position: relative; aspect-ratio: 4 / 3; background: var(--c-lagoon-tint); }
.locmap__map iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Name chip over the map corner. The copy sits on solid ground, not on the
   map itself, so it holds contrast whatever tile is underneath. */
.locmap__pin {
  position: absolute; top: 0; left: 0;
  display: flex; align-items: center; gap: var(--gap-icon-text);
  padding: 0.75rem 1.25rem;
  background: var(--c-ocean-900);
  color: #fff;
  font-size: var(--fs-small);
}
.locmap__pin svg { width: 18px; height: 18px; flex: 0 0 auto; }

.locmap__card { padding: var(--card-pad); border-top: var(--border); }
.locmap__card img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; }
.locmap__card h2 { margin-top: var(--gap-heading-para); font-size: var(--fs-h4); }
.locmap__card .btn { width: 100%; margin-top: 1rem; }
.locmap__card p { margin-top: var(--gap-para-para); font-size: var(--fs-body); }

/* Nearby list, shown in the second tab. */
.locmap__near { padding: var(--card-pad); }

@media (min-width: 1024px) {
  .locmap__stage { grid-template-columns: 1fr minmax(18rem, 24%); }
  .locmap__map { aspect-ratio: auto; height: 100%; min-height: 30rem; }
  .locmap__card { border-top: 0; border-left: var(--border); }
}

/* Directions row — the site's form component, three across from tablet up. */
@media (min-width: 600px) {
  .form__row--3 { grid-template-columns: 1fr 1fr auto; align-items: end; }
}

/* ---- Amenity list -------------------------------------------------------- */
/* Icons are decorative: every item is already named in text beside them, so
   the svgs are aria-hidden and carry no label of their own. */
.amenities { background: var(--c-lagoon-tint); padding: var(--card-pad); }
.amenities__list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.875rem var(--gap-card);
  margin-top: var(--gap-heading-para);
}
.amenities__list li {
  display: flex;
  align-items: center;
  gap: var(--gap-icon-text);
  font-size: var(--fs-small);
  color: var(--c-text);
}
.amenities__list svg {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  color: var(--c-lagoon);
}
@media (min-width: 600px)  { .amenities__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .amenities__list { grid-template-columns: repeat(3, 1fr); } }

/* ---- Map ----------------------------------------------------------------- */
/* Wide and shallow: the container is fluid, so a tall ratio turns into a
   700px-plus band of map on a desktop screen. */
.map-embed { position: relative; border: var(--border); background: var(--c-lagoon-tint); aspect-ratio: 16 / 9; }
/* The iframe sits over the note and hides it the moment Google paints. */
.map-embed iframe { position: relative; z-index: 1; width: 100%; height: 100%; border: 0; display: block; }
.map-embed__note {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: 0.5rem;
  justify-items: center;
  padding: 1rem;
  text-align: center;
  font-size: var(--fs-small);
  color: var(--c-muted);
}
.map-embed__note a { color: var(--c-lagoon); text-decoration: underline; }
@media (min-width: 1024px) { .map-embed { aspect-ratio: 3 / 1; } }
@media (min-width: 1440px) { .map-embed { aspect-ratio: 7 / 2; } }

/* ---- Review card --------------------------------------------------------- */
.review { background: var(--c-white); border: var(--border); padding: var(--card-pad); height: 100%; display: flex; flex-direction: column; }
.review > h3 + p { margin-top: 0.75rem; }
.review p { font-size: var(--fs-body); }
.review__by { margin-top: auto; padding-top: var(--gap-content); font-size: var(--fs-caption); letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-muted); }

/* ---- Rich text (Terms, legal) -------------------------------------------- */
.rich-text { max-width: 74ch; }
.rich-text > * + * { margin-top: var(--gap-para-para); }
.rich-text h2 { margin-top: var(--gap-content); }
.rich-text h3 { margin-top: var(--gap-content); }
.rich-text ul { margin-top: var(--gap-para-para); }
.rich-text li { position: relative; padding-left: 1.25rem; }
.rich-text li + li { margin-top: 0.5rem; }
.rich-text li::before { content: ""; position: absolute; left: 0; top: 0.72em; width: 5px; height: 5px; border-radius: 50%; background: var(--c-lagoon); }

/* ==========================================================================
   12. SITE MAP PAGE CHROME
   Used only by the site map page. Follows the Phoenix
   reference's cataloguing convention: a numbered label block, then the live
   widget beneath it, separated by hairlines.
   ========================================================================== */

.wl-toc { display: grid; gap: 0.5rem 2rem; grid-template-columns: 1fr; }
.wl-toc a { font-size: var(--fs-small); color: var(--c-text); display: flex; gap: 0.625rem; }
.wl-toc a:hover { color: var(--c-brand); }
.wl-toc span { color: var(--c-accent); font-variant-numeric: tabular-nums; flex-shrink: 0; }
@media (min-width: 600px)  { .wl-toc { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .wl-toc { grid-template-columns: repeat(3, 1fr); } }

.wl-group { padding-top: var(--section-y-base); }
.wl-group__title {
  font-family: var(--ff-text);
  font-size: var(--fs-h6);
  font-weight: var(--fw-semi);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-lagoon);
  padding-bottom: 0.75rem;
  border-bottom: 2px solid var(--c-accent);
}

.wl-item { border-top: var(--border); }
.wl-item:first-of-type { border-top: 0; }

.wl-label { padding-block: var(--section-y-base) var(--gap-heading-cards); }
.wl-no { color: var(--c-accent); font-variant-numeric: tabular-nums; }
.wl-label__note {
  margin-top: 0.625rem;
  font-size: var(--fs-small);
  color: var(--c-muted);
  max-width: 74ch;
}

.wl-meta { display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; margin-top: 1.125rem; }
.wl-meta > div { display: flex; gap: 0.5rem; align-items: baseline; }
.wl-meta dt {
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--c-muted);
  white-space: nowrap;
}
.wl-meta dd { margin: 0; font-size: var(--fs-small); color: var(--c-ink); font-weight: var(--fw-medium); }

/* The container is fluid with no max-width, so on a wide screen a label block
   whose note is capped at 74ch leaves most of the row empty. From 1024px the
   note keeps its measure on the left and the meta pairs stack into a second
   column on the right, so the block reaches the full container width. */
@media (min-width: 1024px) {
  .wl-label {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    column-gap: var(--gap-card);
    align-items: start;
  }
  .wl-label > h3 { grid-column: 1; grid-row: 1; }
  .wl-label__note { grid-column: 1; grid-row: 2; }
  .wl-meta {
    grid-column: 2;
    grid-row: 1 / 3;
    margin-top: 0;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0.875rem;
    padding-left: var(--gap-card);
    border-left: var(--border);
  }
}

/* Demos are shown in isolation. On the live site these widgets sit beside
   other content that fills the row; alone on a fluid container with no
   max-width, a left-pinned narrow block leaves most of the row empty. So any
   demo that is narrower than the stage is centred on it rather than flushed
   left. Widgets that genuinely fill the width are untouched. */
.wl-stage { margin-inline: auto; }
.wl-stage--sm { max-width: 720px; }
.wl-stage--md { max-width: 960px; }
.wl-demo .measure,
.wl-demo .rich-text { margin-inline: auto; }

/* The live widget sits on a tinted stage so its own edges stay legible. */
.wl-demo { background: var(--c-bg-alt); padding-block: var(--section-y-base); }
.wl-demo--flush { padding-block: 0; }
.wl-demo--white { background: var(--c-white); }
.wl-item { padding-bottom: var(--section-y-base); }

/* ---- Header: hero-less pages --------------------------------------------
   Widget Library, Site Map, Terms and FAQ have no hero photograph for the
   bar to sit over, so it is solid from the start. This is independent of
   .is-scrolled, which JS clears at scroll position zero. */
/* The solid header, for pages with no photograph behind the bar.
   NOTE ON SPECIFICITY: the transparent-state rule that paints the links white
   is `.site-header .nav__list > li > a` (0-2-2). These overrides must match
   that child chain or they lose the cascade whatever their source order — and
   losing it means white links on a white bar, i.e. an invisible menu. The
   `.is-scrolled` block is written the same way for the same reason. Do not
   "simplify" any of these back to a bare descendant `a`. */
.site-header--solid { background: var(--c-white); border-bottom-color: var(--c-line); }
.site-header--solid::before { opacity: 0; }
.site-header--solid .nav__logo img { filter: none; }
.site-header--solid .nav__list > li > a,
.site-header--solid .nav__tel { color: var(--c-brand); }
.site-header--solid .nav__toggle span { background: var(--c-brand); }
/* The overflow panel sits on its own white ground and never wants the white
   link colour, in either header state. */
.site-header--solid .nav__more-panel a { color: var(--c-brand); }

/* ---- Outlet tab strip (dining pages) -------------------------------------
   A tinted bar naming the outlet, directly under the header. Built as a tab
   list so a second outlet can be added without redesigning the page; with
   one outlet it reads as a section marker. */
.outlet-tabs { background: var(--c-lagoon-tint); border-bottom: var(--border); }
.outlet-tabs ul {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.5rem 2.5rem;
  padding-block: 0.875rem;
}
.outlet-tabs a {
  display: inline-block;
  font-size: var(--fs-small);
  letter-spacing: 0.06em;
  color: var(--c-text);
  padding-bottom: 0.25rem;
  border-bottom: 2px solid transparent;
}
.outlet-tabs a:hover { color: var(--c-brand); }
.outlet-tabs a[aria-current="page"] { color: var(--c-brand); border-bottom-color: var(--c-accent); }

/* ---- Info panel (timings, at-a-glance facts) -----------------------------
   A centred tinted box. Used wherever a short set of label/value facts needs
   lifting out of the running copy. */
.info-panel {
  background: var(--c-lagoon-tint);
  padding: var(--card-pad);
  max-width: 520px;
  margin-inline: auto;
  text-align: center;
}
.info-panel > h2, .info-panel > h3 { font-family: var(--ff-text); font-size: var(--fs-h6); font-weight: var(--fw-semi); letter-spacing: 0.12em; text-transform: uppercase; color: var(--c-brand); }
.info-panel dl { margin-top: var(--gap-heading-para); display: grid; gap: 0.625rem; }
.info-panel dl > div { display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap; }
.info-panel dt { font-size: var(--fs-small); color: var(--c-text); }
.info-panel dt::after { content: ":"; }
.info-panel dd { margin: 0; font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--c-ink); }
.info-panel__note { margin-top: var(--gap-para-para); font-size: var(--fs-small); color: var(--c-muted); }

/* Fact row — a centred inline list of short facts under a heading. */
.fact-row {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.5rem 1.75rem;
}
.fact-row li { display: flex; align-items: center; gap: 0.5rem; font-size: var(--fs-small); }
.fact-row li::before { content: ""; width: 4px; height: 4px; border-radius: 50%; background: var(--c-lagoon); flex-shrink: 0; }

/* ---- Room USP strip ------------------------------------------------------
   The four facts a room is actually chosen on — size, occupancy, bed and
   bathroom — one icon each, directly under the room's heading. Built on the
   same divided-panel construction as .stats so the two read as one family;
   the icon is the only thing that distinguishes it. */
.usp {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--c-line);
  border: var(--border);
}
.usp__item {
  background: var(--c-lagoon-tint);
  text-align: center;
  padding: clamp(1.25rem, 2.4vw, 2rem) 1rem;
}
.usp__item svg {
  width: 30px; height: 30px;
  margin: 0 auto 0.875rem;
  color: var(--c-lagoon);
}
.usp__value {
  display: block;
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  line-height: 1.2;
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
.usp__label {
  display: block;
  margin-top: 0.5rem;
  font-size: var(--fs-caption);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-text);
}
@media (min-width: 600px) { .usp { grid-template-columns: repeat(4, 1fr); } }

/* ---- Contact page: enquiry band -----------------------------------------
   The band sits on the plain white page ground, so the form card is held by
   its border rather than by a fill, and the details panel takes the deep
   ocean of the footer. Both treatments already exist in the system, so
   nothing new is invented. */

/* The two columns are opaque panels sitting side by side, so they must share
   a bottom edge. `align-items: start` left each one only as tall as its own
   content, which is what produced the ragged step between the white form card
   and the dark reservations panel. They stretch instead, and the panels
   themselves flex to fill their column. */
.contact-band { align-items: stretch; }
@media (min-width: 1024px) { .contact-band { grid-template-columns: 1.08fr 1fr; } }

.contact-card {
  background: var(--c-white);
  border: var(--border);
  padding: clamp(1.25rem, 2.6vw, 2.25rem);
}
/* Fields take the cream ground so they read as inputs against the white card,
   then lift to white on focus. */
.contact-card .field input,
.contact-card .field textarea { background: var(--c-bg-alt); }
.contact-card .field input:focus,
.contact-card .field textarea:focus { background: var(--c-white); }
.contact-card .btn-row { margin-top: 0.5rem; }
.contact-card .btn-row .btn { width: 100%; justify-content: center; }
@media (min-width: 600px) { .contact-card .btn-row .btn { width: auto; } }

.contact-panel {
  background: var(--c-ocean-800);
  color: rgba(255, 255, 255, 0.78);
  padding: clamp(1.5rem, 2.9vw, 2.5rem);
}
.contact-panel > h3 {
  font-family: var(--ff-text);
  font-size: var(--fs-h6);
  font-weight: var(--fw-semi);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-white);
}

.contact-methods { margin-top: var(--gap-heading-para); }
.contact-methods li {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-icon-text);
  padding: 0.9375rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.contact-methods li:first-child { border-top: 0; padding-top: 0; }
.contact-methods svg {
  width: 20px; height: 20px;
  flex-shrink: 0;
  margin-top: 0.2rem;
  color: var(--c-accent);
}
.contact-methods__label {
  display: block;
  font-size: var(--fs-caption);
  line-height: 1.4;
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
}
.contact-methods__value {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  color: var(--c-white);
  word-break: break-word;
}
a.contact-methods__value { transition: color var(--dur-fast) var(--ease); }
a.contact-methods__value:hover { color: var(--c-accent); }

.contact-times {
  margin-top: var(--gap-para-para);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  border: 1px solid rgba(255, 255, 255, 0.14);
}
.contact-times > div { padding: 0.875rem 0.75rem; text-align: center; }
.contact-times > div + div { border-left: 1px solid rgba(255, 255, 255, 0.14); }
.contact-times dt {
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-overline);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
}
.contact-times dd {
  margin: 0.25rem 0 0;
  font-family: var(--ff-display);
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  color: var(--c-white);
}

.contact-panel__note {
  margin-top: var(--gap-para-para);
  font-size: var(--fs-small);
  line-height: var(--lh-tight);
  color: rgba(255, 255, 255, 0.58);
}
.contact-panel .btn-row { margin-top: var(--gap-para-btn); }
.contact-panel .btn-row .btn { width: 100%; justify-content: center; }

/* Location page: the map is that page's principal visual, so it runs wider
   than tall from tablet up. The container is fluid with no max-width, so a
   pure aspect-ratio keeps growing on wide monitors — 16/9 reached ~950px of
   map at 1920, which is why it needs capping.
   A max-height cannot do that job: paired with aspect-ratio on an auto-width
   block it shrinks the WIDTH to keep the ratio, pulling the map off the
   container edges. From laptop up the height is simply stated outright. */
.map-embed--tall { aspect-ratio: 4 / 3; }
@media (min-width: 768px)  { .map-embed--tall { aspect-ratio: 16 / 9; } }
@media (min-width: 1024px) { .map-embed--tall { aspect-ratio: auto; height: 32rem; } }

/* ---- Holiday package card -------------------------------------------------
   Duration on top, then the route as one row per island with its nights. No
   photograph: these are itineraries, and the property has no picture of a
   package. Built on the tinted card ground the icon tiles already use, so
   nothing new is introduced. */
.pkg {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  /* Tighter than --card-pad. That token is sized for cards carrying a
     photograph and a paragraph; these hold three short rows, and at five
     across the padding was eating more of the card than the content. */
  padding: clamp(1rem, 1.6vw, 1.375rem);
  background: var(--c-white);
  border: var(--border);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.pkg:hover { border-color: var(--c-lagoon); box-shadow: 0 14px 34px rgba(7, 43, 51, 0.1); }

/* There are five itineraries, and from 1536px up they all fit on one line.
   On the standard --3 ladder they break 3 + 2, and the orphan pair reads as
   a mistake rather than a row. Only the packages grid takes this: it is the
   one place on the site with exactly five peers. Below 1536px the normal
   1 -> 2 -> 3 ladder is untouched, so the cards stay responsive as before.
   Declared after the ladder's 1024px rule so it wins at equal specificity. */
@media (min-width: 1536px) {
  .grid--pkg { grid-template-columns: repeat(5, 1fr); }
}

/* Five across needs a tighter gutter than the site's card rhythm. --gap-card
   is 48px, tuned for two or three wide cards with photographs; at five it was
   taking 192px of the row and squeezing every card. Half that gives the width
   back to the cards, which is where it is wanted. */
.grid--pkg { gap: 1.5rem; }
@media (min-width: 1536px) { .grid--pkg { gap: 1.25rem; } }

/* The system's heading rule at card scale, as the experience cards use. */
.pkg::before {
  content: "";
  position: absolute;
  top: -1px; left: -1px;
  width: 56px; height: 3px;
  background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
}

.pkg__nights {
  font-family: var(--ff-display);
  font-size: var(--fs-h2);
  font-weight: var(--fw-display);
  line-height: 1.1;
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
/* The days ride quieter than the nights: the nights are what is being sold. */
.pkg__nights span {
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-muted);
}

/* The route reads as a JOURNEY, not a table: a dot per island joined by a
   line down the card. The rows used to be hairline label/value pairs, which
   made a two-island package look like a half-empty table beside a
   three-island one. */
.pkg__route {
  margin-top: 1rem;
  padding-left: 1.25rem;
}
.pkg__route li {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  /* see .pkg-card__body .pkg__route li — tighter inside the photo cards */
  font-size: var(--fs-small);
  color: var(--c-ink);
}
/* The stop. */
.pkg__route li::before {
  content: "";
  position: absolute;
  left: -1.25rem;
  top: 1.05em;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--c-white);
  box-shadow: inset 0 0 0 2px var(--c-lagoon);
}
/* The leg between stops — never drawn after the last one. */
.pkg__route li::after {
  content: "";
  position: absolute;
  left: calc(-1.25rem + 3px);
  top: 1.6em;
  bottom: -0.4em;
  width: 1px;
  background: var(--c-line);
}
.pkg__route li:last-child::after { display: none; }
.pkg__route li:last-child::before { box-shadow: inset 0 0 0 2px var(--c-accent); }

.pkg__n {
  flex-shrink: 0;
  font-weight: var(--fw-semi);
  font-variant-numeric: tabular-nums;
  color: var(--c-accent);
}

/* Pinned to the foot, so every card ends on the same line however many
   islands it visits — which is what closed the gap under the short ones. */
.pkg__foot {
  margin-top: auto;
  padding-top: 1rem;
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-muted);
}


/* ---- Holiday package card (photograph-led) --------------------------------
   The holiday-packages page's own card. Distinct from .pkg above, which is
   the compact five-across summary the experiences page carries: that one is
   a route list with no image, this one is the reference's photograph-led
   card — media on top, name, duration, route, then one CTA.

   Both share .pkg__route, so the journey renders identically in either
   place and a route edited in one reads the same in the other. */
.pkg-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--c-white);
  border: var(--border);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.pkg-card:hover { border-color: var(--c-lagoon); box-shadow: 0 14px 34px rgba(7, 43, 51, 0.1); }

/* The system's heading rule at card scale, as .pkg and the experience cards
   carry. Sits over the media, so it reads on the plate as well as on a
   photograph once one lands. */
.pkg-card::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: -1px; left: -1px;
  width: 56px; height: 3px;
  background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
}

/* 16:10, the same frame .card__media uses — a package photograph and a
   facilities photograph should not crop differently. */
.pkg-card__media {
  position: relative;
  /* 3:2 rather than 16:10 — a shallower crop of the same photograph, which
     is ~40px less card height at desktop and loses nothing but sky. */
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--c-line);
}
.pkg-card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 600ms var(--ease); }
.pkg-card:hover .pkg-card__media img { transform: scale(1.04); }

.pkg-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--card-pad);
}

/* The duration is no longer set in the body — it is the tag on the photograph
   (.pkg-card__tag, below), which is a row of body height saved on every card. */

/* The shared route, hairline-separated from the name above it. */
.pkg-card__body .pkg__route {
  /* Was --gap-heading-para (24px) on both: 48px of rule-and-air for a list of
     two or three single lines. Halved on each side; the hairline still
     separates the route from the name above it. */
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  padding-left: 1.25rem;
  border-top: var(--border);
}
/* One line of text does not need 44px of row. */
.pkg-card__body .pkg__route li { padding: 0.3125rem 0; }
.pkg-card__body .pkg__route li::before { top: 0.85em; }
.pkg-card__body .pkg__route li::after { top: 1.4em; bottom: -0.25em; }

/* The islands count sits with the route now rather than pinned to the card
   foot, where it cost a row PLUS all the slack above it on the short cards. */
.pkg-card__foot {
  margin-top: 0;
  padding-top: 0.5rem;
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-muted);
}

/* ---- Duration tag on the photograph --------------------------------------
   The duration is the card's headline fact, so it rides on the image as a tag
   instead of taking a row in the body. That is two rows of body height back
   (the line itself and the gap under it) on every card.

   This is the ONE place text sits over a photograph, which the design system
   otherwise forbids. It earns it the way the room counter pill does: an opaque
   ground, not a wash over the image, so the text never depends on what is in
   the photograph behind it. */
.pkg-card__tag {
  position: absolute;
  z-index: 1;
  left: 0;
  bottom: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 0.375rem;
  padding: 0.5rem 0.875rem;
  background: var(--c-brand);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* The days ride quieter than the nights: the nights are what is being sold. */
.pkg-card__tag span {
  font-weight: var(--fw-body);
  opacity: 0.82;
}
.pkg-card__body > .btn { margin-top: var(--gap-para-btn); align-self: flex-start; }

/* Five cards on a --3 ladder break 3 + 2. Unlike .pkg there is no 5-across
   rescue here, because a photograph-led card at a fifth of the row is too
   narrow to carry an image: the trailing pair is the correct outcome and it
   reads as a row, not an orphan, because these cards are wide. */
.grid--pkg-cards { gap: var(--gap-card); }
/* Two CTAs per package card. The site button carries up to 50px of side
   padding, tuned for a button standing on its own in a section; two of those
   will not sit beside each other inside a card, so within .pkg-card__cta the
   pair goes full width and stacks, and the padding is trimmed to match.

   Book Now is --primary and sits first: it is the action the page exists for.
   Enquire Now is --outline beneath it, for the trip that needs a quote before
   it can be booked. */
.pkg-card__cta {
  /* margin-top:auto pins the pair to the card foot so all five align; the
     value itself is the minimum clearance, not the 32px section token. */
  margin-top: auto;
  padding-top: 1.125rem;
  display: grid;
  gap: 0.5rem;
}
.pkg-card__cta .btn {
  width: 100%;
  padding-inline: 1rem;
}

/* From 480px the card is wide enough to take them side by side, which keeps
   the card shorter without crowding either label. Below that they stay
   stacked, where a 44px target matters most. */
@media (min-width: 480px) {
  .pkg-card__cta { grid-template-columns: 1fr 1fr; }
}
/* The 3-up grid narrows each card again, so revert to stacked while three sit
   in a row, and go back to the pair once there is room at 1280px. */
@media (min-width: 1024px) and (max-width: 1279.98px) {
  .pkg-card__cta { grid-template-columns: 1fr; }
}

/* ---- Package enquiry form -------------------------------------------------
   The form is the whole point of the page, so it sits on the tinted card
   ground the site already uses for content that must read as a distinct
   block, rather than floating on white. */
.enq-wrap {
  background: var(--c-lagoon-tint);
  padding: clamp(1.25rem, 3.2vw, 2.75rem);
}
/* The hint under the submit row runs the full width of the form grid. */
.form--enquiry .field__hint.field--full { grid-column: 1 / -1; margin-top: 0.25rem; }


/* The plate fills its frame and any photograph lies over it, exactly as the
   experience blocks do — so a file that has not been supplied removes itself
   and the plate shows through rather than a broken image. */
.pkg-card__media .photo-plate { position: absolute; inset: 0; }
.pkg-card__media .photo-plate__img { position: absolute; inset: 0; }
/* ---- Experience list ------------------------------------------------------
   Six alternating image/copy blocks inside ONE section, so the page carries
   one set of section paddings rather than six. The rhythm between blocks is
   the section token, keeping it identical to section-to-section spacing
   elsewhere. Alternation is handled by .split--reverse, already in the system. */
.exp-list { display: grid; gap: var(--section-y); }

/* Slider inside an experience block. */
.exp-media .slider__viewport { aspect-ratio: 4 / 3; }
@media (min-width: 1024px) { .exp-media .slider__viewport { aspect-ratio: 3 / 2; } }

/* Photography pending — an editorial title plate, NOT a photograph and never
   dressed as one. Every experience currently uses this because the property
   has no photograph of any activity; each is a slot waiting for a real image.
   Delete the plate slide and drop an <img> in its place. */
.photo-plate {
  width: 100%; height: 100%;
  display: grid;
  place-content: center;
  gap: 0.875rem;
  text-align: center;
  padding: var(--card-pad);
  background: var(--c-lagoon-tint);
  background-image:
    repeating-linear-gradient(135deg,
      rgba(31,122,140,0.05) 0 12px, transparent 12px 24px);
}
.photo-plate__name {
  font-family: var(--ff-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
.photo-plate__rule {
  width: 48px; height: 2px; border: 0; margin: 0 auto;
  background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
}
.photo-plate__note {
  font-size: var(--fs-caption);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-muted);
}

/* The photograph lies over its own plate rather than replacing it, so the
   plate is the fallback: a file that has not been supplied yet removes itself
   (data-fallback, site.js) and the plate underneath shows through. No broken
   image ever reaches the page while the photography is being gathered. */
.exp-media .slider__slide { position: relative; }
.photo-plate__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Grey pending-photography plate ------------------------------------------
   The tinted .photo-plate reads as brand furniture; a slot that is only
   waiting for a file should read as neutral and obviously provisional, so
   this variant drops to grey. Placeholder only — delete the modifier, or
   the whole plate, when the photograph arrives. */
.photo-plate--grey {
  background: #E9E9E9;
  background-image:
    repeating-linear-gradient(135deg,
      rgba(0, 0, 0, 0.045) 0 12px, transparent 12px 24px);
}
.photo-plate--grey .photo-plate__name { color: #5A5C5B; }
.photo-plate--grey .photo-plate__note { color: #8A8C8B; }
.photo-plate--grey .photo-plate__rule { background: #B9BBBA; }

/* Rate list inside an experience block. */
.rate-list { display: grid; gap: 0; border-top: var(--border); }
.rate-list > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: var(--border);
}
.rate-list dt { font-size: var(--fs-small); color: var(--c-ink); }
.rate-list dd {
  margin: 0; font-size: var(--fs-small); font-weight: var(--fw-medium);
  color: var(--c-accent); white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* Names-only variant. With the rates gone the list is no longer a set of
   term-and-definition pairs, so it is a plain <ul>; this gives the <li> the
   same ruled rhythm the <div> rows had, and it keeps the .rate-list class so
   the two-column rule below still applies. */
/* No `margin: 0` here. The shorthand ALSO zeroes margin-top, and this list
   carries .mt-content to clear the paragraph above it — same specificity
   (0,1,0), but components.css loads after design-system.css, so the reset won
   and the list rode up into the last line of the paragraph. The global
   `ul, ol` reset already handles list margin and padding, and `li` is already
   list-style: none, so none of this needed restating. */
.rate-list--plain { list-style: none; }
.rate-list--plain > li {
  padding: 0.5rem 0;
  border-bottom: var(--border);
  font-size: var(--fs-small);
  color: var(--c-ink);
}

@media (min-width: 600px) { .rate-list { grid-template-columns: repeat(2, 1fr); column-gap: var(--gap-card); } }

/* Stat strip, compact variant — for values that are phrases rather than
   figures (meal times), where the display H1 size would wrap. */
.stats--compact .stat__value { font-size: var(--fs-h3); line-height: 1.2; }

/* Side-by-side only: each column becomes a flex column so its panel can grow
   to the height of the taller one. Below 1024px they stack, where equal
   heights would mean nothing. */
@media (min-width: 1024px) {
  .contact-band > div { display: flex; flex-direction: column; }
  .contact-band .contact-card,
  .contact-band .contact-panel { flex: 1 1 auto; }
}

/* ---- Contact strip --------------------------------------------------------
   Four light, actionable tiles in a row, above the form. This replaces the
   dark slab that used to sit beside the form: two heavy columns side by side
   put the whole page in front of the reader at once. Split top-to-bottom, the
   page reads "how to reach us" then "write to us". */
/* Stacked on a phone these are four tiles in a column, not four cards across
   a row — the full card gap between them reads as the page falling apart.
   They take a tight gap while stacked and the standard one once they sit
   side by side. */
.contact-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 600px)  { .contact-grid { gap: var(--gap-card); grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .contact-grid { grid-template-columns: repeat(4, 1fr); } }

/* Tinted, not white: on a white ground a white box with a hairline border is
   an empty rectangle. The lagoon tint is the system's own card body colour,
   and the brand gradient along the top edge is the same mark .rule carries. */
.contact-tile {
  position: relative;
  display: grid;
  gap: 0.5rem;
  align-content: start;
  padding: var(--card-pad);
  background: var(--c-lagoon-tint);
  height: 100%;
}
.contact-tile::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--c-lagoon) 0%, var(--c-accent) 100%);
}
.contact-tile svg { width: 20px; height: 20px; color: var(--c-accent); }
.contact-tile__label {
  font-size: var(--fs-caption);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-brand);
}
.contact-tile__value {
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--c-ink);
  line-height: 1.5;
}
a.contact-tile__value:hover { color: var(--c-accent); }

/* Slim meta line under the strip — times and the reception note, inline. */
.contact-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.5rem clamp(1rem, 2.2vw, 2rem);
  text-align: center;
}
.contact-meta li { font-size: var(--fs-small); color: var(--c-text); }
.contact-meta li strong { color: var(--c-ink); font-weight: var(--fw-medium); }

/* The form gets the page to itself. Wider than a reading measure because the
   fields now sit three across — at 760px the three columns were too narrow
   to type a date into. */
.contact-form-wrap { max-width: 1040px; margin-inline: auto; }

/* ---- Room summary card (simplified) ---------------------------------------
   One photograph, name, a short paragraph, three icon facts and one action.
   No slider, no thumbnails, no rate and no spec table: the summary page is a
   choice between two rooms, and the detail page carries the full picture. */
.room__photo { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

.room__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem clamp(1rem, 2vw, 1.75rem);
  margin-top: var(--gap-heading-para);
  padding-top: var(--gap-heading-para);
  border-top: var(--border);
}
.room__facts li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--fs-small);
  color: var(--c-ink);
}
.room__facts svg {
  width: 18px; height: 18px;
  flex: 0 0 auto;
  color: var(--c-accent);
}
.room__body > .btn { margin-top: auto; align-self: flex-start; }
.room__body > .room__facts + .btn { margin-top: var(--gap-para-btn); }

@media (min-width: 1280px) {
  .room .room__photo { height: 100%; aspect-ratio: auto; min-height: clamp(15rem, 33vh, 24rem); }
}

/* ---- room-card row alignment ----------------------------------------------
   The two summary cards must line up row for row. "Deluxe Room with Bathtub"
   wraps to two lines where "Deluxe Room" does not, which pushed that card's
   paragraph, facts and button down relative to its neighbour.

   Two rules do it: the heading reserves two lines in both cards, and the body
   becomes a 4-row grid whose slack is given to the PARAGRAPH row — so the
   facts and the button are pushed to the foot of the card and align across
   the pair regardless of how long the copy runs. */
@media (min-width: 600px) {
  .room__body {
    display: grid;
    grid-template-rows: auto 1fr auto auto;   /* name | copy | facts | action */
    align-content: stretch;
  }
  .room__body > h2 { min-height: calc(2em * var(--lh-h2)); }
}

/* ---- Booking bar: desktop and tablet only ---------------------------------
   Removed from the phone view at client request. Below 768px the fixed action
   bar (call / WhatsApp / book) already carries the booking path, and a
   five-field form stacked on a phone is a poor substitute for it. Every CTA
   that used to jump to this section now goes straight to the booking engine,
   so nothing dead-ends where the bar is hidden. */
@media (max-width: 767px) {
  .section--booking { display: none; }
  /* #about only takes the short top padding to sit under the booking bar.
     With the bar hidden it follows the hero directly, so it gets the full
     section spacing back. */
  #about { padding-top: var(--section-y); }
}

/* ---- Drawer: edge-to-edge on phones ---------------------------------------
   The drawer is min(88vw, 380px) so that on tablets a sliver of the page shows
   behind it and the overlay reads as temporary. On a phone that sliver is just
   a dead strip, so the panel runs the full width instead. */
@media (max-width: 599px) {
  .drawer { width: 100%; }
}

/* ---- Card grid → swipeable row on phones -----------------------------------
   A three-up grid stacked on a phone becomes a very long column. This turns it
   into a scroll-snap carousel instead: native touch scrolling, no JS.

   ONE CARD AT A TIME. The row used to show a 72vw card with its neighbours
   peeking either side, scaled to 0.94 and dimmed. On a 390px phone that left
   roughly 280px of usable card, which the testimonial quotes overran, and the
   scaled neighbours made three equal boxes read as three different sizes.
   A full-width card fixes both: nothing is cropped, and there is no second
   card on screen to be unequal to. */
@media (max-width: 599px) {
  .grid--slide-m {
    display: flex;
    grid-template-columns: none;
    gap: 0.75rem;
    align-items: stretch;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .grid--slide-m::-webkit-scrollbar { display: none; }

  .grid--slide-m > * {
    flex: 0 0 100%;
    scroll-snap-align: center;
    /* Every card takes the height of the tallest in the row: stretch does
       that, but `.testimonial` sets height:100%, which against an auto-height
       flex parent is indeterminate and cancels it. */
    height: auto;
    align-self: stretch;
  }

  /* Dots, not arrows. One card fills the phone, so what the reader needs to
     know is how many cards there are and where they are in them — which two
     arrow buttons cannot say. Each dot is a full 44px touch target with the
     mark drawn inside it, so the target is thumb-sized while the dot is not. */
  .mslider__dots {
    display: flex;
    justify-content: center;
    gap: 0.25rem;
    margin-top: 1rem;
  }
  .mslider__dots button {
    width: 26px;
    height: 44px;
    display: grid;
    place-items: center;
    background: none;
  }
  .mslider__dots button::before {
    content: "";
    display: block;
    width: 8px;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--c-line);
    transition: width var(--dur-fast) var(--ease),
                background-color var(--dur-fast) var(--ease);
  }
  .mslider__dots button[aria-current="true"]::before {
    width: 22px;
    background: var(--c-accent);
  }

  /* On the parallax band the hairline grey would disappear into the photograph. */
  .on-dark .mslider__dots button::before { background: rgba(255, 255, 255, 0.4); }
  .on-dark .mslider__dots button[aria-current="true"]::before { background: #fff; }
}

/* The wrapper is injected by script AT EVERY WIDTH, which breaks the
   `.section-head + .grid` adjacency that used to supply the gap under a
   heading — the grid is no longer the heading's next sibling, the wrapper is.
   So this rule belongs outside the phone media query it was written in:
   scoped there, the cards collided with the heading on every screen 600px
   and wider, which is where the grid is a grid rather than a carousel. */
.section-head + .mslider,
.section-headRow + .mslider { margin-top: var(--gap-heading-cards); }

/* Phone-only affordance: above 600px the row is a grid again. */
@media (min-width: 600px) {
  .mslider__dots { display: none; }
}

/* ---- Image-slider dots ----------------------------------------------------
   Generated by script for any slider authored without them. They sit beneath
   the image on a light ground, so they take the brand palette rather than the
   hero's white-on-photograph treatment. As with the hero dots, the visible dot
   is a pseudo-element inside a 44px target, so the global coarse-pointer rule
   `button { min-height: 44px }` cannot stretch it into a bar. */
/* OFF at every width. These were generated for image sliders so a phone had
   an indicator once the arrows were hidden, but every slider that gets them
   already says where you are twice over — the "1 / 5" counter on the frame,
   and a thumbnail strip underneath on the room galleries. A third row of
   marks between the two was just noise. The rules below are kept so the
   component can be switched back on by deleting one line. */
.slider__dots { display: none; }
.slider__dots--on {
  display: flex;
  justify-content: center;
  gap: 0.125rem;
  margin-top: 0.75rem;
}
.slider__dots button {
  width: 26px; height: 44px; min-height: 0;
  padding: 0; border: 0; background: none;
  display: grid; place-items: center; cursor: pointer;
}
.slider__dots button::before {
  content: "";
  display: block;
  width: 8px; height: 8px;
  border-radius: var(--radius-pill);
  background: var(--c-lagoon-tint);
  box-shadow: inset 0 0 0 1px var(--c-lagoon);
  transition: width var(--dur) var(--ease), background var(--dur) var(--ease);
}
.slider__dots button[aria-current="true"]::before {
  width: 22px; background: var(--c-accent); box-shadow: none;
}
.on-dark .slider__dots button::before { background: rgba(255,255,255,0.4); box-shadow: none; }
.on-dark .slider__dots button[aria-current="true"]::before { background: #fff; }

@media (max-width: 599px) {
  /* The arrows STAY on a phone. They used to come off here because the dots
     took over as the way through a slider — with the dots gone, hiding the
     arrows too would leave an image slider with no control at all, since
     these tracks are transform-driven and do not answer a swipe.
     44px exactly: the touch-target minimum, and square, so the disc is a
     circle rather than the oval a 38px width stretched to a 44px height
     produced. This also overrides the 38px discs the room cards use, which
     is where the squeeze showed worst. */
  .slider__viewport .slider-arrow,
  .room .slider-arrow {
    width: 44px;
    height: 44px;
  }
  .slider__viewport .slider-arrow::before,
  .room .slider-arrow::before { width: 15px; }
  .slider__viewport .slider-arrow::after,
  .room .slider-arrow::after { width: 7px; height: 7px; }
  .slider__viewport .slider-arrow--prev,
  .room .slider-arrow--prev { left: 0.5rem; }
  .slider__viewport .slider-arrow--next,
  .room .slider-arrow--next { right: 0.5rem; }

  /* Two short lines centred, reading as fine print rather than a broken row. */
  .footer__bottom { flex-direction: column; gap: 0.375rem; padding-block: 1.25rem; }
}

/* ---- Footer menu ----------------------------------------------------------
   One list carrying every page, replacing the old Explore / Guest Information
   split. Ten items is a long single column, so it runs two-up on every screen
   and the column it lives in takes the extra width on desktop. */
.footer__menu {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.625rem 1.5rem;
}
.footer__menu li + li { margin-top: 0; }
@media (min-width: 1024px) {
  .footer__cols { grid-template-columns: 1.6fr 1fr; }
}

/* ---- Inner-page hero: taller on phones -------------------------------------
   3/2 gave only ~260px at 390 wide, which read as a thin strip. Height is
   viewport-relative rather than another ratio so it scales with the phone
   instead of with its width, and it is clamped at both ends so short handsets
   are not swamped. Covers both inner-page variants: .hero--band is the same
   band as .hero--still with a slider in it, and leaving it out left Experiences
   on the old 3/2 at ~250px while every other inner page stood at ~468px. The
   homepage hero is the separate 100svh rule and is deliberately untouched. */
@media (max-width: 599px) {
  .hero--still .slider__viewport,
  .hero--band .slider__viewport {
    aspect-ratio: auto;
    height: clamp(20rem, 52svh, 32.5rem);   /* 320 - 439@844 - 520 */
  }
}

/* ==========================================================================
   FLOATING DOCK — bottom-right cluster
   Studied from dabayinn.in, which stacks a Why Book Direct panel, a chat
   launcher, WhatsApp, call and back-to-top in the bottom-right corner. The
   BEHAVIOUR is reproduced; none of the markup or copy is. Their offers are
   theirs — every line in our panel is a fact from this property.
   ========================================================================== */

.dock {
  position: fixed;
  right: clamp(0.75rem, 1.6vw, 1.25rem);
  bottom: clamp(0.75rem, 1.6vw, 1.25rem);
  z-index: var(--z-dock, 60);
  display: flex;
  /* Panel opens to the LEFT of the rail, bottom-aligned with it. Stacked above
     the rail it had only the strip between the rail and the header, which on
     a short screen left almost no room; beside it, it gets the full height. */
  flex-direction: row;
  align-items: flex-end;
  gap: 0.5rem;
}
/* Rail is 48px + the 0.5rem gap + the dock's own right offset; the panel takes
   what is left of the viewport so it never runs off the left edge. */
.dock .dock__panel { width: min(20rem, calc(100vw - 48px - 0.5rem - 2 * clamp(0.75rem, 1.6vw, 1.25rem))); }

/* ---- Why Book Direct — vertical tab, left edge --------------------------
   The reference hangs this off the left edge as a rotated tab rather than
   putting it in the corner cluster: it is an offer, not a way to reach the
   desk, and on its own edge it never widens the contact rail. The panel opens
   beside the tab, not over it. */
/* Bottom-left, so it sits level with the contact cluster in the opposite
   corner and the two read as one row of controls across the foot of the page
   rather than one floating halfway up the side. */
.direct {
  position: fixed;
  left: 0;
  bottom: clamp(0.75rem, 1.6vw, 1.25rem);
  z-index: var(--z-dock, 60);
  display: flex;
  align-items: flex-end;
}

.direct__tab {
  writing-mode: vertical-rl;
  padding: 1.5rem 0.6rem;
  border: 0;
  background: var(--c-accent);
  color: #fff;
  cursor: pointer;
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  box-shadow: 0 6px 18px rgba(7, 43, 51, 0.22);
  transition: background-color var(--dur-fast) var(--ease);
}
.direct__tab:hover { background: var(--c-accent-dark); }

.direct .dock__panel { margin-left: 0.5rem; }

/* Off below 768px. A rotated tab pinned to the left edge has nowhere to go on
   a phone — it sits over the page rather than beside it, and in the footer it
   covered the email address. Same breakpoint the booking bar leaves mobile at.
   The phone-only tab tuning that used to live here went with it: it could
   never apply once the tab stops rendering. The markup stays, so the tab is
   unchanged from 768px up. */
@media (max-width: 767px) {
  .direct { display: none; }
}

/* ---- The rail of buttons ------------------------------------------------ */
.dock__rail {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}

.dock__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 48px;
  height: 48px;
  min-height: 48px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--c-brand);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(7, 43, 51, 0.22);
  transition: background-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.dock__btn:hover { background: var(--c-ocean-800); transform: translateY(-1px); }
.dock__btn svg { width: 22px; height: 22px; flex: 0 0 auto; }

/* The one labelled button. It is the offer, so it carries the CTA colour and
   the only words in the rail. */
.dock__btn--direct {
  width: auto;
  padding-inline: 1rem;
  background: var(--c-accent);
  font-family: var(--ff-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semi);
  letter-spacing: var(--ls-btn);
  text-transform: uppercase;
  white-space: nowrap;
}
.dock__btn--direct:hover { background: var(--c-accent-dark); }

.dock__btn--wa { background: #25D366; }
.dock__btn--wa:hover { background: #1DA851; }

/* Back to top only exists once there is a page behind you. */
.dock__btn--top { background: var(--c-white); color: var(--c-brand); border-color: var(--c-line); }
.dock__btn--top:hover { background: var(--c-bg-alt); }

/* ---- The panels --------------------------------------------------------- */
.dock__panel {
  width: min(20rem, calc(100vw - 2rem));
  background: var(--c-white);
  border: var(--border);
  box-shadow: 0 18px 45px rgba(7, 43, 51, 0.22);
  /* The panel is bottom-anchored and used to have no height limit, so on a
     short screen it simply grew up past the header and off the top. It now
     sits beside the rail rather than above it, so it may use everything below
     the header. Capped here and scrolled inside instead, so the panel is
     always whole and always reachable. */
  max-height: calc(100vh - var(--header-h) - 3rem);
  max-height: calc(100svh - var(--header-h) - 3rem);
  display: flex;
  flex-direction: column;
}
.dock__panel[hidden] { display: none; }
/* The head stays put; only the list under it scrolls. */
.dock__panel .dock__body { overflow-y: auto; overscroll-behavior: contain; }
.dock__panel .dock__panel-head { flex-shrink: 0; }

.dock__panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.875rem 0.75rem 0.875rem 1.125rem;
  background: var(--c-ocean-800);
  color: #fff;
}
.dock__panel-head h2 {
  font-family: var(--ff-text);
  font-size: var(--fs-small);
  font-weight: var(--fw-semi);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #fff;
  margin: 0;
}
.dock__panel-head p {
  margin: 0.15rem 0 0;
  font-size: var(--fs-caption);
  color: rgba(255, 255, 255, 0.7);
  text-transform: none;
  letter-spacing: 0;
}
.dock__close {
  width: 32px; height: 32px; min-height: 32px;
  display: grid; place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  color: #fff;
  font-size: 1.25rem;
  line-height: 1;
}
.dock__close:hover { background: rgba(255, 255, 255, 0.14); }

.dock__body { padding: 1.125rem; }

.dock__list { display: grid; gap: 0.75rem; }
.dock__list li {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--c-text);
}
.dock__list svg {
  width: 16px; height: 16px;
  flex: 0 0 auto;
  margin-top: 0.25em;
  color: var(--c-lagoon);
}
.dock__body .btn { width: 100%; margin-top: 1.125rem; }

/* Chat panel: a way to reach a person, not a robot pretending to be one. */
.dock__ways { display: grid; gap: 0.5rem; }
.dock__way {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  border: var(--border);
  font-size: var(--fs-small);
  color: var(--c-ink);
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}
.dock__way:hover { border-color: var(--c-lagoon); background: var(--c-bg-alt); color: var(--c-brand); }
.dock__way svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--c-lagoon); }
.dock__way span { display: block; }
.dock__way small { display: block; font-size: var(--fs-caption); color: var(--c-muted); }

@media (max-width: 599px) {
  .dock__btn--direct span { display: none; }
  .dock__btn--direct { width: 48px; padding-inline: 0; }
}

/* ---- Footer contact list --------------------------------------------------
   Icon + value rows. The icons need an explicit size: an inline SVG with only
   a viewBox scales to fill its container, which rendered them several hundred
   pixels tall across the footer. */
.footer__contact { display: grid; gap: 0.875rem; }
.footer__contact li {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  font-size: var(--fs-small);
  line-height: 1.6;
}
.footer__contact li + li { margin-top: 0; }
.footer__contact svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  margin-top: 0.28em;
  /* White, not terracotta. These are labels for the line beside them, not
     calls to action, and the accent was the only warm colour in an otherwise
     white-on-ocean footer. */
  color: #fff;
}
.footer__contact a { color: rgba(255,255,255,0.72); }
.footer__contact a:hover { color: #fff; }

/* ---- Mobile header order --------------------------------------------------
   Hamburger, then logo, then Book Now. The toggle is markup-nested inside
   .nav__actions, so `order` alone could not lift it out; `display: contents`
   dissolves that wrapper on mobile and makes the three controls direct flex
   children of .nav, which can then be ordered freely. No markup change, so it
   applies to every page at once.

   Spacing is measured to what the eye sees, not to the box. The 30px page
   padding puts the toggle's 44px touch target at the edge, but its bars are
   only 22px wide and centred, so they sit 11px in; -11px margin pulls them
   back to the 30px line and the flex gap drops to 19px so the visible bar ->
   logo distance is also 30px. Book Now has a real border, so its box edge is
   its visible edge and the page padding alone gives it 30px. */
@media (max-width: 1023px) {
  .nav {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 1.1875rem;                 /* 19px box gap = 30px optical */
  }
  .nav__actions { display: contents; }
  /* .nav__list and .nav__more both dissolve to display:contents (see the
     max-width:1023px block in section 2), so the button below is a direct flex
     child of .nav and the -11px optical pull lands on the right box. */
  .nav__more   { display: contents; }
  .nav__toggle { order: 1; margin-right: 0; margin-left: -11px; }
  .nav__logo   { order: 2; }
  .nav__tel    { order: 3; }
  .nav .btn    { order: 4; margin-left: auto; }
}

/* The open drawer repeats the header it covers: close where the hamburger
   was, then logo, then Book Now, each landing on the same x. The drawer is
   only ever opened below 1024px — from 1024px the same burger opens the
   overflow panel instead (see site.js) — so these need no media query. */
.drawer__head {
  justify-content: flex-start;
  gap: 1.1875rem;
  padding-inline: var(--page-padding);
}
.drawer__close { margin-left: -11px; }
.drawer__head .btn { margin-left: auto; }
/* Same height as .nav__logo img below 1024px, so the logo does not jump when
   the drawer opens over the header. */
.drawer__head img { height: 74px; }
.drawer__nav,
.drawer__foot { padding-inline: var(--page-padding); }

/* The three controls total more than a 320px phone can hold, and flex would
   otherwise pay for it out of the 44px touch targets - squeezing the burger
   off its 30px line. Pin the targets and the logo, and buy the room back from
   the button's generous side padding instead. Kept last so the 359px logo
   height wins over the unconditional one above. */
.nav__toggle,
.drawer__close,
.nav__logo,
.drawer__head img { flex: 0 0 auto; }

/* "Book Now" stays on one line. Left shrinkable, flex squeezed the button
   below its text width on small screens and the label wrapped to two lines. */
.nav .btn,
.drawer__head .btn { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 359px) {
  .nav .btn,
  .drawer__head .btn { padding-inline: 1.125rem; }
  .nav__logo img,
  .drawer__head img { height: 62px; }
}

/* ---- Site map list --------------------------------------------------------
   The site map is a plain index of pages, so it is a list of links and nothing
   else - no descriptions, no status pills, no widget columns. Child pages nest
   one level under their parent, which is the only structure the page needs. */
.sm-list,
.sm-list ul { list-style: none; margin: 0; padding: 0; }
.sm-list { max-width: 34rem; }
.sm-list a {
  display: block;
  padding: 0.9rem 0;
  border-bottom: var(--border);
  font-family: var(--ff-display);
  font-size: 1.25rem;
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-heading);
  color: var(--c-brand);
}
.sm-list a:hover { color: var(--c-accent); }
.sm-list a[aria-current="page"] { color: var(--c-accent); }
.sm-list ul { margin-left: 1.5rem; }
.sm-list ul a { font-size: 1.0625rem; color: var(--c-ink); }

/* ---- Webform integration: consent row + status ---------------------------
   New elements only (js/forms.js). Nothing above is altered. The consent row
   sits outside .field on purpose so the .field input sizing (and the coarse-
   pointer 44px min-height) does not inflate the checkbox. */
.ct-consent { display: flex; align-items: flex-start; gap: 0.625rem; cursor: pointer; }
.ct-consent input[type="checkbox"] { width: 1rem; height: 1rem; min-height: 0; flex: 0 0 auto; margin-top: 0.2rem; accent-color: var(--c-lagoon); }
.ct-consent span { font-size: var(--fs-small); color: var(--c-muted); }
.ct-status { padding: 0.75rem 1rem; border-radius: var(--radius); font-size: var(--fs-small); }
