@charset "utf-8";
/* ==========================================================================
   Shirgaonkar Beach Resort — site.css
   ONE stylesheet for the whole project. Every value below comes from
   DESIGN-SYSTEM.md. No component may introduce a value that is not a token.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS — the single place this site is re-skinned from
   -------------------------------------------------------------------------- */
:root{
  /* --- palette (DESIGN-SYSTEM.md §3) --- */
  --brand:#481C0A;
  --brand-deep:#2E1004;
  --accent:#C8873A;
  --accent-dk:#8F5D16;
  --accent-lt:#E3AE63;
  --ground:#FBF2E8;
  --surface:#FFFFFF;
  --ink:#2E1004;
  --ink-2:#6B4A36;
  --on-dark:#F7EADC;
  --rule:#9B7853;            /* UI edge on a LIGHT ground */
  --hair-soft:#E3D2BF;       /* decorative divider on a LIGHT ground, never a UI edge */
  --rule-dark:rgba(227,174,99,.42);   /* UI edge on a DARK band -- ONE alpha, not three */
  --hair-dark:rgba(247,234,220,.18);  /* decorative divider on a DARK band */
  --rule-accent:rgba(46,16,4,.22);    /* divider on the ochre fill */
  /* The eyebrow rule is DECORATION, so it is matched by perceived weight rather than by
     hex. --accent measures 2.72:1 on cream and 3.01:1 on white; --accent-lt measured
     7.26:1 on brown, which made the same 2px bar read as a much heavier line on the dark
     bands. This token holds it at ~2.7-3.1 on every ground. */
  --motif-ink:#C8873A;
  --motif-ink-dark:#9A6A32;
  --r-circle:50%;                     /* was undeclared and used 7 times */

  --scrim:linear-gradient(180deg,rgba(46,16,4,.10) 0%,rgba(46,16,4,.55) 55%,rgba(46,16,4,.82) 100%);

  /* --- type (DESIGN-SYSTEM.md §6) --- */
  --font-display:'DM Serif Display','Iowan Old Style',Georgia,'Times New Roman',serif;
  --font-body:'Plus Jakarta Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;

  --text-h1:clamp(44px,7vw,84px);
  /* The inner-page h1. The homepage h1 is the page's whole opening and owns 84px;
     an inner h1 sits in a half-width column under a breadcrumb and does not.
     50px is ONE step of the 1.375 display ratio above --text-h2 (36 x 1.375 = 49.5),
     so the inner pages get their own h1 size without inventing a second scale. */
  --text-h1-page:clamp(32px,3.7vw,50px); --lh-h1-page:1.08;
  --text-h2:clamp(27px,3vw,36px);
  --text-h3:clamp(21px,2.1vw,26px);
  --text-h4:clamp(17px,1.5vw,19px);
  /* Reading type is FIXED at every width. Size for reading is physiological, not
     proportional -- 16px is right at arm's length on a phone and on a desktop -- and a
     vw-scaled body also shortens the measure on wide screens and fights browser zoom.
     Only DISPLAY type is fluid. The lead was the last fluid reading size; it ran
     17/18/19 across the range, which is why the same line measured differently
     depending on the window. Client's call, 2026-09-23: 16px. */
  --text-lead:16px;
  --text-body:16px;
  --text-sm:14px;
  --text-xs:12px;
  --text-eyebrow:12px;
  --text-btn:14px;
  --text-crumb:13px;
  --text-figure:clamp(34px,3.6vw,44px);

  /* ONE leading per size. A size that renders at two different line-heights is drift,
     not hierarchy -- DM Serif 19px was inheriting the 1.70 BODY leading in three places.
     Two declared exceptions, both lockups set as a single unit: .brand__name (1.10) and
     .foot__name (1.20). The uppercase .eyebrow keeps 1.40 because letterspaced caps need
     less leading, not more. Nothing else may deviate. */
  --lh-eyebrow:1.4;   /* uppercase letterspaced labels: the eyebrow, field labels */
  --lh-h1:1.02;  --lh-h2:1.15; --lh-h3:1.22; --lh-h4:1.3;
  --lh-lead:1.7; --lh-body:1.7; --lh-sm:1.55; --lh-xs:1.5;
  --tr-h1:-.02em; --tr-h2:-.01em; --tr-h3:-.005em;
  --tr-eyebrow:.18em; --tr-btn:.06em; --tr-xs:.02em;

  /* --- spacing: THE scale. There are no other spacing values. --- */
  --space-1:4px;  --space-2:8px;  --space-3:12px; --space-4:16px;
  --space-5:20px; --space-6:24px; --space-8:32px; --space-10:40px;
  --space-12:48px;--space-16:64px;--space-20:80px;--space-24:96px;

  --gap-widget:96px;
  --gap-inner:48px;
  --gutter:40px;
  --container:1280px;

  /* --- shape, shadow, motion --- */
  --r-1:2px; --r-2:4px; --r-pill:999px;
  --shadow-sm:0 1px 2px rgba(46,16,4,.06),0 2px 8px rgba(46,16,4,.05);
  --shadow-md:0 2px 4px rgba(46,16,4,.06),0 12px 32px rgba(46,16,4,.10);
  --dur:240ms; --ease:cubic-bezier(.22,.61,.36,1);
  --dur-reveal:600ms;          /* the scroll reveal is the one deliberate exception */

  /* TWO weights and no others. 400 is every running text and every display line;
     600 is every label, button, nav item and small heading. A third weight was in the
     build (500 on nav links, 700 on footer headings) and read as drift, not hierarchy. */
  --w-body:400;
  --w-bold:600;

  /* --- layout mechanisms --- */
  --align-x:flex-start;
  /* 102 = --hdr-top + button 44 + gap 4 + the number's VISIBLE height 22 + --hdr-top.

     The bar was 116 with a 32px inset, which left 32px above the content and only 10px
     below it -- loose at the top, tight at the bottom. Halving the inset squared them.

     The number is then counted at 22px, not at the 26px its row actually occupies,
     because the last 4px of that row is leading below the glyphs. Measuring box to box
     said 16px top and 16px bottom while the eye saw 16px above the button's filled
     edge and 20px under the number. The row keeps its 26px so the tap target clears
     the 24px minimum; only the bar's height is reckoned from what is painted. */
  --header-h:102px;
  /* Where the brand lockup and the header CTA begin. */
  --hdr-top:16px;
  /* The nav drops this much further, so its words sit on the SECOND brand line
     ("Beach Resort") rather than the first. The arithmetic: that line's optical
     centre is 29px below the top of the lockup (the 21px "Shirgaonkar" line, then
     half of the 17px line under it), and the nav's words are centred in a 44px tap
     row, so the row starts 22px above them -- 29 - 22 = 7. */
  --nav-drop:7px;
  --nav-menu-gap:8px;
}
@media (max-width:991px){ :root{ --gutter:32px; } }
@media (max-width:767px){
  :root{ --gap-widget:64px; --gap-inner:32px; --gutter:24px; --header-h:64px; }
}

/* --------------------------------------------------------------------------
   2. RESET
   -------------------------------------------------------------------------- */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto; } }
body{
  margin:0; background:var(--ground); color:var(--ink);
  font-family:var(--font-body); font-size:var(--text-body); line-height:var(--lh-body);
  font-weight:400; -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
h1,h2,h3,h4,h5,h6,p,figure,blockquote,ul,ol,dl,dd{ margin:0; }
ul,ol{ padding:0; list-style:none; }
img,svg,video{ display:block; max-width:100%; }
img,video{ height:auto; }
a{ color:inherit; text-decoration:none; }
button,input,select,textarea{ font:inherit; color:inherit; margin:0; }
button{ background:none; border:0; padding:0; cursor:pointer; }
[hidden]{ display:none !important; }
:focus-visible{ outline:2px solid var(--rule); outline-offset:2px; border-radius:var(--r-1); }
.on-dark :focus-visible,.site-footer :focus-visible,.band--dark :focus-visible{ outline-color:var(--accent-lt); }
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
/* A skip link that never becomes visible is no use to a sighted keyboard user, which
   is WCAG 2.4.1 and 2.4.7 both. On focus it un-clips and lands over the header. */
a.sr-only:focus{
  position:fixed; inset-block-start:var(--space-3); inset-inline-start:var(--space-3);
  width:auto; height:auto; margin:0; clip:auto; overflow:visible; z-index:100;
  padding:var(--space-3) var(--space-5); min-height:44px;
  display:inline-flex; align-items:center;
  background:var(--brand); color:var(--on-dark);
  border:1px solid var(--accent-lt); border-radius:var(--r-2);
  font-size:var(--text-sm); line-height:var(--lh-sm); text-decoration:none;
  box-shadow:var(--shadow-md);
}
.ic-sprite{ display:none; }
.ic{ width:1em; height:1em; flex:0 0 auto; fill:currentColor; }

/* --------------------------------------------------------------------------
   3. TYPE
   -------------------------------------------------------------------------- */
h1,.h1{ font-family:var(--font-display); font-size:var(--text-h1); line-height:var(--lh-h1); letter-spacing:var(--tr-h1); font-weight:400; }
h2,.h2{ font-family:var(--font-display); font-size:var(--text-h2); line-height:var(--lh-h2); letter-spacing:var(--tr-h2); font-weight:400; }
h3,.h3{ font-family:var(--font-display); font-size:var(--text-h3); line-height:var(--lh-h3); letter-spacing:var(--tr-h3); font-weight:400; }
h4,.h4{ font-size:var(--text-h4); line-height:var(--lh-h4); font-weight:var(--w-bold); }
.lead{ font-size:var(--text-lead); line-height:var(--lh-lead); color:var(--ink); }
.small{ font-size:var(--text-sm); line-height:var(--lh-sm); }
.xs{ font-size:var(--text-xs); line-height:var(--lh-xs); letter-spacing:var(--tr-xs); }
/* --ink-2, not --accent-dk. The eyebrow used to be the exact colour of an inline
   link (#8F5D16), so a label and a thing you could click were indistinguishable
   until you noticed the underline. --ink-2 is the palette's existing secondary-text
   token and measures 7.14:1 on cream, 7.90:1 on white. Dark bands keep their own
   rule below. */
.eyebrow{
  font-size:var(--text-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--tr-eyebrow);
  text-transform:uppercase; font-weight:var(--w-bold); color:var(--ink-2);
}
/* .bleed is a dark band too -- its eyebrow was painting --accent-dk over a brown scrim,
   which measures 2.59:1. Anything sitting on a dark ground inverts here. */
.band--dark .eyebrow,.site-footer .eyebrow,.bleed .eyebrow{ color:var(--accent-lt); }
.figure{ font-family:var(--font-display); font-size:var(--text-figure); line-height:1; letter-spacing:var(--tr-h1); }
em,.italic{ font-style:italic; }

/* --------------------------------------------------------------------------
   4. LAYOUT — container, widgets, the spacing mechanism
   -------------------------------------------------------------------------- */
.container{ width:100%; max-width:var(--container); margin-inline:auto; padding-inline:var(--gutter); }
.widget{ position:relative; }
.widget--fill,.band--dark{ overflow:hidden; }

/* rule 5 — adjacent-sibling, so no stray space at the page ends */
.widget + .widget{ margin-block-start:var(--gap-widget); }
/* rule 6 — four ordered selectors, all (0,2,0), resolved by source order */
.widget--fill{ padding-block:calc(var(--gap-widget) / 2); }
.widget + .widget--fill{ margin-block-start:calc(var(--gap-widget) / 2); }
/* The USP strip is a plain .widget, so it was taking the FULL --gap-widget from
   `.widget + .widget` while every other band on the page takes half. That made it
   the only 96px seam among 48px ones, and it read as a gap under the masthead's
   buttons rather than as the masthead's own companion. Same half-gap as the fill
   bands. Declared after the rule above so the (0,2,0) tie resolves here. */
.widget + .facts{ margin-block-start:calc(var(--gap-widget) / 2); }
.widget--fill + .widget{ margin-block-start:calc(var(--gap-widget) / 2); }
.widget--fill + .widget--fill{ margin-block-start:0; }
/* The seam rule: no two stacked flow siblings inside a widget may touch. Declared BEFORE
   the head rule so --gap-inner wins the specificity tie by source order. Without this the
   location widget's address sat flush against its button row -- zero is on the scale, so
   the grid lint cannot see a missing margin; only a rendered-seam probe can. */
.widget .split > * > * + *{ margin-block-start:var(--space-6); }
/* ...but NOT inside a container that lays its children out in a ROW. A vertical stack
   margin on a row pushes the second item down and the pair stops aligning. Reset at
   (0,3,0) so it beats the rule above, which the fact strip proved is necessary. */
.widget .split > * > .facts__in > * + *,
.widget .split > * > .chips > * + *,
.widget .split > * > .actions > * + *,
.widget .split > * > .hubs > * + *{ margin-block-start:0; }

/* rule 3 — heading block to its related content is exactly half the widget gap */
.widget__head + *{ margin-block-start:var(--gap-inner); }
/* the seam rule — no two stacked flow siblings inside a widget may touch */
.widget .container > * + *{ margin-block-start:var(--space-6); }
/* rule 4 warning — reset the vertical stack margin on every ROW container, at (0,3,0) */
.widget .container.row-reset > * + *,
.widget .container .row-reset > * + *{ margin-block-start:0; }

/* heading block micro-rhythm (§2.3 rule 4) */
.widget__head{ max-width:62ch; }
.widget__head .eyebrow + .motif{ margin-block-start:var(--space-1); }
.widget__head .motif + h2,.widget__head .motif + h3{ margin-block-start:var(--space-1); }
.widget__head h2 + p,.widget__head h3 + p{ margin-block-start:var(--space-4); }
.widget__head p + p{ margin-block-start:var(--space-3); }
@media (max-width:767px){ .widget__head h2 + p,.widget__head h3 + p{ margin-block-start:var(--space-5); } }
.widget__head.center{ margin-inline:auto; }
/* a centred widget centres its rule too -- the rule belongs to the heading block */
.center .motif,.widget__head.center .motif,
.center .eyebrow,.widget__head.center .eyebrow{ margin-inline:auto; }
/* cards keep their own left-aligned copy inside a centred widget, and re-declare
   --align-x with it so the card CTA does not inherit the widget's centring (2.4) */
.center .card__body,.center .fac,.center .xp__body{ text-align:left; --align-x:flex-start; }


/* alignment pairing (§2.4) — every text-align sets --align-x in the same block */
.center{ text-align:center; --align-x:center; }
.align-left{ text-align:left; --align-x:flex-start; }
.actions{ display:flex; flex-wrap:wrap; gap:var(--space-3); justify-content:var(--align-x); }
/* Two CTAs sharing a card's width. .actions is a wrapping flex row, which suits a
   band where there is room for both side by side; inside a room card there is not,
   and the second button dropped onto its own line ragged against the first. A
   two-track grid gives them equal widths and one baseline, and the buttons lose
   their horizontal padding floor so a long label like "View Sea View Room" still
   fits its half. They stack only when the card itself is too narrow for two. */
.actions--pair{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--space-3); align-items:stretch;
}
.actions--pair .btn{ padding-inline:var(--space-4); text-align:center; }
/* A CONTAINER query, not a media query. The pair's width comes from the card, and
   the card's width comes from how many slides the slider shows -- at a 700px viewport
   each card is narrow enough that a 110px button broke "Book Now" onto two lines and
   stood 74px tall, while at 1440 and at 390 the same buttons were fine. No viewport
   breakpoint can describe that, because the viewport is not what is squeezing them.
   The container is .card__body rather than .card: making the card itself a
   containment root would change what the slider measures when it sizes its slides. */
.card__body{ container-type:inline-size; }
@container (max-width:280px){ .actions--pair{ grid-template-columns:1fr; } }
.widget__head + .actions,p + .actions{ margin-block-start:var(--space-6); }
@media (max-width:767px){ .widget__head + .actions,p + .actions{ margin-block-start:var(--space-5); } }

.grid{ display:grid; gap:var(--space-6); }
@media (max-width:767px){ .grid{ gap:var(--space-4); } }
.split{ display:grid; gap:var(--space-10); align-items:center; grid-template-columns:1fr 1fr; }
/* A grid item defaults to min-width:auto, so a Swiper inside one measures, grows, and
   re-measures without bound. Measured: a 33,554,432px page. min-width:0 closes the loop. */
.split > *{ min-width:0; }
@media (max-width:991px){ .split{ grid-template-columns:1fr; gap:var(--space-8); } }
/* The side a split's image sits on is decided by DOM ORDER: media first = left.
   --rev flips that, for the case where the media must come second in the source for
   reading order but sit left visually. It is NOT "the alternate layout" -- applying it
   to a section whose media is already first puts two consecutive splits on the same
   side, which is how the zigzag broke once already. */
.split--rev .split__media{ order:2; }
@media (max-width:991px){ .split--rev .split__media{ order:0; } }
/* .split centres its columns, which is right when one side is a photograph and the
   other a paragraph. It is wrong when BOTH sides open with a widget__head: the
   shorter column gets centred and its eyebrow drops below the other's, so the two
   headings no longer read as a pair. --top tops them out instead. */
.split--top{ align-items:start; }

/* --------------------------------------------------------------------------
   5. THE MOTIF — the logo's wave under its rising sun (DESIGN-SYSTEM.md §1b)
   -------------------------------------------------------------------------- */
/* THE MOTIF — a straight line, at the client's direction (2026-09-23).
   ONE fixed length everywhere. At 100% it inherited its container and rendered at 580px,
   609px and 726px in different bands, which reads as a mistake rather than a rule. */
.motif{ display:block; width:120px; height:2px; background:var(--motif-ink); }

/* ...and now the length of the eyebrow it sits under (client, 2026-10-23). A fixed
   120px overhung 23 eyebrows ("Stay" is 68px) and fell short of 26 others (285px), so the
   line read as a different length in every widget. The eyebrow shrinks to its own text
   and draws the line itself, one text-width long; the .motif span stays in the markup
   as the no-:has() fallback and as the hook for the heading spacing (.motif + h2).
   The trailing letter-spacing is taken off, or the line would run .18em past the last
   letter. Same 4px gap and 2px weight the span had, so nothing below it moves. */
@supports selector(:has(+ *)){
  .eyebrow:has(+ .motif){ width:fit-content; }
  .eyebrow:has(+ .motif)::after{
    content:""; display:block; width:calc(100% - var(--tr-eyebrow)); height:2px;
    margin-block-start:var(--space-1); background:var(--motif-ink);
  }
  .eyebrow + .motif{ display:none; }
}

.band--dark .motif,.bleed .motif,.site-footer .motif,
.band--dark .eyebrow,.bleed .eyebrow,.site-footer .eyebrow{ --motif-ink:var(--motif-ink-dark); }

/* The sun-arc crop — exactly one image per page carries it. Specificity (0,2,0): as a
   bare .sun-arc it lost to .ratio, which is declared later in this file, and the page
   rendered a plain rectangle where its signature device should be. */
.ratio.sun-arc,
.sun-arc{ border-radius:50% 50% var(--r-2) var(--r-2) / 22% 22% 0 0; overflow:hidden; }

/* --------------------------------------------------------------------------
   6. BUTTONS AND LINKS
   -------------------------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; padding:var(--space-3) var(--space-8);
  font-size:var(--text-btn); font-weight:var(--w-bold); letter-spacing:var(--tr-btn);
  text-transform:uppercase; border-radius:var(--r-1); border:1px solid transparent;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.btn--primary{ background:var(--accent); color:var(--ink); border-color:var(--accent); }
.btn--primary:hover{ background:var(--accent-dk); color:var(--on-dark); border-color:var(--accent-dk); }
.btn--outline{ background:transparent; color:var(--ink); border-color:var(--rule); }
.btn--outline:hover{ background:var(--accent); color:var(--ink); border-color:var(--accent); }
.btn--on-dark{ background:transparent; color:var(--on-dark); border-color:var(--accent-lt); }
.btn--on-dark:hover{ background:var(--accent); color:var(--ink); border-color:var(--accent); }

/* ---- ANCHOR LINKS ----------------------------------------------------------
   One rule for every link that is not a button. A link is underlined at rest -- that
   is what makes it recognisable as a link without relying on colour alone -- and the
   underline THICKENS on hover rather than disappearing.
   `.lnk` wraps the label inside anchors that also carry an icon, so the underline
   lands on the words and not under the glyph. */
.link,
.lnk{
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-decoration-color:currentColor;
  text-underline-offset:.2em;
  transition:text-decoration-thickness var(--dur) var(--ease),
             text-decoration-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
a:hover .lnk{ text-decoration-thickness:2px; }

/* ---- THE LINK SYSTEM ------------------------------------------------------
   An inline link has to be identifiable WITHOUT relying on colour alone (1.4.1),
   and it was not: the rule above underlines in currentColor at 1px, so a link on a
   light ground rendered as dark-brown body text with a hairline under it. The
   contact block showed the problem plainly -- #6B430F against --ink #2E1004 is a
   difference a reader has to look for.

   So the UNDERLINE carries the brand, not the text. Two pixels of --accent under
   dark text reads as a link at a glance, and because an underline is decoration
   rather than text it costs nothing against 1.4.3 -- the text colour stays free to
   be whatever the ground needs for contrast. Hovering fills the underline in to the
   text's own colour, which is a state change that does not depend on colour either.

   Three grounds, because the site has three: light (cream and white), the artwork
   bands, and the dark brown ones. Each sets a text colour; all three share the
   recipe. */
.link{
  color:var(--accent-dk);
  text-decoration-thickness:1.5px;
  text-decoration-color:var(--accent);
  text-underline-offset:.24em;
}
/* Lighter at rest (client, 2026-10-23): 1.5px of --accent under a link read as the
   same stroke as the 2px --accent line under every eyebrow, so body copy looked ruled
   like a heading. A 1px hairline at 55% keeps the link underlined -- still identifiable
   without colour -- but clearly lighter than the motif. Hover is unchanged: it fills
   in to the text colour at 2px. The fallback above stays for browsers without
   color-mix(). */
@supports (text-decoration-color:color-mix(in srgb, red 50%, transparent)){
  .link{
    text-decoration-thickness:1px;
    text-decoration-color:color-mix(in srgb, var(--accent) 55%, transparent);
  }
}
.link:hover{ text-decoration-thickness:2px; text-decoration-color:currentColor; }

/* 2. the starfish bands: the sand tint eats --accent-dk's margin, so the text goes
      darker here. Measured at 8.62:1 on white and 5.49:1 on the sand. */
.widget--alt .link{ color:#6B430F; }

/* 3. brown bands and the footer: cream-ochre text, and the underline matches it --
      --accent is too close to the brown behind it to register there. */
.band--dark .link,.site-footer .link{
  color:var(--accent-lt); text-decoration-color:var(--accent-lt);
}
.band--dark .link:hover,.site-footer .link:hover{ text-decoration-color:currentColor; }

/* 4. the ochre fill: ink text, and the underline drops to the deep brand brown,
      since --accent on --accent would be invisible. */
.facts .link{ color:var(--accent-lt); text-decoration-color:var(--accent-lt); }
/* the same lighter hairline on the dark grounds; after rules 3 and 4 so it outranks them */
@supports (text-decoration-color:color-mix(in srgb, red 50%, transparent)){
  .band--dark .link,.site-footer .link,.facts .link{
    text-decoration-color:color-mix(in srgb, var(--accent-lt) 60%, transparent);
  }
}

@media (prefers-reduced-motion:reduce){ .link,.lnk{ transition:none; } }

/* --------------------------------------------------------------------------
   7. HEADER (§2.8a)
   -------------------------------------------------------------------------- */
.site-header{
  position:fixed; inset-block-start:0; inset-inline:0; z-index:60;
  /* `auto 1fr auto`, with the nav centred inside the middle track.

     It was `1fr auto 1fr`, which put the nav's centre exactly on the header's centre.
     That is only the same thing as looking centred while the two flanks are a similar
     width, and they stopped being similar: the brand grew a third line (a parent-brand
     line, the widest in the lockup at 188px) and the CTA became a stacked
     column narrower than the button alone. Measured at 1440 the flanks were 327px and
     189px, so a mathematically centred nav sat 24px from the logo and 162px from the
     button -- crammed against one side and adrift from the other.

     Letting the middle track take what is left and centring the items inside it splits
     the slack evenly instead: 93px on each side at 1440. The nav block then sits a few
     dozen pixels off the header's true centre, which is the point -- it is balanced
     against what is actually beside it rather than against the viewport. */
  display:grid; grid-template-columns:auto 1fr auto; align-items:center;
  gap:var(--space-6); padding-inline:var(--gutter); min-height:var(--header-h);
  background:transparent; transition:background-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
.site-header.is-solid{ background:var(--ground); box-shadow:var(--shadow-sm); }
.site-header.is-solid .nav__link{ color:var(--ink); }
.site-header.is-solid .brand__name{ color:var(--ink); }
.site-header.is-solid .brand__sub{ color:var(--ink-2); }
.site-header.is-solid .nav-more__btn,.site-header.is-solid .drawer-btn,
.site-header.is-solid .nav-sub__btn{ color:var(--ink); }

.brand{ display:flex; align-items:center; gap:var(--space-3); min-height:44px; }
.brand__mark{ width:48px; height:48px; border-radius:var(--r-circle); object-fit:cover; flex:0 0 auto; }
.brand__text{ display:flex; flex-direction:column; }
.brand__name{ font-family:var(--font-display); font-size:19px; line-height:1.1; color:var(--on-dark); white-space:nowrap; }
.brand__sub{
  font-size:var(--text-xs); letter-spacing:var(--tr-eyebrow); text-transform:uppercase;
  color:var(--on-dark); opacity:.85; line-height:1.4;
}

/* Third line of the header lockup: the operating company under the resort name. Set a
   step down from .brand__sub and dimmer again, so the order of importance reads name,
   then property type, then operator, rather than three lines competing. It follows the
   same white-to-ink switch as the rest of the bar. Nowrap: broken across two lines it
   would push the lockup past the logo's own height and unbalance the row. */
.brand__by{
  font-size:10px; letter-spacing:var(--tr-eyebrow); text-transform:uppercase;
  color:var(--on-dark); opacity:.7; line-height:1.4; white-space:nowrap;
}
.site-header.is-solid .brand__by{ color:var(--ink-2); }

.nav{ min-width:0; }
.nav__list{ display:flex; align-items:center; justify-content:center; gap:var(--space-6); overflow:hidden; }
.nav__link{
  display:inline-flex; align-items:center; min-height:44px;
  font-size:var(--text-sm); font-weight:var(--w-bold); color:var(--on-dark); white-space:nowrap;
  border-block-end:1px solid transparent; transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.nav__link:hover{ color:var(--accent-lt); border-block-end-color:var(--accent-lt); }
.site-header.is-solid .nav__link:hover{ color:var(--accent-dk); border-block-end-color:var(--accent-dk); }

/* the overflow trigger is the LAST <li> of the same list, and it is an icon */
.nav-more{ position:relative; }
.nav-more__btn{
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; color:var(--on-dark);
  border:1px solid transparent; border-radius:var(--r-1);
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.nav-more__btn .ic{ width:20px; height:20px; }
.nav-more__btn:hover{ color:var(--accent-lt); border-color:var(--accent-lt); }
.site-header.is-solid .nav-more__btn:hover{ color:var(--accent-dk); border-color:var(--accent-dk); }
/* fixed, because the list needs overflow:hidden to measure and would clip an absolute menu */
.nav-more__list{
  position:fixed; z-index:70; min-width:200px; padding:var(--space-2) 0;
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--r-2);
  box-shadow:var(--shadow-md);
  opacity:0; visibility:hidden; transition:opacity var(--dur) var(--ease),visibility 0s linear var(--dur);
}
.nav-more__list::before{ /* the hover bridge — 1px taller than the gap */
  content:""; position:absolute; inset-inline:0;
  top:calc(-1 * var(--nav-menu-gap) - 1px); height:calc(var(--nav-menu-gap) + 1px);
}
.nav-more:hover .nav-more__list,
.nav-more:focus-within .nav-more__list,
.nav-more__btn[aria-expanded="true"] + .nav-more__list{
  opacity:1; visibility:visible;
  transition:opacity var(--dur) var(--ease),visibility 0s;
}
.nav-more__list a{
  display:flex; align-items:center; justify-content:flex-start; /* reads left */
  min-height:44px; padding-inline:var(--space-5); font-size:var(--text-sm); color:var(--ink);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.nav-more__list a:hover{ background:var(--ground); color:var(--accent-dk); }

/* ---- Rooms dropdown -------------------------------------------------------
   Same visual language as .nav-more__list, and fixed for the same reason: the
   parent .nav__list needs overflow:hidden to measure the priority+ overflow, so
   an absolutely positioned menu would be clipped by it. JS places it from the
   trigger's rect. The parent link stays a link; only the chevron opens the list. */
.nav-sub{ position:relative; display:inline-flex; align-items:center; gap:var(--space-1); }
.nav-sub__btn{
  display:inline-flex; align-items:center; justify-content:center;
  width:24px; height:44px; color:var(--on-dark); flex:0 0 auto;
  transition:color var(--dur) var(--ease),transform var(--dur) var(--ease);
}
.nav-sub__btn .ic{ width:16px; height:16px; transition:transform var(--dur) var(--ease); }
.nav-sub__btn:hover{ color:var(--accent-lt); }
.site-header.is-solid .nav-sub__btn:hover{ color:var(--accent-dk); }
.nav-sub__btn[aria-expanded="true"] .ic{ transform:rotate(180deg); }

.nav-sub__list{
  position:fixed; z-index:70; min-width:190px; padding:var(--space-2) 0;
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--r-2);
  box-shadow:var(--shadow-md); list-style:none; margin:0;
  opacity:0; visibility:hidden;
  transition:opacity var(--dur) var(--ease),visibility 0s linear var(--dur);
}
.nav-sub__list::before{ /* hover bridge across the gap, so the pointer can travel */
  content:""; position:absolute; inset-inline:0;
  top:calc(-1 * var(--nav-menu-gap) - 1px); height:calc(var(--nav-menu-gap) + 1px);
}
/* ONLY aria-expanded opens this. It used to open on :hover and :focus-within as well,
   and those two fought the button: clicking the chevron focuses it, so :focus-within
   kept the menu open while the click set aria-expanded="false" -- the arrow could never
   close what it had opened. Hover still opens the menu, but by asking the script to set
   this attribute rather than by a rule of its own. */
.nav-sub__btn[aria-expanded="true"] + .nav-sub__list{
  opacity:1; visibility:visible;
  transition:opacity var(--dur) var(--ease),visibility 0s;
}
.nav-sub__list a{
  display:flex; align-items:center; min-height:44px; padding-inline:var(--space-5);
  font-size:var(--text-sm); color:var(--ink); white-space:nowrap;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.nav-sub__list a:hover{ background:var(--ground); color:var(--accent-dk); }
.nav-sub__list a[aria-current="page"]{ color:var(--accent-dk); font-weight:var(--w-bold); }

/* When the priority+ logic moves Rooms INTO the overflow menu, the dropdown is
   already inside a popup -- a second floating layer there would be unreachable.
   It flattens to an indented static list instead. */
.nav-more__list .nav-sub{ display:block; }
.nav-more__list .nav-sub__btn{ display:none; }
.nav-more__list .nav-sub__list{
  position:static; opacity:1; visibility:visible; min-width:0; padding:0;
  background:none; border:0; box-shadow:none;
}
.nav-more__list .nav-sub__list a{ padding-inline-start:var(--space-8); font-size:var(--text-xs); }
.nav-more__list .nav-sub__list::before{ content:none; }

/* The drawer mirrors the bar: parent link plus a chevron that opens the list.
   The row is flex so the two tap targets sit side by side and never overlap --
   the label goes to the rooms page, the chevron only opens the children. */
/* The divider lives on .drawer__list a, but in this row the anchor is only as wide
   as the label, so the rule stopped short and left a gap beside the chevron. It moves
   to the row, which spans the full width, and comes off the anchor so the two do not
   stack into a double line. */
.drawer-sub__row{
  display:flex; align-items:center; justify-content:space-between; gap:var(--space-3);
  border-block-end:1px solid var(--hair-dark);
}
/* .drawer__list a is declared LATER in this file at the same specificity (0,1,1),
   so it won on source order and the anchor kept its own rule under the row's.
   Scoping to .drawer__list makes this (0,2,1) and settles it by specificity
   rather than by which line happens to come last. */
.drawer-sub__row > a{ flex:1 1 auto; min-width:0; }
.drawer__list .drawer-sub__row > a{ border-block-end:0; }
.drawer-sub__btn{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:44px; min-height:44px; color:var(--on-dark); opacity:.85;
  transition:opacity var(--dur) var(--ease);
}
.drawer-sub__btn .ic{ width:18px; height:18px; transition:transform var(--dur) var(--ease); }
.drawer-sub__btn:hover{ opacity:1; }
.drawer-sub__btn[aria-expanded="true"] .ic{ transform:rotate(180deg); }
.drawer-sub__list{ list-style:none; margin:0; padding:0; }
.drawer-sub__list a{
  padding-inline-start:var(--space-6); font-size:var(--text-sm); opacity:.9;
}
.drawer-sub__list a[aria-current="page"]{ opacity:1; color:var(--accent-lt); }

.header-actions{ justify-self:end; flex:0 0 auto; display:flex; align-items:center; gap:var(--space-3); }
.header-actions .btn{ flex:0 0 auto; white-space:nowrap; }
.drawer-btn{
  display:none; align-items:center; justify-content:center;
  width:44px; height:44px; color:var(--on-dark);
}
.drawer-btn .ic{ width:22px; height:22px; }

@media (max-width:767px){
  .site-header{ grid-template-columns:44px 1fr auto; gap:var(--space-2); }
  .drawer-btn{ display:inline-flex; }
  .nav{ display:none; }
  .brand{ justify-content:center; }
  .brand__mark{ width:40px; height:40px; }
  /* The name used to be hidden here because the header CTA needed the room. That
     button now lives in the bottom bar, so the lockup reads the same as it does on
     desktop: mark, then the two-line name. Sized down a step so it clears the
     burger at 360 without wrapping. */
  .brand__name{ font-size:16px; }
  .brand__sub{ font-size:10px; }
  .header-actions .btn{ padding-inline:var(--space-5); }
}
@media (max-width:359px){
  .site-header{ padding-inline:var(--space-4); }
  .header-actions .btn{ padding-inline:var(--space-4); font-size:var(--text-crumb); }
}

/* mobile drawer */
.drawer{
  position:fixed; inset:0; z-index:80; display:grid; grid-template-columns:1fr;
  background:var(--brand-deep); color:var(--on-dark);
  /* Bottom padding matched to the top: it was --space-12, twice the top, which left
     a shelf of empty space under the Book Your Stay button. The rows are declared
     rather than left to stretch -- the list takes the slack, so the button sits at
     the bottom on purpose instead of wherever the default stretch happened to put
     it. (align-content:start was worse: it pulled everything up and left 179px.) */
  grid-template-rows:auto 1fr auto;
  padding:var(--space-6) var(--gutter);
  overflow-y:auto;
}
.drawer__top{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); }
.drawer__close{ width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center; color:var(--on-dark); }
.drawer__close .ic{ width:22px; height:22px; }
.drawer__list{ margin-block-start:var(--space-8); }
.drawer__list a{
  display:flex; align-items:center; min-height:48px;
  font-family:var(--font-display); font-size:var(--text-h4);
  border-block-end:1px solid var(--hair-dark);
}
.drawer__list a:hover{ color:var(--accent-lt); }
.drawer__meta{ margin-block-start:var(--space-8); font-size:var(--text-sm); color:var(--on-dark); opacity:.85; }
.drawer__meta a{ display:inline-flex; min-height:32px; align-items:center; }

/* --------------------------------------------------------------------------
   8. HERO + the nested booking form (§2.8)
   -------------------------------------------------------------------------- */
/* The hero carries NO copy (client instruction, 2026-09-23): the video runs clean and
   edge to edge, and the ONLY thing over it is the booking form. Because the headline has
   moved out, the hero is pure media, so it keeps its full-height cinematic read and the
   form becomes the single element in the fold that asks for an action. */
:root{ --cookie-h:0px; --bw-overhang:var(--space-12); }
@media (max-width:767px){ :root{ --bw-overhang:var(--space-8); } }
/* --bw-lift raises the form clear of the hero's lower edge so it reads as sitting
   ON the video rather than balanced on its rim. Measured before the change: the
   hero is 900 tall and the form occupied 779-900, its bottom flush with the hero's
   bottom. The lift is desktop only, at the same 992 breakpoint where .bf stops
   being a 2-column form; on a phone the fold is tight and the form has no room to
   give away. It works through the hero's own bottom padding, because the hero is
   justify-content:flex-end, so padding pushes the form up by exactly this much and
   nothing else in the section moves. */
:root{ --bw-lift:0px; }
@media (min-width:992px){ :root{ --bw-lift:var(--space-12); } }
.hero{
  position:relative; min-height:100svh; display:flex; flex-direction:column;
  justify-content:flex-end; isolation:isolate;
  /* The Rooms & Guests popover opens downwards and overflows the hero's box. `isolation`
     confines .bw's z-index inside the hero, so the popover painted UNDERNEATH the section
     that follows and its counters could not be clicked at all -- the functional pass hit
     "<p class=lead> from <section class=masthead> intercepts pointer events".
     Raising the hero itself lifts everything it contains above the widgets below it. */
  z-index:5;
  /* The cookie bar is fixed to the bottom of the viewport and was covering the booking
     form -- the fold's only call to action. The hero reserves the bar's measured height
     PLUS the form's own overhang, or the overhang eats the clearance and the lower 48px
     of the form stays behind the bar. Measured: 48px covered before this was added. */
  padding-block-end:calc(var(--cookie-h) + var(--bw-overhang) + var(--bw-lift));
}
.hero__media{ position:absolute; inset:0; z-index:-2; overflow:hidden; background:var(--brand-deep); }
.hero__media video,.hero__media img{ width:100%; height:100%; object-fit:cover; }
/* A light bottom-only vignette, not the text scrim: nothing here has to stay legible over
   the video, so the footage is left alone except where the white form bar meets it. */
.hero::before{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg,rgba(46,16,4,.28) 0%,rgba(46,16,4,0) 26%,rgba(46,16,4,0) 62%,rgba(46,16,4,.38) 100%);
}
/* the video pause control is an accessibility requirement, not copy — it stays.
   Top right, clear of the docked form and below the header. */
/* The controls sit directly above the booking form, not up in the corner under the
   header. They are a flow item of the hero's column now rather than absolutely
   positioned, so they track the form at every width and share its measure -- their
   right edge lines up with the form's right edge instead of with the viewport. */
/* On a phone the bar sits directly on the booking form (client, 2026-09-30), so
   the space that used to separate the two is gone below 768. */
.hero__ctrls{
  position:relative; z-index:4;
  width:100%; max-width:calc(var(--container) - var(--gutter) * 2);
  margin-inline:auto; margin-block-end:var(--space-3);
  display:flex; gap:var(--space-2); justify-content:flex-end;
}
.hero__ctrls > * + *{ margin-block-start:0; }   /* it lays out in a ROW (2.3 rule 4) */
/* (the round .hero__vidbtn controls were replaced by the .vctrl bar, section 31) */

/* THE PAGE'S ONE DELIBERATE OVERLAP (DESIGN-SYSTEM.md 1b, Depth).
   The booking form overhangs the hero's bottom edge, so one elevated panel crosses the
   boundary between the video and the cream page. The fact strip used to own this device;
   it cannot now that the form occupies that edge, so the device moved here. */
.bw{
  position:relative; z-index:4;
  margin-block-end:calc(-1 * var(--bw-overhang));
  background:var(--surface); box-shadow:var(--shadow-md);
  border-radius:var(--r-2);
  width:100%; max-width:calc(var(--container) - var(--gutter) * 2);
  margin-inline:auto;
}
@media (max-width:767px){ .bw{ border-radius:0; max-width:none; } }
/* the intro band below must clear the overhang */
.hero + .masthead{ margin-block-start:calc(var(--gap-widget) - var(--bw-overhang)); }

/* --------------------------------------------------------------------------
   32. PHONE HERO — the video, and nothing in front of it (client, 2026-09-30)
   --------------------------------------------------------------------------
   The booking form is hidden below 768 and the sticky Book Your Stay bar carries the
   booking on a phone, which is what every inner page already does. Measured before this:
   the form took 364px and the cookie bar 163px, leaving the video 201px of a 390x844
   screen and 137px of a 360x780 one -- under a quarter of the screen for the thing the
   hero exists to show.

   Three knock-on adjustments, all of them undoing something the form needed:
   - the hero reserved the form's overhang; with no form there is nothing to overhang, and
     what it has to clear instead is the sticky bar, or the video controls sit behind it;
   - the band below pulled itself up by that same overhang to meet the form, and would
     otherwise now pull up into nothing;
   - --mobar-h gets a real default at this width so the reservation is right on the first
     paint and with JS off, where the bar shows regardless. JS overwrites it with the
     measured height as soon as it runs. */
@media (max-width:767px){
  :root{ --mobar-h:58px; }
  .hero .bw{ display:none; }
  /* A shorter video on phones (client, 2026-10-07): 60% of the screen instead of all of
     it, so the H1 shows in the first screen. The hero now ends above the fixed Book bar
     and cookie bar, so it no longer reserves room for them, and the controls sit on the
     video's bottom edge. */
  .hero{ min-height:clamp(360px,60svh,560px); padding-block-end:0; }
  .hero + .masthead{ margin-block-start:calc(var(--gap-widget) / 2); }
  .hero__ctrls.vctrl{ margin-block-end:0; }
}


/* THE PAGE OPENING — the H1 now lives here, below the booking form.
   It is the page's single biggest typographic moment (84px, 5.25x body), so the band is
   given room and nothing competes with it. */
/* THE MASTHEAD — the page opening, flush left like every other section (the reference
   opens flush left throughout, so a centred band here would have broken the one grammar
   the L3 clone exists to reproduce). */
/* start, not end. The columns used to be bottom-aligned, which balanced while the
   lead was a single line: both columns were about the same height. The longer lead
   makes the right column 59px taller, and bottom-alignment then pushed the whole
   name lockup down so the eyebrow began below the paragraph's first line. Aligning
   the tops ties the eyebrow to the first line of the lead, which is the relationship
   a reader actually sees. */
.masthead__in{
  display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:var(--space-10); align-items:start;
}
@media (max-width:991px){ .masthead__in{ grid-template-columns:1fr; gap:var(--space-8); align-items:start; } }
.masthead__in > * + *{ margin-block-start:0; }   /* it lays out in a ROW (2.3 rule 4) */

/* The two-line lockup is the logo's own hierarchy: the family name large, the
   descriptor smaller beneath it. Traceable to the mark, not a type default. */
.masthead__title{ color:var(--brand); }
/* Smaller on phones only (client, 2026-10-07); desktop keeps the full clamp. */
@media (max-width:767px){ .masthead__title{ font-size:34px; } }
.masthead__sub{
  display:block; margin-block-start:var(--space-2);
  font-family:var(--font-body); font-size:clamp(14px,1.5vw,19px); font-weight:var(--w-bold);
  letter-spacing:var(--tr-eyebrow); text-transform:uppercase; line-height:1.2;
  color:var(--accent-dk);
}
.masthead__aside > * + *{ margin-block-start:var(--space-6); }
.masthead .lead{ max-width:46ch; }
.masthead .eyebrow + .motif{ margin-block-start:var(--space-1); }
.masthead .motif + .masthead__title{ margin-block-start:var(--space-3); }

.bw__in{ width:100%; padding:var(--space-6); }
@media (max-width:767px){ .bw__in{ padding:var(--space-5) var(--gutter); } }
.bw__in{ width:100%; max-width:var(--container); margin-inline:auto; padding:var(--space-6) var(--gutter); }
/* The four fields are NOT equal. Rooms & Guests carries the longest label in the row
   -- "5 Rooms, 10 Adults, 5 Children" at full count -- while a promo code is a few
   characters and a date input sizes itself. Equal tracks gave this one 157px of
   text space for a string needing 190, so it wrapped inside a control fixed at
   48px and spilled. The weight moves from the promo field to this one. */
.bf{ display:grid; gap:var(--space-4);
     /* Real floors, not bare ratios. Ratios alone left Rooms & Guests 15px short at
        992px, and simply raising its weight starved the promo track until its own
        uppercase label -- "PROMO CODE" is about 110px set -- wrapped to two lines and
        broke the label row instead. Each track now states the least it can live with:
        a date input's intrinsic width, the longest guest string, the promo label. */
     grid-template-columns:minmax(150px,1fr) minmax(150px,1fr) minmax(260px,1.6fr)
                           minmax(115px,.7fr) auto;
     align-items:end; }
/* Small desktop / tablet, 768-991: ONE row, never wrapped (client, 2026-10-23). The
   old two-column fallback stacked the form into four rows, 364px tall, which buried the
   hero video it sits on. The row fits by giving up slack, not fields: tighter gaps and
   control padding, a narrower Book Now. The labels keep the SAME size and tracking as
   at every other width (client: consistent labels) -- so the floors are what each
   label and control needs at 768: a date's text and icon, "PROMO CODE" at 107px.
   A guest summary longer than its track ends in the ellipsis #rgSummary already has. */
@media (min-width:768px) and (max-width:991px){
  .bf{ gap:var(--space-3);
       grid-template-columns:minmax(130px,.8fr) minmax(130px,.8fr) minmax(0,1.9fr)
                             minmax(112px,.6fr) auto; }
  .bf .bf__control{ padding-inline:var(--space-3); }
  .rg__toggle{ gap:var(--space-2); }
  .bf__submit .btn{ padding-inline:var(--space-3); white-space:nowrap; }
}
/* Two columns down to 380px: stacked as five full-width rows, the form consumed the
   whole fold on a phone and left roughly 200px of the hero video visible. */
@media (max-width:339px){ .bf{ grid-template-columns:1fr; } }
.bf__field{ display:flex; flex-direction:column; gap:var(--space-2); text-align:left; }
.bf__label{ font-size:var(--text-xs); line-height:var(--lh-eyebrow); letter-spacing:var(--tr-eyebrow); text-transform:uppercase; font-weight:var(--w-bold); color:var(--ink-2); }
.bf__control{
  height:48px; padding-inline:var(--space-4); background:var(--surface);
  border:1px solid var(--rule); border-radius:var(--r-1); color:var(--ink);
  font-size:var(--text-sm); line-height:var(--lh-sm); width:100%;
  /* a native date input carries an intrinsic minimum width that pushed the two-column
     form 26px past a 320px viewport; min-width:0 lets the grid track govern instead */
  min-width:0;
}
.bf__field{ min-width:0; }
.bf__control:focus-visible{ border-color:var(--accent); }
.bf__submit .btn{ width:100%; }
/* Live starting price under Book Now. On desktop it hangs below the button so the
   field row keeps its bottom-aligned line; on the stacked tablet form it flows. */
/* Same type as the "Why Book Direct?" trigger, and the same gap below the form, so the
   two sit on one line at either end of the form. */
.bf__price{
  margin:0; text-align:center; white-space:nowrap;
  font-size:var(--text-xs); line-height:var(--lh-eyebrow); letter-spacing:var(--tr-eyebrow);
  text-transform:uppercase; font-weight:var(--w-bold); color:var(--ink-2);
}
.bf__price strong{ color:var(--brand); font-weight:var(--w-bold); }
@media (min-width:992px){
  .bf__submit{ position:relative; }
  .bf__price{
    position:absolute; inset-inline:0; inset-block-start:calc(100% + var(--space-3));
  }
}

/* Why Book Direct -- a small text trigger under the form; hovering or focusing it opens
   a speech bubble above it. Desktop only: below 992 the form stacks and the fold goes
   to the video. Pure CSS (:hover / :focus-within), so it needs no script. */
.bwd{ display:none; }
@media (min-width:992px){
  .bwd{ display:flex; position:relative; width:max-content; margin-block-start:var(--space-3); }
}
.bwd__trigger{
  display:inline-flex; align-items:center; gap:var(--space-2);
  padding:0; background:none; border:0; cursor:pointer;
  font-size:var(--text-xs); line-height:var(--lh-eyebrow); letter-spacing:var(--tr-eyebrow);
  text-transform:uppercase; font-weight:var(--w-bold); color:var(--accent-dk);
  text-decoration:underline; text-decoration-style:dotted; text-underline-offset:3px;
}
.bwd__trigger .ic{ width:16px; height:16px; flex:0 0 auto; fill:none; }
.bwd__trigger:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }
.bwd__bubble{
  position:absolute; inset-inline-start:0; inset-block-end:calc(100% + 14px); z-index:6;
  width:max-content; max-width:320px; margin:0; padding:var(--space-4) var(--space-5);
  list-style:none; background:var(--brand-deep); color:var(--on-dark);
  border-radius:var(--r-2); box-shadow:var(--shadow-md);
  opacity:0; visibility:hidden; transform:translateY(6px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease),visibility var(--dur);
}
/* the tail, pointing down at the trigger */
.bwd__bubble::after{
  content:""; position:absolute; inset-block-start:100%; inset-inline-start:var(--space-5);
  border:8px solid transparent; border-block-start-color:var(--brand-deep);
}
.bwd:hover .bwd__bubble,
.bwd:focus-within .bwd__bubble{ opacity:1; visibility:visible; transform:none; }
.bwd__bubble > * + *{ margin-block-start:var(--space-2); }
.bwd__bubble li{
  display:flex; align-items:center; gap:var(--space-2);
  font-size:var(--text-sm); line-height:var(--lh-sm);
}
.bwd__bubble .ic{ width:18px; height:18px; flex:0 0 auto; fill:none; color:var(--accent-lt); }
@media (prefers-reduced-motion:reduce){ .bwd__bubble{ transition:none; } }

/* rooms and guests popover */
.rg{ position:relative; }
.rg__toggle{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); text-align:left; }
/* The label trims rather than wraps. Every field in this row is a fixed 48px, so a
   second line has nowhere to go and spills out of the control -- which is what
   happened as the counts went up. min-width:0 is what lets a flex child shrink
   below its text width at all; without it the ellipsis never gets a chance. */
#rgSummary{ min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rg__toggle .ic{ flex:0 0 auto; }
.rg__panel{
  position:absolute; z-index:20; inset-inline-start:0; inset-block-start:calc(100% + var(--space-2));
  min-width:300px; background:var(--surface);
  /* Five rooms with a child each stand ~900px tall, so the panel is capped and only its
     body scrolls; Done sits in a footer that never scrolls away. On desktop site.js
     tightens the cap to the real gap between the header and this field. */
  display:flex; flex-direction:column; max-height:min(560px, 62svh); overflow:hidden;
  border:1px solid var(--rule); border-radius:var(--r-2); box-shadow:var(--shadow-md);
}
@media (max-width:479px){ .rg__panel{ inset-inline:0; min-width:0; } }
/* Desktop: the panel opens UPWARD. The booking form is the last thing in a 100svh
   hero, so "below the trigger" is always past the fold -- the 248px panel simply has
   nowhere to go. It went unnoticed while the form sat flush with the hero's bottom
   edge, because clicking the toggle then forced the page to scroll and dragged the
   panel into view with it. Once --bw-lift raised the form clear of that edge no scroll
   happened, the panel hung off-screen, and the adults "+" control stopped being
   hit-testable at all: drive.mjs F4b went from PASS to elementFromPoint returning null.
   Opening upward puts it over the video, where there is a full screen of room.
   Scoped to the same breakpoint as --bw-lift; below it the behaviour is unchanged. */
@media (min-width:992px){
  .rg__panel{ inset-block-start:auto; inset-block-end:calc(100% + var(--space-2)); }
}
.rg__row{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:44px; }
.rg__row + .rg__row{ margin-block-start:var(--space-2); }
/* One block per room (client, 2026-10-23). The hairline now separates ROOMS, not the
   rows inside one, so each room reads as a unit: its title, its two counters, its ages. */
.rg__room + .rg__room{ margin-block-start:var(--space-4); border-block-start:1px solid var(--hair-soft); padding-block-start:var(--space-4); }
.rg__head{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); margin-block-end:var(--space-2); }
.rg__title{ font-size:var(--text-xs); line-height:var(--lh-eyebrow); letter-spacing:var(--tr-eyebrow); text-transform:uppercase; font-weight:var(--w-bold); }
.rg__remove{ min-height:32px; padding-inline:var(--space-2); margin-inline-end:calc(var(--space-2) * -1);
  font-size:var(--text-xs); color:var(--accent-dk); text-decoration:underline; text-underline-offset:3px; }
.rg__remove:hover{ color:var(--ink); }
.rg__add{ display:block; inline-size:100%; min-height:44px; margin-block-start:var(--space-4);
  border:1px dashed var(--rule); border-radius:var(--r-1); color:var(--accent-dk); font-size:var(--text-sm); font-weight:var(--w-bold); }
.rg__add:hover{ border-style:solid; background:var(--accent); color:var(--ink); }
.rg__body{ flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain; padding:var(--space-5); }
.rg__foot{ flex:0 0 auto; padding:var(--space-4) var(--space-5) var(--space-5); border-block-start:1px solid var(--hair-soft); }
.rg__done{ inline-size:100%; }
.rg__step{ display:flex; align-items:center; gap:var(--space-3); }
.rg__btn{
  width:32px; height:32px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--rule); border-radius:var(--r-1); color:var(--ink); font-size:18px; line-height:1;
}
.rg__btn:hover:not(:disabled){ background:var(--accent); border-color:var(--accent); }
.rg__btn:disabled{ opacity:.4; cursor:not-allowed; }
.rg__val{ min-width:24px; text-align:center; font-size:var(--text-sm); font-weight:var(--w-bold); }
/* The child-age box. On a phone it takes the stepper's exact width so the two line up
   as one column of controls: 32 + 12 + 24 + 12 + 32 = 112, the two buttons, the number
   between them, and the two gaps. Client asked for this on mobile only; on a wider
   screen it keeps the narrower 96px it has always had. */
.rg__age{ height:36px; width:96px; }
/* On a phone it also matches their HEIGHT. The select stood 36px against the
   buttons' 32px, so the one control in the column was taller than the three above
   it. Same width, same height, one column. */
@media (max-width:767px){ .rg__age{ width:112px; height:32px; } }

/* Mobile: the label and its control sit together in the middle of the panel rather than
   pushed to opposite edges -- the stepper had 188px of slack on its left and none on its
   right (client, 2026-09-29).

   justify-content:center alone would have broken the column the controls were only just
   aligned into: each row centres its OWN content, and the labels run 42px to 74px, so
   every row would land its stepper at a different x. Giving the label a fixed width --
   the widest of them, "Child 3 age" at 74px, rounded to 80 -- makes every group the same
   width, so they centre to the same place and the controls stay in one line. */
@media (max-width:767px){
  .rg__row{ justify-content:center; }
  .rg__row > .small,
  .rg__row > label{ inline-size:80px; flex:0 0 80px; }
}
.rg__note{ margin-block-start:var(--space-3); font-size:var(--text-xs); line-height:var(--lh-xs); color:var(--ink-2); }

/* --------------------------------------------------------------------------
   9. FACT STRIP — the page's one deliberate overlap
   -------------------------------------------------------------------------- */
/* Brand brown, at the client's direction (2026-09-25). It was --accent, and that
   also carried a contrast problem worth recording: the labels sit at opacity .82,
   which put them at 4.47:1 on the ochre -- under 1.4.3's 4.5. The a11y harness never
   saw it, because it reads the computed colour and opacity is applied afterwards.
   On brown the same labels measure 8.70:1 and the figures 12.28:1. */
.facts__in{
  background:var(--brand); box-shadow:var(--shadow-md); border-radius:var(--r-2);
  display:grid; grid-template-columns:repeat(4,1fr); padding:var(--space-8) var(--space-6);
}
@media (max-width:767px){ .facts__in{ grid-template-columns:1fr 1fr; padding:var(--space-6) var(--space-4); } }
.fact{ min-width:0; display:flex; flex-direction:column; align-items:center; text-align:center; gap:var(--space-2); padding:var(--space-4) var(--space-3); }
.fact + .fact{ border-inline-start:1px solid var(--rule-dark); }
@media (max-width:767px){
  .fact:nth-child(odd){ border-inline-start:0; }
  .fact:nth-child(n+3){ border-block-start:1px solid var(--rule-dark); }
}
.fact .ic{ width:44px; height:44px; color:var(--accent-lt); }
.fact__fig{ color:var(--on-dark); }
.fact__label{ font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--on-dark); opacity:.82; }

/* --------------------------------------------------------------------------
   10. CARDS, CHIPS, MEDIA
   -------------------------------------------------------------------------- */
.ratio{ position:relative; overflow:hidden; border-radius:var(--r-2); background:var(--hair-soft); }
.ratio > img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; transition:transform var(--dur) var(--ease); }
.ratio--16x9{ aspect-ratio:16/9; }
.ratio--4x3{ aspect-ratio:4/3; }
.ratio--1x1{ aspect-ratio:1/1; }
.ratio--3x4{ aspect-ratio:3/4; }

.card{
  display:flex; flex-direction:column; background:var(--surface);
  border:1px solid var(--rule); border-radius:var(--r-2); overflow:hidden;
  box-shadow:var(--shadow-sm); transition:box-shadow var(--dur) var(--ease);
}
/* The whole card used to be the hit area: the single CTA's ::after stretched over it.
   That breaks the moment a card carries TWO CTAs. Both buttons stretched to inset:0,
   the later one in the DOM painted on top, and Book Now took every click on the card
   -- including clicks that landed squarely on "View Room". That is the bug: the room
   page had become hard to reach from its own card.

   So the card is no longer a link. Three explicit targets remain: the photograph, the
   View button and Book Now, each going where it says it goes. */
.card{ position:relative; }

/* The photograph links to the room, for anyone who expects to click a card image.
   It is aria-hidden and out of the tab order on purpose: it goes exactly where the
   View button beside it goes, and a screen reader listing the links should not meet
   the same destination twice. Removing it from the tab order first is what makes
   aria-hidden legitimate here. */
/* Not draggable. A link and an image are both natively draggable, so inside the
   slider a press-and-move on a card started a browser drag -- picking the photo up as
   a ghost -- instead of swiping to the next room. The attribute is what actually stops
   it; -webkit-user-drag backs it up where the attribute is ignored, and user-select
   stops a drag selecting the caption text on the way past. */
.card__link{ display:block; -webkit-user-drag:none; user-select:none; }
.card__link img{ -webkit-user-drag:none; pointer-events:none; }
@media (hover:hover){ .card__link:hover .ratio > img{ transform:scale(1.04); } }
@media (hover:hover){ .card:hover{ box-shadow:var(--shadow-md); } .card:hover .ratio > img{ transform:scale(1.04); } }
.card__body{ display:flex; flex-direction:column; padding:var(--space-8); flex:1 1 auto; }
.card__body > * + *{ margin-block-start:var(--space-3); }
/* Outside a card the same spec block gets the same rule above it, so a fact list always
   reads as a separate thing from the prose that introduces it. */
.split .chips{
  margin-block-start:var(--space-6); padding-block-start:var(--space-6);
  border-block-start:1px solid var(--hair-soft);
}
.band--dark .split .chips{ border-block-start-color:var(--rule-dark); }

/* Inside a card the facts are a spec block, not a loose run of chips: a fixed two-column
   grid so they align down the card instead of wrapping ragged, and a hairline that
   separates what the room IS from what it HAS. */
.card__body .chips{
  display:grid; grid-template-columns:auto auto; justify-content:start;
  gap:var(--space-4) var(--space-8);
  margin-block-start:var(--space-5); padding-block-start:var(--space-5);
  border-block-start:1px solid var(--hair-soft);
}
.card__body .chip{ color:var(--ink); }
.card__body .chip .ic{ flex:0 0 auto; }
/* Two columns that can actually give way.
   This was `auto auto` with `white-space:nowrap`, which is a grid that cannot reflow
   holding text that cannot wrap -- so whenever the card was narrower than its own
   content the right-hand column simply left the card: 91px past the edge at 360, and
   again 74px past it at 1024, where the card sits in a 3-up grid. That put "King Bed"
   and "Sea Facing" on top of the 1px --rule border, which is what harness 9 reported
   as 4.37:1 -- text on a border rather than on the card.
   A media query cannot fix it, because the card's width comes from its grid column,
   not from the viewport. minmax(0,1fr) lets each cell shrink below its content's
   intrinsic width, and dropping nowrap lets a long label take a second line instead
   of escaping. The two-column alignment the card wanted is kept. */
.card__body .chips{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.card__body .chip{ min-width:0; align-items:flex-start; }
.card__body .chip .ic{ margin-block-start:1px; }
.card__body h3 + .small{ color:var(--ink-2); font-size:var(--text-body); line-height:var(--lh-body); }
.card__body .actions{ margin-block-start:auto; padding-block-start:var(--space-6); }
.card .ratio{ border-radius:0; }

/* Amenity facts read as a list, not as pills. The border was doing no work -- it framed
   each fact separately when they belong together -- so the separation is spacing now. */
.chips{ display:flex; flex-wrap:wrap; gap:var(--space-4) var(--space-6); }
.chip{
  display:inline-flex; align-items:center; gap:var(--space-2);
  padding:0; border:0; border-radius:0;
  font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink-2);
}
.chip .ic{ width:26px; height:26px; color:var(--accent-dk); }
.band--dark .chip{ color:var(--on-dark); }
.band--dark .chip .ic{ color:var(--accent-lt); }

/* --------------------------------------------------------------------------
   11. DARK BANDS
   -------------------------------------------------------------------------- */
/* the alternate band fill. It is a --fill widget, so it takes the half-gap arithmetic
   of 2.3 rule 6 automatically and the content-to-content distance stays 96px. */
.widget--alt{ background:var(--surface); }
/* a white card on a white band disappears, so cards invert to the cream ground */
.widget--alt .card,
.widget--alt .fac,
.widget--alt .near{ background:var(--ground); }


.band--dark{ background:var(--brand); color:var(--on-dark); }
.band--dark h2,.band--dark h3{ color:var(--on-dark); }
.band--dark .lead{ color:var(--on-dark); opacity:.86; }
.band--deep{ background:var(--brand-deep); }

/* this grid sits inside a .split half-column, so 4 columns never fit: it overflowed
   the viewport by 67px at 1024 before this was measured. 2 up, 1 up on a phone. */
.exp__grid{ grid-template-columns:1fr 1fr; }
@media (max-width:479px){ .exp__grid{ grid-template-columns:1fr; } }
.exp{ min-width:0; display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-3); padding:var(--space-6); border:1px solid var(--rule-dark); border-radius:var(--r-2); }
.exp .ic{ width:44px; height:44px; color:var(--accent-lt); }
.exp__name{ font-family:var(--font-display); font-size:var(--text-h4); line-height:var(--lh-h4); }
.exp__note{ font-size:var(--text-sm); line-height:var(--lh-sm); opacity:.86; }
/* Swiper's stylesheet is appended to the END of this file, so its
   `.swiper-slide{ display:block }` beat `.exp{ display:flex }` on source order at
   equal specificity: the icon, the name and the note stopped stacking and ran
   together on one line. Two classes (0,2,0) settle it by specificity instead of
   depending on which sheet happens to be last. */
.exp.swiper-slide,
.fac.swiper-slide{ display:flex; }
.exp.swiper-slide{ flex-direction:column; align-items:flex-start; }
/* a slide is sized by Swiper, so the item must fill it rather than size itself.
   NOT applied to .fac: that track runs in Swiper's grid mode, where the module
   sets the row height itself -- height:auto there let each tile size to its own
   content and the two rows came out ragged and overlapping. */
.exp.swiper-slide{ height:auto; }
/* ---- these two bands slide on PHONES ONLY ----------------------------------
   At 992 and up they go back to being the grids they always were. The slider
   markup stays in the DOM and Swiper still initialises; the track is simply laid
   out as a grid and its transform pinned, the same trick .rooms__sw already uses.
   For facilities each slide holds a PAIR of tiles, so the slide and the pair are
   both display:contents up here -- that dissolves the two wrapper boxes and lets
   all 12 tiles fall into the 4-column grid as if they had never been grouped. */
@media (min-width:992px){
  .exp__sw .swiper,
  .fac__sw .swiper{ overflow:visible; }
  /* display:grid stated HERE, not left to the .grid class on the same element:
     Swiper's stylesheet is appended after this file and its
     `.swiper-wrapper{ display:flex }` beat .grid on source order, so the track kept
     flexing and every tile stayed on one row. Two classes (0,2,0) settle it. */
  .exp__sw .swiper-wrapper,
  .fac__sw .swiper-wrapper{ display:grid; transform:none !important; }
  .exp__sw .swiper-slide{ width:auto !important; margin-right:0 !important; }
  .fac__sw .swiper-slide,
  .fac__sw .fac__pair{ display:contents; }
  .exp__sw .sw__ctrls,
  .fac__sw .sw__ctrls{ display:none; }
}

/* Each slide is a stacked PAIR of tiles, which is how this track gets its two
   rows: Swiper's Grid module is not in the UMD bundle the site vendors, so
   grid:{rows:2} was ignored and the tiles just flex-wrapped. Equal row heights
   come from the pair being a 2-row grid with equal tracks. */
.fac__pair{ display:grid; grid-template-rows:1fr 1fr; gap:var(--space-4); height:100%; list-style:none; margin:0; padding:0; }
.fac__pair > .fac{ height:100%; }
.fac__sw .swiper-slide{ height:auto; }

.pull{ font-family:var(--font-display); font-style:italic; font-size:var(--text-h3); line-height:var(--lh-h3); color:var(--accent-lt); max-width:34ch; }

/* --------------------------------------------------------------------------
   12. FACILITIES ICON GRID
   -------------------------------------------------------------------------- */
.fac__grid{ grid-template-columns:repeat(4,1fr); }
@media (max-width:991px){ .fac__grid{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:599px){ .fac__grid{ grid-template-columns:1fr 1fr; } }
/* Below 480 two columns cannot hold a facility name and its note: the grid forced the
   document to 389px at a 320px viewport. Measured, then dropped to one column. */
/* Two-up on a phone. Eighteen facilities stacked one per row made the list
   longer than the rest of the page put together.
   The icon moves ABOVE the label at the same time: beside it, the text had only
   about 100px and .fac__t carries overflow-wrap:anywhere, so long words split
   mid-syllable -- "Compliment/ary", "Housekee/ping". Stacking gives the label the
   card's full width and the breaks disappear. */
@media (max-width:479px){
  .fac__grid{ grid-template-columns:1fr 1fr; gap:var(--space-3); }
  .fac{ flex-direction:column; align-items:flex-start; gap:var(--space-2); padding:var(--space-4); }
}
/* grid items default to min-width:auto, so long labels push the track wider than the page */
.fac{ display:flex; align-items:center; gap:var(--space-4); padding:var(--space-5); background:var(--surface); border:1px solid var(--hair-soft); border-radius:var(--r-2); min-width:0; }
.fac__t{ min-width:0; overflow-wrap:anywhere; }
.fac .ic{ width:28px; height:28px; color:var(--accent-dk); }
.fac__t{ font-size:var(--text-sm); line-height:var(--lh-sm); font-weight:var(--w-bold); }
.fac__n{ display:block; font-size:var(--text-xs); line-height:var(--lh-xs); color:var(--ink-2); font-weight:400; }

/* --------------------------------------------------------------------------
   13. LOCATION — where we are and how you reach us, and nothing else
   -------------------------------------------------------------------------- */
.loc__addr{
  font-style:normal; font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink-2);
  padding-inline-start:var(--space-4); border-inline-start:2px solid var(--accent);
}
.loc__hubs{ display:grid; gap:var(--space-4); align-content:center; min-width:0; }
.hub{
  display:flex; align-items:center; gap:var(--space-4); min-width:0;
  padding:var(--space-5) var(--space-6);
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--r-2);
}
.hub > * + *{ margin-block-start:0; }          /* it lays out in a ROW (2.3 rule 4) */
.hub .ic{ width:26px; height:26px; color:var(--accent-dk); flex:0 0 auto; }

/* Name and distance at ONE size, reading as a single phrase. The distance used to be
   --text-h4 in the display face against a --text-sm name, which set the two halves of
   "Malvan Bus Stand ~3 km" at different sizes and pulled the eye to the number. */
.hub__t{
  flex:0 1 auto; min-width:0;
  font-size:var(--text-sm); line-height:var(--lh-sm); font-weight:var(--w-bold);
  color:var(--ink);
}
.hub__km{ font-weight:var(--w-body); color:var(--ink-2); white-space:nowrap; }

/* The leader: it takes the slack, so the link sits hard right whatever the name's
   length, and the row reads name -- rule -- action. Decorative, so aria-hidden in the
   markup and no contrast requirement. */
.hub__rule{
  flex:1 1 var(--space-6); min-width:var(--space-4);
  border-block-end:1px dashed var(--hair-soft);
  align-self:center; translate:0 -1px;
}
.hub__go{ flex:0 0 auto; font-size:var(--text-sm); line-height:var(--lh-sm); white-space:nowrap; }

/* Below 480 the row has no width left for a leader, so the link drops under the name
   and the rule goes away rather than being squeezed to a few pixels. */
@media (max-width:479px){
  .hub{ flex-wrap:wrap; }
  .hub__rule{ display:none; }
  .hub__go{ flex-basis:100%; }
}
.widget--alt .hub{ background:var(--ground); }

/* --------------------------------------------------------------------------
   14. GALLERY BAND (full-bleed, text over image)
   -------------------------------------------------------------------------- */
.bleed{ position:relative; isolation:isolate; min-height:440px; display:flex; align-items:flex-end; overflow:hidden; }
.bleed.widget--fill{ padding-block:calc(var(--gap-widget) / 2); }
.bleed__media{ position:absolute; inset:0; z-index:-2; }
.bleed__media img{ width:100%; height:100%; object-fit:cover; }
.bleed::before{
  content:""; position:absolute; inset:0; z-index:-1;
  background:
    linear-gradient(90deg,rgba(46,16,4,.86) 0%,rgba(46,16,4,.78) 42%,rgba(46,16,4,.72) 100%),
    linear-gradient(180deg,rgba(46,16,4,.10) 0%,rgba(46,16,4,.30) 100%);
}
.bleed .container{ padding-block:0; color:var(--on-dark); }
/* a teaser strip drawn from the same library the gallery page will hold */
.gal__strip{ grid-template-columns:repeat(4,1fr); }
@media (max-width:767px){ .gal__strip{ grid-template-columns:1fr 1fr; } }
.gal__strip li{ min-width:0; }
.gal__strip .ratio{ border:1px solid var(--rule-dark); }
/* The thumbnails ARE links now -- each opens the photograph in the lightbox -- so per
   the hover rule they earn a hover state. */
.gal__link{ display:block; border-radius:var(--r-2); }
.gal__link:focus-visible{ outline:2px solid var(--accent-lt); outline-offset:3px; }
@media (hover:hover){
  .gal__link:hover .ratio > img{ transform:scale(1.05); }
  .gal__link:hover .ratio{ border-color:var(--accent-lt); }
}

/* ---- LIGHTBOX (widget E2) --------------------------------------------------
   role=dialog, focus trapped and returned, Escape closes, arrows page. Without JS
   the thumbnails are plain links to the image file, which is the honest fallback. */
.lb{
  position:fixed; inset:0; z-index:100;
  display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  gap:var(--space-4); padding:var(--space-6) var(--space-4);
  background:rgba(23,10,2,.97);
}
/* the page chrome is hidden while the lightbox is open -- at 97% the header's own
   text and the ochre CTA still ghosted through the backdrop */
html.lb-open .site-header,
html.lb-open .fab,
html.lb-open .cookie{ display:none; }
.lb__fig{ margin:0; display:flex; flex-direction:column; align-items:center; gap:var(--space-4); min-width:0; }
.lb__fig > * + *{ margin-block-start:0; }
.lb__fig img{
  max-width:100%; max-height:76svh; width:auto; height:auto;
  border-radius:var(--r-2); background:var(--brand);
}
.lb__cap{
  display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-2) var(--space-4);
  color:var(--on-dark); font-size:var(--text-sm); line-height:var(--lh-sm); text-align:center; max-width:64ch;
}
.lb__cap span:first-child{ font-family:var(--mono,var(--font-body)); color:var(--accent-lt); white-space:nowrap; }
.lb__close{
  position:absolute; inset-block-start:var(--space-4); inset-inline-end:var(--space-4);
  width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--rule-dark); border-radius:var(--r-circle); color:var(--on-dark);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.lb__nav{
  width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--rule-dark); border-radius:var(--r-circle); color:var(--on-dark);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.lb__close:hover,.lb__nav:hover{ background:var(--accent); color:var(--ink); border-color:var(--accent); }
.lb__close:focus-visible,.lb__nav:focus-visible{ outline:2px solid var(--accent-lt); outline-offset:2px; }
.lb .ic{ width:18px; height:18px; }
@media (max-width:599px){
  .lb{ grid-template-columns:minmax(0,1fr); }
  .lb__nav{ position:absolute; inset-block-end:var(--space-6); }
  .lb__nav--prev{ inset-inline-start:var(--space-6); }
  .lb__nav--next{ inset-inline-end:var(--space-6); }
  .lb__fig img{ max-height:62svh; }
}
.bleed h2{ color:var(--on-dark); }
.bleed .lead{ color:var(--on-dark); opacity:.9; }

/* --------------------------------------------------------------------------
   15. SWIPER (project skin — vendor CSS is appended below the markers)
   -------------------------------------------------------------------------- */
.sw{ position:relative; min-width:0; }
.sw .swiper{ overflow:hidden; border-radius:var(--r-2); min-width:0; width:100%; }
.sw .swiper-slide{ min-width:0; }
/* Slider controls live in a row BELOW the track, never floating over the slides.
   Edge-centred arrows sat on top of the room cards' copy at every width under 1400 --
   the control obscured the content it was there to reveal. */
.sw__ctrls{
  display:flex; align-items:center; justify-content:center;
  gap:var(--space-4); margin-block-start:var(--space-5);
  /* 5 bullets at the 44px target floor plus two arrows is 340px, which overflowed a
     320px viewport by 26px. The row wraps rather than shrinking any tap target. */
  flex-wrap:wrap; min-width:0;
}
.sw__ctrls > * + *{ margin-block-start:0; }
/* The room-card, experiences and facilities sliders carry .row-reset, and the reset
   rule (.widget .container .row-reset > * + *, 0,3,0) zeroed this row's margin too: their
   dots sat 18px under the cards while every other slider's sat 38px under (client,
   2026-10-23: one gap everywhere). Four classes restore the same --space-5. */
.widget .container .sw.row-reset > .sw__ctrls{ margin-block-start:var(--space-5); }
.sw__nav{
  position:static; flex:0 0 auto;
  width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--rule); border-radius:50%; color:var(--ink);
  box-shadow:var(--shadow-sm);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.sw__nav:hover:not(:disabled){ background:var(--accent); border-color:var(--accent); color:var(--ink); }
.sw__nav:disabled{ opacity:.35; cursor:not-allowed; }
.sw__nav .ic{ width:16px; height:16px; }
.sw__ctrls .sw__dots{ margin-block-start:0; }
.sw__dots{ display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-2); margin-block-start:var(--space-5); min-width:0; }
.sw__dots .swiper-pagination-bullet{
  width:44px; height:44px; margin:0 !important; border-radius:var(--r-1); background:transparent; opacity:1;
  position:relative; display:inline-flex; align-items:center; justify-content:center;
}
.sw__dots .swiper-pagination-bullet::after{ content:""; width:8px; height:8px; border-radius:var(--r-circle); background:var(--rule); transition:background-color var(--dur) var(--ease),width var(--dur) var(--ease); }
.sw__dots .swiper-pagination-bullet-active::after{ background:var(--accent-dk); width:24px; border-radius:var(--r-pill); }
.band--dark .sw__dots .swiper-pagination-bullet::after{ background:rgba(247,234,220,.45); }
.band--dark .sw__dots .swiper-pagination-bullet-active::after{ background:var(--accent-lt); }

/* Rooms: ONE set of cards in the DOM. At >=992 the track is a 3-up grid, because 3 cards
   do not overflow a 1240px container and a slider that nearly fits is a layout accident
   (SKILL 2.8c). Below 992 the same markup is driven by Swiper, where it genuinely overflows. */
.rooms__sw .swiper-slide{ height:auto; display:flex; }
@media (min-width:992px){ .rooms__sw .sw__ctrls{ display:none; } }
.rooms__sw .card{ width:100%; }
@media (min-width:992px){
  .rooms__sw .swiper{ overflow:visible; }
  .rooms__sw .swiper-wrapper{
    display:grid; grid-template-columns:repeat(3,1fr); gap:var(--space-6);
    transform:none !important;
  }
  .rooms__sw .swiper-slide{ width:auto !important; margin-right:0 !important; }
  /* two cards in a three-track grid sat against the left edge with a dead third
     column; Our Other Rooms is always a pair and was asked to be centred */
  .rooms__sw--pair .swiper-wrapper{
    grid-template-columns:repeat(2,minmax(0,384px)); justify-content:center;
  }
  .rooms__sw .sw__nav{ display:none; }
}

/* --------------------------------------------------------------------------
   16. FOOTER (§2.8b)
   -------------------------------------------------------------------------- */
.site-footer{ margin-block-start:var(--gap-widget); background:var(--brand-deep); color:var(--on-dark); padding-block:var(--space-20) var(--space-8);
  /* the watermark positions against this, and must not bleed past the band */
  position:relative; overflow:hidden; }
.site-footer--flush{ margin-block-start:0; }
/* a rule between the three blocks -- vertical where they sit side by side, horizontal
   once they stack, so the separation survives the breakpoint */
.foot{ display:grid; grid-template-columns:1fr 1.1fr 1.32fr; gap:var(--space-10); }
/* the third column is the widest of the three on purpose: it carries a 37-character
   email address, and at 16px that address sets the column's minimum. */
.foot > * + *{ border-inline-start:1px solid var(--hair-dark); padding-inline-start:var(--space-10); }
@media (max-width:991px){
  .foot{ grid-template-columns:1fr 1fr; gap:var(--space-8); }
  .foot > * + *{ border-inline-start:0; padding-inline-start:0; }
  .foot > :nth-child(n+2){ border-block-start:1px solid var(--hair-dark); padding-block-start:var(--space-8); }
}
@media (max-width:599px){
  .foot > :nth-child(n+2){ border-block-start:1px solid var(--hair-dark); padding-block-start:var(--space-8); }
}

@media (max-width:599px){ .foot{ grid-template-columns:1fr; } }
.foot > *{ min-width:0; }
.foot__col{ min-width:0; }
.foot__col > * + *{ margin-block-start:var(--space-4); }
/* .foot__h, not h3. A content h3 is 26px and these were 16px -- one level rendered at
   two sizes. The words still label their group: each is the accessible name of the
   <nav>/<section> beside it via aria-labelledby, so the footer keeps its structure for
   anyone navigating by landmark. Scoped `.foot .foot__h` so it outranks `.foot__col p`,
   which would otherwise set the size from a plain-paragraph rule. */
.foot .foot__h,
.foot .foot__by{ font-family:var(--font-body); font-size:var(--text-body); line-height:var(--lh-body); letter-spacing:var(--tr-eyebrow); text-transform:uppercase; font-weight:var(--w-bold); color:var(--accent-lt); }
.foot .foot__h + *{ margin-block-start:var(--space-2); }
/* the whole footer body reads at 16px; only the legal strip steps down to 14px */
.foot__col p{ font-size:var(--text-body); line-height:var(--lh-body); }
.foot__brand{ display:flex; align-items:center; gap:var(--space-3); }
.foot__brand img{ width:56px; height:56px; border-radius:var(--r-circle); }
.foot__name{ font-family:var(--font-display); font-size:var(--text-h4); line-height:1.2; }
.foot address{ font-style:normal; font-size:var(--text-body); line-height:var(--lh-body); opacity:.86; max-width:34ch; }
.foot__links{ display:grid; grid-template-columns:1fr 1fr; gap:0 var(--space-6); min-width:0; }
/* a long label such as 'Meetings & Celebrations' pushed the column past a 320px
   viewport, because a grid item's min-width defaults to auto */
/* break only where a break is sensible -- `anywhere` split the email mid-word */
.foot__links a,.foot__contact a{ min-width:0; overflow-wrap:break-word; }
.foot__contact a .lnk{ min-width:0; overflow-wrap:break-word; }
/* The number is the link and the role word is not, so they are two elements -- but
   they are one phrase and must read as one line. .foot__contact lays its children out
   as a column, which put "· Resort" on a line of its own under the number. Pairing
   them in a flex row keeps them together and still leaves only the digits clickable.
   On a phone the row wraps as a unit rather than splitting mid-phrase. */
.foot__contact{ display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-2); }
.foot__line{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-2); min-width:0; }
.foot__line a{ min-height:32px; }
.foot__role{ color:var(--on-dark); opacity:.75; font-size:var(--text-body); white-space:nowrap; }
@media (max-width:599px){ .foot__links{ grid-template-columns:1fr 1fr; } }
/* Two columns all the way down: the links are short, and one column made this
   block twice as tall on the smallest screens for nothing. */
@media (max-width:399px){ .foot__links{ grid-template-columns:1fr 1fr; } }
.foot__links a,.foot__contact a{ display:flex; align-items:center; gap:var(--space-2); min-height:32px; font-size:var(--text-body); line-height:var(--lh-body); opacity:.9; }
.foot__links a:hover,.foot__contact a:hover{ opacity:1; color:var(--accent-lt); }

/* No underline on the two footer link LISTS (client, 2026-09-29). The underline exists
   so a link is not identified by colour alone -- which matters inside a paragraph,
   where a link has to be told apart from the prose around it. These are lists of
   nothing but links under their own heading, so there is nothing to confuse them with.
   It comes back on hover and on keyboard focus, so the affordance is still there the
   moment anyone reaches for one. The address, the legal row and any link sitting in
   footer prose keep theirs. */
.foot__links a .lnk,
.foot__contact a .lnk{ text-decoration:none; }
.foot__links a:hover .lnk,
.foot__contact a:hover .lnk,
.foot__links a:focus-visible .lnk,
.foot__contact a:focus-visible .lnk{ text-decoration:underline; text-decoration-thickness:2px; }

/* The lockup is a link home now. It carries no underline and no colour shift -- a logo
   that changes colour on hover reads as a broken image more than as a link -- so the
   cursor and a focus ring are what say it is one. */
.foot__brand{ color:inherit; text-decoration:none; }
.foot__brand:focus-visible{ outline:2px solid var(--accent-lt); outline-offset:4px; border-radius:var(--r-1); }
.foot__contact .ic{ width:26px; height:26px; color:var(--accent-lt); }
.social{ display:flex; gap:var(--space-3); }
.social a{
  width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--rule-dark); border-radius:var(--r-circle); color:var(--on-dark);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.social a:hover{ background:var(--accent); color:var(--ink); border-color:var(--accent); }
.social .ic{ width:18px; height:18px; }
/* three tracks so Sitemap sits in the true centre of the page, not in whatever space
   the two outer items happen to leave */
.legal{
  margin-block-start:var(--space-8); padding-block-start:var(--space-6);
  border-block-start:1px solid var(--hair-dark);
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:var(--space-4);
}
.legal > * + *{ margin-block-start:0; }
.legal > :nth-child(2){ justify-self:center; }
.legal > :nth-child(3){ justify-self:end; text-align:right; }
@media (max-width:767px){
  .legal{ grid-template-columns:1fr; justify-items:center; text-align:center; gap:var(--space-3); }
  .legal > :nth-child(3){ justify-self:center; text-align:center; }
}
.legal p,.legal a{ font-size:var(--text-sm); line-height:var(--lh-sm); opacity:.8; }
.legal a{ min-height:32px; display:inline-flex; align-items:center; }
.legal a:hover{ opacity:1; color:var(--accent-lt); }
.legal__links{ display:flex; flex-wrap:wrap; gap:var(--space-3) var(--space-5); }

/* --------------------------------------------------------------------------
   17. FLOATING ACTIONS + COOKIE NOTICE (the only two overlays, §2.8)
   -------------------------------------------------------------------------- */
/* Two floating stacks, one per side. Reaching the resort is a left-hand action and
   getting back to the top is a right-hand one, so neither shares a corner with the
   other and a mis-tap on 'call' is no longer one pixel from 'scroll'. */
/* --------------------------------------------------------------------------
   MOBILE BOOKING BAR
   -------------------------------------------------------------------------- */
/* Phones only. The header CTA is hidden at this width and this bar carries it
   instead, so the thumb has one obvious target at the bottom of the screen rather
   than a button in the far top corner. It sits above the cookie bar, and the
   floating actions lift by its height so the two never stack on top of each other.
   Held back until JS runs: see site.js -- every page shows it at once, the homepage
   included, since below 768 the homepage hero carries no booking form either. */
:root{ --mobar-h:0px; }
.mobar{ display:none; }
@media (max-width:767px){
  /* One solid strip, edge to edge -- the bar IS the button. No white tray around
     it and no second action beside it: at this width there is room for exactly one
     thing worth tapping. */
  .mobar{
    position:fixed; z-index:60; inset-inline:0;
    inset-block-end:var(--cookie-h);
    display:block;
    box-shadow:0 -6px 24px rgba(46,16,4,.16);
    transform:translateY(100%);
    transition:transform var(--dur) var(--ease);
  }
  .mobar.is-in{ transform:none; }
  html:not(.js) .mobar{ transform:none; }
  .mobar__book{
    display:flex; align-items:center; justify-content:center;
    width:100%; min-height:56px;
    padding-block:var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    padding-inline:var(--gutter);
    background:var(--accent); color:var(--ink);
    font-size:var(--text-btn); font-weight:var(--w-bold);
    letter-spacing:var(--tr-btn); text-transform:uppercase;
  }
  .mobar__book:hover,.mobar__book:focus-visible{ background:var(--accent-dk); color:var(--on-dark); }
  .site-footer{ padding-block-end:calc(var(--space-10) + var(--mobar-h)); }
}

/* The header's Book Now is gone below 768: the mobile bar carries it. Leaving both
   put two primary actions on one small screen and crowded the logo. */
@media (max-width:767px){ .header-actions{ display:none; } }

.fab{
  position:fixed; z-index:50;
  inset-block-end:calc(var(--cookie-h) + var(--space-6));
  display:flex; flex-direction:column; gap:var(--space-3);
  /* It overlapped the booking form by 50px between 1024 and 1280. Rather than nudge it
     around the form, it simply does not exist while the form is the thing on screen:
     in the fold the form is the call to action, below the fold these are. */
  opacity:0; visibility:hidden; transform:translateY(var(--space-2));
  transition:opacity var(--dur) var(--ease),visibility var(--dur) var(--ease),transform var(--dur) var(--ease);
}
.fab.is-in{ opacity:1; visibility:visible; transform:none; }
html:not(.js) .fab{ opacity:1; visibility:visible; transform:none; }
.fab a{
  width:48px; height:48px; display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--r-circle); background:var(--brand); color:var(--on-dark); box-shadow:var(--shadow-md);
  border:1px solid var(--accent-lt);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.fab a:hover{ background:var(--accent); color:var(--ink); }
.fab .ic{ width:26px; height:26px; }
/* Pinned to the viewport edges, not to the content column, so they sit in the same
   place at every width. What they must never cover is the booking form or the h1 --
   probe P18 asserts that. */
.fab--contact{ inset-inline-start:var(--space-6); }
.fab--top{ inset-inline-end:var(--space-6); }
@media (max-width:767px){
  /* + --mobar-h so the actions ride above the mobile booking bar rather than
     sitting behind it; the variable is 0 whenever the bar is hidden */
  .fab{ inset-block-end:calc(var(--cookie-h) + var(--mobar-h) + var(--space-4)); }
  .fab--contact{ inset-inline-start:var(--space-4); }
  .fab--top{ inset-inline-end:var(--space-4); }
}

.cookie{
  position:fixed; z-index:55; inset-block-end:0; inset-inline:0;
  background:var(--brand-deep); color:var(--on-dark);
  padding:var(--space-4) var(--gutter);
}
.cookie__in{ max-width:var(--container); margin-inline:auto; display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-4); justify-content:space-between; }
.cookie p{ font-size:var(--text-sm); line-height:var(--lh-sm); max-width:72ch; }

/* --------------------------------------------------------------------------
   18. SCROLL REVEAL — additive only; without JS the content is already visible
   -------------------------------------------------------------------------- */
html:not(.js) .reveal{ opacity:1; transform:none; }
html.js .reveal{ opacity:0; transform:translateY(16px); transition:opacity var(--dur-reveal) var(--ease),transform var(--dur-reveal) var(--ease); }
html.js .reveal.is-in{ opacity:1; transform:none; }

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; }
  html.js .reveal{ opacity:1; transform:none; }
}

/* --------------------------------------------------------------------------
   19. PRINT
   -------------------------------------------------------------------------- */
@media print{
  .site-header,.fab,.cookie,.vctrl,.sw__nav,.sw__dots,.drawer{ display:none !important; }
  body{ background:#fff; color:#000; }
}

/* ==========================================================================
   20. INNER-PAGE WIDGETS
   Appended to the one project stylesheet. Every value here is a token; nothing
   below introduces a colour, a size or a spacing step the homepage does not
   already use. Widget IDs match WIDGET-LIBRARY.md.
   ========================================================================== */

/* ---- A6: inner hero -------------------------------------------------------
   The homepage hero is 100svh because it carries the booking form. An inner
   hero carries nothing, so it is a band, not a screen. No copy sits over it. */
.page-hero{ position:relative; min-height:clamp(320px,60svh,600px); overflow:hidden; background:var(--brand-deep); }
.page-hero > img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
/* the same warm vignette the homepage hero carries, so the header stays legible */
.page-hero::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  /* The top stop is set by the WORST hero on the site, not by the prettiest. The nav
     sits over this band, and against the Dining hero it measured 4.10:1 at .55 --
     under the 4.5 floor for normal text. Measured, not guessed. */
  background:linear-gradient(to bottom, rgba(46,16,4,.72) 0%, rgba(46,16,4,.14) 42%, rgba(46,16,4,.32) 100%);
}

/* ---- A7: breadcrumb ------------------------------------------------------- */
/* The section under the breadcrumb starts with no top spacing of its own, so this
   bottom padding IS the gap -- 16px (12px on a phone) left the first heading crowding
   the trail on every inner page (client, 2026-10-23). --gap-inner, the site's own
   in-section rhythm: 48px, and 32px on a phone. */
.crumbs{ padding-block:var(--space-6) var(--gap-inner); }
.crumbs ol{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2); list-style:none; margin:0; padding:0; }
.crumbs li{ display:flex; align-items:center; gap:var(--space-2); font-size:var(--text-crumb); color:var(--ink-2); }
.crumbs li + li::before{ content:"/"; color:var(--rule); }
.crumbs a{ color:var(--ink-2); }
.crumbs a:hover{ color:var(--accent-dk); }
.crumbs [aria-current="page"]{ color:var(--ink); }
@media (max-width:767px){ .crumbs{ padding-block:var(--space-4) var(--gap-inner); } }

/* ---- B1 / B2: page opening ------------------------------------------------
   B1 is the narrative opener: H1 left, lead right. B2 (`.intro--centre`) is the
   utility opener -- one centred column, used where the page is a tool. */
.intro{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-10); align-items:start; }
.intro > *{ min-width:0; }
.intro > * + *{ margin-block-start:0; }
/* the heading-block rhythm is rule -> heading 4px, and `margin:0` was overriding it,
   so the h1 sat flush against the motif while every h2 on the site had its 4px */
.intro h1{ margin-block:var(--space-1) 0; font-size:var(--text-h1-page); line-height:var(--lh-h1-page); }
.intro__copy > * + *{ margin-block-start:var(--space-3); }
@media (max-width:899px){ .intro{ grid-template-columns:1fr; gap:var(--space-6); } }
.intro--centre{ grid-template-columns:1fr; max-width:72ch; margin-inline:auto; text-align:center; gap:var(--space-4); }
.intro--centre .widget__head{ align-items:center; }

/* ---- D1: sibling pills ----------------------------------------------------
   A row of links to the other items in the set. Scrolls horizontally below 768
   rather than wrapping into a second row that looks like a second widget. */
.pills{ display:flex; flex-wrap:wrap; gap:var(--space-3); list-style:none; margin:0; padding:0; }
.pills > * + *{ margin-block-start:0; }
.pills a{
  display:inline-flex; align-items:center; min-height:44px; padding:0 var(--space-5);
  border:1px solid var(--rule); border-radius:var(--r-pill);
  font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--ink);
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.pills a:hover{ background:var(--brand); border-color:var(--brand); color:var(--on-dark); }
.pills [aria-current="page"]{ background:var(--brand); border-color:var(--brand); color:var(--on-dark); pointer-events:none; }
@media (max-width:767px){
  .pills{ flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none; padding-block-end:var(--space-2); }
  .pills::-webkit-scrollbar{ display:none; }
  .pills a{ white-space:nowrap; }
}

/* ---- D4: fact table -------------------------------------------------------
   Specs live here and never in the prose (CONTENT-RULES A3.2 rule 4). */
.spec{ width:100%; border-collapse:collapse; }
.spec caption{ text-align:start; font-family:var(--font-display); font-size:var(--text-h4); line-height:var(--lh-h4); color:var(--ink); padding-block-end:var(--space-4); }
.spec th,.spec td{ text-align:start; padding:var(--space-4) 0; border-block-end:1px solid var(--hair-soft); font-size:var(--text-body); line-height:var(--lh-body); vertical-align:top; }
.spec th{ font-weight:var(--w-body); color:var(--ink-2); width:45%; }
.spec td{ color:var(--ink); }
.spec tr:last-child th,.spec tr:last-child td{ border-block-end:0; }
/* the same table on a dark band: on-dark ink, dark hairlines, and a header row */
.band--dark .spec th,.band--dark .spec td{ border-block-end:1px solid var(--hair-dark); }
.band--dark .spec th{ color:var(--on-dark); opacity:.78; }
.band--dark .spec td{ color:var(--on-dark); }
.band--dark .spec caption{ color:var(--on-dark); }
.travel{ margin-block-start:var(--space-8); }
.travel thead th{
  font-size:var(--text-sm); line-height:var(--lh-sm); letter-spacing:var(--tr-eyebrow);
  text-transform:uppercase; color:var(--accent-lt); opacity:1; font-weight:var(--w-bold);
}
.travel th:first-child{ width:26%; }
.travel td:first-of-type{ width:26%; }
@media (max-width:767px){
  /* three columns do not fit a phone; the row becomes a stacked record */
  .travel thead{ position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
  .travel tr{ display:block; padding-block:var(--space-4); border-block-end:1px solid var(--hair-dark); }
  .travel tr:last-child{ border-block-end:0; }
  /* `.band--dark .spec th` is (0,2,1) and beat the plain `.travel th` reset regardless
     of source order, so each stacked cell kept a part-width rule under it. Matched. */
  .band--dark .travel th,.band--dark .travel td{ display:block; width:auto; padding:0; border:0; }
  .travel th{ font-family:var(--font-display); font-size:var(--text-h4); opacity:1; }
  .travel td:first-of-type{ color:var(--accent-lt); font-size:var(--text-sm); margin-block:var(--space-1); }
}

/* ---- D3: amenities panel --------------------------------------------------
   A bordered panel on the ground fill; it inverts to --ground on white, exactly
   as every card does. */
.amen{ border:1px solid var(--rule); border-radius:var(--r-2); padding:var(--space-8); background:var(--surface); }
.widget--alt .amen{ background:var(--ground); }
.amen__grid{
  display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
  /* Every row the same height as the tallest. Two of the seventeen amenities carry a
     bracketed note on a second line, and a grid row is as tall as its tallest cell --
     so the row holding "In-Room Dining (7:30 AM to 10:30 PM)" stood taller than the
     rest and the single item below it, Laundry Service, read as though something had
     pushed it down. Equal rows put the same space under every label, so no one row
     looks padded. 1fr rather than a min-height in pixels: it follows the type if the
     scale ever changes, and it needs no number that could go stale. */
  grid-auto-rows:1fr;
  gap:var(--space-6) var(--space-8); list-style:none; margin:0; padding:0;
}
.amen__grid > * + *{ margin-block-start:0; }
/* flex-start, not center. Grid stretches both cells of a row to the taller one's
   height; a centred single-line item then floats down inside that taller box while
   its two-line neighbour starts at the top, so "Daily Housekeeping" sat 10px below
   "In-Room Dining". Top-aligning every item puts all first lines on the same y. The
   icon is 26px against a 27.2px first-line box, so it takes 1px to sit optically
   centred on that line. */
.amen__grid li{ display:flex; align-items:flex-start; gap:var(--space-3); font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink-2); min-width:0; }
.amen__grid .ic{ width:26px; height:26px; flex:0 0 auto; color:var(--accent-dk); margin-block-start:1px; }
/* inside a split the panel has half the width, so it takes half the columns */
.amen__grid--2{ grid-template-columns:repeat(2,minmax(0,1fr)); }
@media (max-width:991px){ .amen__grid{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (max-width:767px){ .amen{ padding:var(--space-6); } .amen__grid{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--space-5); } }
@media (max-width:419px){ .amen__grid{ grid-template-columns:1fr; } }

/* ---- E1: filtered gallery grid --------------------------------------------
   The chip bar ships hidden and JS reveals it, so with JS off the page is a
   plain unfiltered grid rather than a set of dead buttons. */
.gal-filter{ display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-3); list-style:none; margin:0; padding:0; }
.gal-filter > * + *{ margin-block-start:0; }
/* The same tab language the Location page uses, at the client's direction: no pills.
   An underline carries the selected state instead of a filled capsule, so the filter
   reads as a set of tabs over the grid rather than a row of buttons floating above it. */
.gal-filter{ border-block-end:1px solid var(--hair-soft); gap:var(--space-2); }
.gal-filter button{
  min-height:44px; padding:var(--space-3) var(--space-5);
  border:0; border-block-end:2px solid transparent; border-radius:0; margin-block-end:-1px;
  background:none; color:var(--ink-2);
  font-family:var(--font-body); font-size:var(--text-sm); line-height:var(--lh-sm);
  font-weight:var(--w-bold); letter-spacing:var(--tr-btn); text-transform:uppercase;
  cursor:pointer; white-space:nowrap;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.gal-filter button:hover{ color:var(--accent-dk); }
.gal-filter [aria-selected="true"]{ color:var(--ink); border-block-end-color:var(--accent); }
.gal-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--space-6); list-style:none; margin:0; padding:0; }
.gal-grid > * + *{ margin-block-start:0; }
.gal-grid li{ min-width:0; }
.gal-grid a{ display:block; border-radius:var(--r-2); overflow:hidden; }
.gal-grid .ratio > img{ transition:transform var(--dur-reveal) var(--ease); }
@media (hover:hover){ .gal-grid a:hover .ratio > img{ transform:scale(1.05); } }
@media (max-width:899px){ .gal-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--space-4); } }
@media (max-width:519px){ .gal-grid{ grid-template-columns:1fr; } }
.gal-count{ font-size:var(--text-sm); color:var(--ink-2); text-align:center; }

/* ---- E3: FAQ accordion ----------------------------------------------------
   Native <details>/<summary>. It works with zero JS and must never be rebuilt
   in JS. */
.faq{ border-block-start:1px solid var(--hair-soft); }
.faq details{ border-block-end:1px solid var(--hair-soft); }
.faq summary{
  display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-5);
  padding-block:var(--space-5); cursor:pointer; list-style:none;
  font-family:var(--font-display); font-size:var(--text-h4); line-height:var(--lh-h4); color:var(--ink);
  min-height:44px;
}
.faq summary::-webkit-details-marker{ display:none; }
.faq summary::after{
  content:""; flex:0 0 auto; width:12px; height:12px; margin-block-start:.35em;
  border-inline-end:1.8px solid var(--accent-dk); border-block-end:1.8px solid var(--accent-dk);
  transform:rotate(45deg); transition:transform var(--dur) var(--ease);
}
.faq details[open] summary::after{ transform:rotate(225deg); }
.faq summary:hover{ color:var(--accent-dk); }
.faq__a{ padding-block-end:var(--space-5); max-width:78ch; }
.faq__a > * + *{ margin-block-start:var(--space-3); }
.faq h2{ margin-block-start:0; }
/* A group heading on the FAQ page is followed straight by its accordion, and nothing
   spaced the pair: .prose spaces p/ul/ol only, so the accordion's top rule sat 0px under
   "About Rooms" and every other group heading (client, 2026-10-23). Closer than the 40px
   before the next heading, so each heading reads as belonging to the questions below it. */
h2 + .faq,h3 + .faq{ margin-block-start:var(--space-5); }
@media (prefers-reduced-motion:reduce){ .faq summary::after{ transition:none; } }

/* ---- E4: contact block + map ---------------------------------------------- */
/* stretch, not start: the details card and the map are one row of two blocks, and a
   460px map beside a 250px card reads as two unrelated things */
.contact{ display:grid; grid-template-columns:1fr 1.1fr; gap:var(--space-10); align-items:stretch; }
.contact > *{ min-width:0; }
.contact > * + *{ margin-block-start:0; }
@media (max-width:899px){ .contact{ grid-template-columns:1fr; gap:var(--space-8); } }
.cblock{ border:1px solid var(--rule); border-radius:var(--r-2); padding:var(--space-8); background:var(--surface); }
.contact .cblock{ display:flex; flex-direction:column; }
.contact .cblock .actions{ margin-block-start:auto; padding-block-start:var(--space-6); }
.widget--alt .cblock{ background:var(--ground); }
.cblock dl{ margin:0; }
.cblock dt{ font-size:var(--text-sm); line-height:var(--lh-sm); letter-spacing:var(--tr-eyebrow); text-transform:uppercase; color:var(--accent-dk); }
.cblock dd{ margin:0 0 var(--space-6); font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink); }
.cblock dd:last-of-type{ margin-block-end:0; }
.cblock dd a{ color:var(--ink); }
.cblock dd a:hover{ color:var(--accent-dk); }
.map{ border-radius:var(--r-2); overflow:hidden; border:1px solid var(--rule); min-height:400px; }
.map iframe{ display:block; width:100%; height:100%; min-height:400px; border:0; }
@media (max-width:767px){ .map,.map iframe{ min-height:300px; } }

/* ---- E5: enquiry form -----------------------------------------------------
   Field styling is the booking form's, so the site has one input, not two. */
/* The form sits inside a centred widget head, so it has to re-declare its own
   alignment or every label centres itself over its field. 860px is about 2 x 40
   characters per column, which is a form rather than a banner. */
/* The column count comes from the form, not from this rule. Both forms used to be
   locked to two columns, which suits the enquiry form's four short fields exactly
   (2 x 2) but left the contact form's three sitting 2 + 1 -- the phone field alone on
   its own row beside 420px of nothing. Measured: name and email at y1728, phone at
   y1831 with an empty cell next to it, then the message jumping to y1934.
   Each form now declares how many short fields it has, so neither leaves a hole. */
.form{
  display:grid; grid-template-columns:repeat(var(--form-cols,2),minmax(0,1fr));
  gap:var(--space-5);
  max-width:860px; margin-inline:auto; text-align:start;
}
/* Three short fields, so three columns: two would leave the third alone beside an
   empty cell. A CLASS, not an inline style -- an inline --form-cols would outrank
   every media query below and the form would hold three columns all the way down. */
.form--trio{ --form-cols:3; }

/* The trio stacks straight to one column, never to two, because two is exactly the
   arrangement that strands the third field. It stacks earlier than the pair does: at
   600px three columns put the phone input at 62px, which is not wide enough to type a
   number into, and it is still only 113px at 768. */
@media (max-width:859px){ .form--trio{ --form-cols:1; } }
@media (max-width:599px){ .form{ --form-cols:1; } }
.form > * + *{ margin-block-start:0; }
.form__field{ display:flex; flex-direction:column; gap:var(--space-2); min-width:0; }
.form__field--wide{ grid-column:1 / -1; }
.form__field label{ font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--ink-2); }
.form__field input,.form__field textarea,.form__field select{
  width:100%; min-height:48px; padding:var(--space-3) var(--space-4);
  border:1px solid var(--rule); border-radius:var(--r-1); background:var(--surface);
  font-family:var(--font-body); font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink);
}
.form__field textarea{ min-height:132px; resize:vertical; }
.form__field input:focus-visible,.form__field textarea:focus-visible{ outline:2px solid var(--accent-dk); outline-offset:2px; }
.form__err{ font-size:var(--text-sm); line-height:var(--lh-sm); color:#9B2C15; min-height:0; }
.form__err:empty{ display:none; }
.form [aria-invalid="true"]{ border-color:#9B2C15; }
.form__actions{ grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-4); }
.form__note{ grid-column:1 / -1; font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--ink-2); }
/* Field hints. Quieter than the label and the input, and tied to the field with
   aria-describedby so a screen reader reads the format BEFORE the person types,
   rather than only hearing it as an error afterwards. The placeholder alone was
   not enough: it disappears the moment they start typing. */
.form__hint{
  margin-block-start:var(--space-1);
  font-size:var(--text-xs); line-height:var(--lh-xs); color:var(--ink-2);
}
.form__status{ grid-column:1 / -1; font-size:var(--text-body); line-height:var(--lh-body); color:var(--accent-dk); }
.form__status:empty{ display:none; }
.form__consent{
  grid-column:1 / -1; display:flex; align-items:flex-start; gap:var(--space-3);
  font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--ink-2); cursor:pointer;
}
.form__consent input{ width:24px; height:24px; margin-block-start:0; flex:0 0 auto; accent-color:var(--accent-dk); }
/* forms.js injects its own `.ct-err` paragraphs and a generic red; re-point them at
   the site's type scale so a validation message is not the one element in a
   different typeface and size. The red itself stays -- it is the error signal. */
.form .ct-err{ font-size:var(--text-sm); line-height:var(--lh-sm); font-family:var(--font-body); }
.form .ct-invalid{ border-color:#9B2C15 !important; }
/* forms.js bakes its calendar glyph as a data URI stroked in its own blue. One extra
   class outranks it, so the picker matches the palette instead of importing one. */
.form input[type="date"].ct-date::-webkit-calendar-picker-indicator{
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238F5D16' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
}
#g-recaptcha-contact,#g-recaptcha-events{ grid-column:1 / -1; }
@media (max-width:599px){ .form{ grid-template-columns:1fr; } }

/* ---- E7: policy prose -----------------------------------------------------
   One measured column. No cards -- a policy is read, not scanned. */
.prose{ max-width:72ch; }
.prose h2{ margin-block-start:var(--space-10); }
.prose h2:first-child{ margin-block-start:0; }
.prose h3{ margin-block-start:var(--space-6); font-size:var(--text-h4); line-height:var(--lh-h4); }
.prose p,.prose ul,.prose ol{ margin-block-start:var(--space-3); }
/* The global reset at the top of this file sets list-style:none on every ul and ol,
   which is right for the nav, card grids and chip rows that make up most of this site.
   Prose is the exception: it asks for bullets and had none, so "put this in bullets"
   produced markup that was a real list and looked like a run of indented paragraphs.
   The marker colour below had nothing to colour. Restored here, scoped to .prose. */
.prose ul,.prose ol{ padding-inline-start:var(--space-6); }
.prose ul{ list-style:disc; }
.prose ol{ list-style:decimal; }
/* --ink, matching .prose p. These were --ink-2, so a page that moves between a
   bulleted section and a prose one -- Booking and Cancellation Rules against
   Privacy Policy on Terms -- visibly changed text colour partway down. */
.prose li{ margin-block-start:var(--space-2); font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink); }
.prose li::marker{ color:var(--accent-dk); }

/* ---- Sitemap link list ---------------------------------------------------- */
.linklist{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--space-10); list-style:none; margin:0; padding:0; }
.linklist > * + *{ margin-block-start:0; }
.linklist > li{ min-width:0; }
/* No font-size here: these are section headings like any other, so they inherit the
   site's h2 token. They used to be pinned to --text-h4, which rendered them at 19px
   while every other page's h2 was 36px -- the same level at two sizes across pages.
   Only the rule and the space under it are this list's own. */
.linklist h2{ padding-block-end:var(--space-3); border-block-end:1px solid var(--hair-soft); }
.linklist ul{ list-style:none; margin:var(--space-4) 0 0; padding:0; }
.linklist ul li + li{ margin-block-start:var(--space-3); }
.linklist ul a{ font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink-2); }
.linklist ul a:hover{ color:var(--accent-dk); }
@media (max-width:899px){ .linklist{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--space-8); } }
@media (max-width:519px){ .linklist{ grid-template-columns:1fr; } }

/* ---- Experience cards -----------------------------------------------------
   Card = a 4:3 visual, then name, distance, and one line. The visual slot holds the
   mark on a tinted panel because the image library carries NO experience photography
   (FACT-SHEET.md needs-input 4); a photograph drops into the same figure with no
   change to the card. */
/* the homepage deep-links to individual experiences, so a card arrived at by
   fragment must clear the sticky header rather than sit under it */
.xp[id],
#locTravelH,
/* the enquiry section, which "Send a Message" jumps to -- without this the sticky bar
   covers the eyebrow the jump exists to show */
.widget[id$="-write"]{ scroll-margin-block-start:calc(var(--header-h, 72px) + var(--space-6)); }
.xp-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--space-6); list-style:none; margin:0; padding:0; }
.xp-grid > * + *{ margin-block-start:0; }
.xp{
  min-width:0; display:flex; flex-direction:column; overflow:hidden;
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--r-2);
}
.widget--alt .xp{ background:var(--ground); }
.xp__fig{ background:var(--brand); border-block-end:1px solid var(--rule); }
.xp__fig > img{ transition:transform var(--dur-reveal) var(--ease); }
.xp__body{ padding:var(--space-6); display:flex; flex-direction:column; }
.xp__body > * + *{ margin-block-start:0; }
.xp h3{ margin:0; }
.xp__fact{
  display:flex; align-items:center; gap:var(--space-2);
  margin-block-start:var(--space-2); padding-block-end:var(--space-4);
  border-block-end:1px solid var(--hair-soft);
  font-size:var(--text-sm); line-height:var(--lh-sm); color:var(--accent-dk);
}
.xp__fact .ic{ width:26px; height:26px; flex:0 0 auto; }
.xp__fact + p{ margin-block-start:var(--space-4); font-size:var(--text-body); line-height:var(--lh-body); color:var(--ink-2); }
@media (max-width:991px){ .xp-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
/* ONE per row on a phone, deliberately. Two-up was tried at the client's request
   and measured worse: at 390 each column is about 175px, and these cards carry a
   full paragraph, so the body text wrapped to two or three words a line ("The /
   surrounding / coast is known / for its"). The cards got TALLER, not shorter, and
   much harder to read. Facilities takes two-up happily because its labels are two
   words; these are not. */
@media (max-width:639px){ .xp-grid{ grid-template-columns:1fr; } }

/* the travel-hub rail sits inside the contact card on the Location page */
.cblock .loc__hubs{ margin-block-start:var(--space-6); padding-block-start:var(--space-6); border-block-start:1px solid var(--hair-soft); }

/* ---- C3: alternating split rows ------------------------------------------
   One row per item, media alternating side to side. The side is decided by DOM
   order, exactly as on the homepage; `.split--rev` flips it and is never put on
   a row whose media is already first. Probe P21 asserts the zigzag. */
.rows > * + *{ margin-block-start:var(--space-16); }
@media (max-width:767px){ .rows > * + *{ margin-block-start:var(--space-12); } }

/* --------------------------------------------------------------------------
   21. EXPERIMENT — the wave mark on the booking CTA (2026-09-24)
   --------------------------------------------------------------------------
   The client's glyph (beach.svg) set after the label, with the sun taken out: of its
   11 paths, 9 were the sun's disc and its 8 rays, all inside x 38..207, y 10..179.
   Dropping them left the ink at 455.4 x 429.6 from 28.3,72.5, so the original
   0 0 512 512 box would have carried a dead band where the sun had been and drawn the
   parasol low and undersized. The viewBox is re-normalised to a square around what
   is actually left, with a 4% pad, which is how every other symbol in this sprite is
   cut.

   The glyph carries no fill of its own, so .ic's fill:currentColor reaches it and the
   mark changes colour with the label: --ink on the ochre at rest, --on-dark on hover.
   That is also why it needs no second rule for the hover state.

   The symbol is ic-wave-cta, NOT ic-wave: the sprite already carries an ic-wave, used
   by the "Minutes from Malvan Beach" fact. Reusing that id put two symbols of the same
   name in one document, and since a duplicate id resolves to whichever comes first,
   the new glyph silently replaced the fact strip's icon. The two are separate symbols
   and the fact strip is untouched.

   The mark is decorative and the label still reads "Book Now", so the svg is
   aria-hidden and the accessible name is unchanged. gap is scoped to .btn--mark
   rather than added to .btn, because every other button on the site is a lone text
   node and a bare gap there would be a rule that never fires.

   To pull the experiment: delete this section, the ic-wave symbol, and the btn--mark
   class and svg on the CTA. */
.btn--mark{ gap:var(--space-2); }
/* The glyph is filled outlines with no stroke of its own, and at 26px those outlines
   came out around half a pixel wide -- far lighter than the 700-weight label beside
   them, which is what read as "faint". Colour could not fix it: currentColor is
   already --ink, the darkest value in the palette. Stroking the same currentColor
   thickens the drawn line instead. 14 user units is 7 a side, and the viewBox carries
   9.1 units of pad a side, so the heavier glyph still does not clip. Above about 20
   the umbrella's panel gaps start closing up. */
.btn--mark .ic{
  width:26px; height:26px;
  stroke:currentColor; stroke-width:14; stroke-linejoin:round; stroke-linecap:round;
}

/* --------------------------------------------------------------------------
   22. The starfish ground on every white band (2026-09-24)
   --------------------------------------------------------------------------
   Every .widget--alt on the site, which is the only class that paints var(--surface)
   as a full band.

   The artwork is 2167x726 and bottom-weighted: the top two thirds are effectively
   blank and the sand wave, the dust and the starfish all live along the lower edge,
   with the starfish at bottom right. So it is anchored to the bottom of the band at
   full width rather than stretched to cover -- covering would crop off the only part
   of the image that carries anything.

   background-image alone, not the shorthand: .widget--alt above already paints
   var(--surface), and that white has to stay as the base under the artwork. At 1440
   the image draws 482px tall, at 390 it draws 131px, and any band shorter than that
   simply loses the blank top of the picture, which costs nothing.

   The URL keeps the client's own 2167x726 crop and drops the library thumbnail's
   w_200,h_150,c_pad,b_rgb:eeeeee -- that pad exists to letterbox a preview tile and
   would have painted grey bars across the band. f_auto,q_auto is the same delivery
   the rest of the site's images use; it serves 17KB of WebP here.

   To pull this: delete this section. */
/* Links on these bands take a darker ochre. --accent-dk measures 5.61:1 on plain
   white, which is a thin margin, and the artwork's sand tint eats it: the contact
   email and phone low in the Terms band measured 3.57:1 and 4.35:1 over the sand,
   both under 1.4.3's 4.5. #6B430F is the same hue carried further down and restores
   the margin -- 8.62:1 on white, 5.49:1 on the sand, 4.01:1 even on the artwork's
   darkest pixel. Scoped to .widget--alt because that is exactly where the artwork
   is; links on every other band keep --accent-dk. */
.widget--alt .link{ color:#6B430F; }

.widget--alt{
  background-image:url("https://assets.simplotel.com/simplotel/image/upload/x_0,y_0,w_2167,h_726,c_crop/w_1600,f_auto,q_auto/shirgaonkar-beach-resort/background-starfish");
  background-repeat:no-repeat;
  background-position:center bottom;
  background-size:100% auto;
}

/* --------------------------------------------------------------------------
   23. GETTING THERE - the distance tabs on Location (2026-09-24)
   --------------------------------------------------------------------------
   Fourth version of this widget. The table, the row panels and the chip bands all
   failed the same way: ten places listed flat, with nothing to look at. Now the three
   distances are tabs and each holds image cards, so the page shows the coast instead
   of tabulating it.

   PROGRESSIVE ENHANCEMENT. The markup ships with every panel visible and this rule
   hides the tab strip, so with JS off the widget is a plain stack of all three groups
   and nothing is unreachable. site.js is what hides the panels, and it reveals the
   strip in the same pass -- the strip can never appear while the panels it controls
   are still stacked. */
.tabs__list{ display:none; }
.tabs.is-tabbed .tabs__list{
  display:flex; flex-wrap:wrap; gap:var(--space-2); justify-content:center;
  margin-block-start:var(--space-8);
  border-block-end:1px solid var(--hair-soft);
}

.tabs__tab{
  min-height:44px; padding:var(--space-3) var(--space-5);
  border:0; border-block-end:2px solid transparent; border-radius:0;
  background:transparent; color:var(--ink-2);
  font-size:var(--text-sm); line-height:var(--lh-sm); font-weight:var(--w-bold);
  letter-spacing:var(--tr-btn); text-transform:uppercase;
  cursor:pointer; white-space:nowrap;
  /* the 2px rule sits ON the list's hairline rather than under it */
  margin-block-end:-1px;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.tabs__tab:hover{ color:var(--accent-dk); }
.tabs__tab[aria-selected="true"]{ color:var(--ink); border-block-end-color:var(--accent); }

.tabs__panel{ margin-block-start:var(--space-8); }
/* the panel takes focus after the arrow keys move selection, so it needs a ring */
.tabs__panel:focus-visible{ outline:2px solid var(--accent-dk); outline-offset:4px; }
/* stacked and unhidden without JS, each group needs its own separation */
.tabs:not(.is-tabbed) .tabs__panel + .tabs__panel{
  padding-block-start:var(--space-8); border-block-start:1px solid var(--hair-soft);
}

/* ---- the cards -------------------------------------------------------------
   There is no card component here: the panels reuse .xp, the Experiences card, so
   the two pages cannot drift apart and the directions link comes with it. The old
   .near block was a parallel component that had to be kept in visual step by hand. */

/* The CTA under this widget was removed at the client's direction (2026-09-24), so
   the last thing in the section is now a panel of cards. Without the CTA's own
   margin the band closed up too tightly under them. */
.tabs__panel .xp-grid{ margin-block-end:var(--space-4); }

/* FLEX, not the grid .xp-grid normally is. A grid cannot centre a partial last row:
   its tracks are fixed, so the four-card panel left Sindhudurg Fort hard against the
   left edge with two empty tracks beside it. Flex wrapping with justify-content:center
   centres whatever the last row happens to hold, and the basis calculation reproduces
   the same three-up, two-up, one-up steps the grid had. */
.tabs__panel .xp-grid,
.xp-grid--centred{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:var(--space-6);
}
.tabs__panel .xp-grid > .xp,
.xp-grid--centred > .xp{ flex:0 1 calc((100% - 2 * var(--space-6)) / 3); }
@media (max-width:991px){
  .tabs__panel .xp-grid > .xp,
  .xp-grid--centred > .xp{ flex-basis:calc((100% - var(--space-6)) / 2); }
}
@media (max-width:639px){
  .tabs__panel .xp-grid > .xp,
  .xp-grid--centred > .xp{ flex-basis:100%; }
}

/* --------------------------------------------------------------------------
   24. EXPERIENCES — the directions link on each card (2026-09-24)
   --------------------------------------------------------------------------
   margin-block-start:auto pins it to the bottom of the card body, so the links line
   up across a row whatever length the descriptions run to. Without it the link sat
   directly under its own paragraph and the row read as a ragged staircase. */
/* The card is already a column flex, and .xp__body is already a column flex inside it
   -- but the body never GREW, so it ended at its own content and margin-block-start:auto
   had no slack to push into. Measured before this line: the links in row 1 sat at
   1629, 1646 and 1629, and in row 3 at 2897 and 2924, which is the ragged staircase
   this rule exists to remove. flex:1 makes the body fill the card, and auto then puts
   every link on the card's own floor. */
.xp__body{ flex:1 1 auto; }
.xp__go{ margin-block-start:auto; padding-block-start:var(--space-4); }

/* The pin holds a two-line slot whether or not it needs one.
   Three of the eleven facts wrap -- "Jetty about 2 to 3 km, then a 10 to 15 minute
   ferry", and the two "off the Tarkarli and Devbag coast" lines -- so those pins
   measured 60px against 43px for the rest, and the prose under them started 17px
   lower than its neighbours (1510 against 1493). Reserving two lines aligns the
   first line of every pin AND the first line of every paragraph across a row,
   without shortening any of the facts.

   align-items:flex-start, not centre: centring would float a one-line pin in the
   middle of the taller slot, so the first lines would still not agree. The icon is
   26px against a 21.7px line, so it takes a 2px lift to sit on that first line.

   `2lh`, not a calc from the tokens: 14px x 1.55 is 21.7, and calc'ing it gave a
   59.4px slot while a real two-line box rounds each line box to 22 and lands on 60 --
   a 1px disagreement that put the prose at 1509 against 1510. `lh` is the element's
   own line box, rounded the way the browser rounds it, so the slot matches a genuine
   two-line pin exactly and still tracks the tokens if the type scale changes. */
.xp__fact{
  align-items:flex-start;
  /* +1px absorbs sub-pixel line-box rounding: 2lh computes 43.4 while the real
     two-line flex item lays out a hair taller, which left the slot 1px short of the
     very case it exists to match. With the pixel, the slot wins in both cases and
     every prose block starts on the same line. */
  min-height:calc(2lh + var(--space-4) + 1px);
}
.xp__fact .ic{ margin-block-start:-2px; }

/* --------------------------------------------------------------------------
   25. SEATING LAYOUTS - the plan diagrams on Meetings & Celebrations (2026-09-24)
   --------------------------------------------------------------------------
   This sits beside the venue-facts table as one half of a .split, so it is built to
   the SAME rhythm as that table rather than to its own. Measured before: the table
   ran 387px over six rows while a 3-across grid of plans ran 247px over two, leaving
   the right column ending 141px short of the left and an orphan cell in its second
   row. Neither column looked like it belonged to the other.

   One row per layout, 16px of padding above and below and a --hair-soft rule between,
   which is exactly .spec's own row spec. Five rows of 44px artwork come to 384px
   against the table's 387 -- the two columns now end level, and read down together.

   Columns are auto / 1fr / auto: plan, name, capacity. That mirrors the table's
   label-then-value split, with the diagram taking the place the table gives nothing. */
.seats{ display:flex; flex-direction:column; margin-block-start:var(--space-6); }

/* Row for row with the spec table beside it.
   A .spec row is 16px of padding, one 27.2px body line, 16px of padding and a 1px
   rule -- 60.2px. The plan is 36px, taller than a text line, so it would drive the
   row to 69 and the two columns would drift apart by 9px on every row: by the fourth,
   "Suited To" and "U-Shape" would be 27px out and no rule would meet its neighbour.
   The padding gives back exactly half the excess, so the row lands on 60.2 again and
   every hairline meets the one across the gutter. */
.seats{ --seat-dia:36px; }
.seat{
  display:grid; grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center; gap:var(--space-5);
  padding-block:calc(var(--space-4) - (var(--seat-dia) - var(--text-body) * var(--lh-body)) / 2);
  border-block-end:1px solid var(--hair-soft);
}
.seat:last-child{ border-block-end:0; }

.seat .ic{
  width:52px; height:var(--seat-dia); flex:0 0 auto; color:var(--accent-dk);
  padding:3px; border:1px solid var(--hair-soft); border-radius:var(--r-1);
  background:var(--surface);
}
.seat__name{ color:var(--ink); font-size:var(--text-body); line-height:var(--lh-body); }
.seat__cap{ color:var(--ink-2); font-size:var(--text-body); line-height:var(--lh-body); white-space:nowrap; }

/* On a narrow phone the capacity drops under the name instead of being stranded at
   the far edge of a row that has run out of width for three tracks. */
@media (max-width:479px){
  .seat{ grid-template-columns:auto minmax(0,1fr); gap:var(--space-4); }
  .seat__cap{ grid-column:2; }
}

/* A laid-out set of room cards, used where the set is small and fixed. "Our Other
   Rooms" is always two, and a carousel there was a control with nothing to control --
   worse, a 2.2-per-view track left both cards hanging at the left edge.
   Flex rather than grid, because a row holding fewer cards than the layout allows has
   to centre, and grid tracks cannot do that. */
.card-grid{ display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-6); list-style:none; margin:0; padding:0; }
.card-grid > li{ flex:0 1 calc((100% - 2 * var(--space-6)) / 3); min-width:0; display:flex; }
.card-grid > li > .card{ width:100%; }
@media (max-width:991px){ .card-grid > li{ flex-basis:calc((100% - var(--space-6)) / 2); } }
@media (max-width:639px){ .card-grid > li{ flex-basis:100%; } }

/* An amenity whose label carries a bracketed detail sets that detail on its own line,
   the way a facility card already sets its note under its name. Before this,
   "In-Room Dining (7:30 AM to 10:30 PM)" broke after "10:30" and left "PM)" stranded. */
.amen__t{ min-width:0; }
.amen__note{ display:block; font-size:var(--text-xs); line-height:var(--lh-xs); color:var(--ink-2); }
/* (the :has(.amen__note) overrides that used to live here are now the default) */

/* --------------------------------------------------------------------------
   26. EXPERIENCES / LOCATION — one shared set of rows across a band of cards
   -------------------------------------------------------------------------- (2026-09-28)
   The four parts of a card -- heading, pin, prose, directions link -- have to start
   on the same line right across a row. Measured before this rule, they did not:
   "Chhatrapati Shivaji Maharaj Statue" is the one heading that wraps to two lines, so
   on that card the pin began 32px lower than its two neighbours and the prose with it.

   Subgrid, not a min-height slot. A min-height tall enough for a two-line heading
   would have reserved that second line on all twelve cards to fix the one row that
   needs it -- about 28px of blank space under ten headings that are a single line.
   Subgrid sizes each row band to its own tallest card, so rows 2 to 4 stay exactly as
   tight as they are today and only row 1 grows. It also aligns the rule under the pin,
   because .xp__fact stretches to its row and carries its border-block-end to the floor.

   The card spans five rows: image, heading, pin, prose, link. .xp__body keeps its own
   padding and passes the four text rows straight through, so the padding is identical
   on every card and shifts every track by the same amount.

   The four activity cards (Scuba Diving, Water Sports, Dolphin Spotting, Malvani
   Cuisine) have no directions link, since they are things to do rather than a place
   with an address. They still span the link row and leave it empty, which is what
   keeps the links that DO exist on one line instead of each sitting under its own
   paragraph.

   .xp-grid--centred goes back to being a grid here. It was made a flex row to centre a
   partial last row, and subgrid needs a grid parent -- safe on Experiences because it
   holds twelve cards, a full row at three-up, two-up and one-up alike. If a card is
   ever added or removed there, the orphans on the last row will sit left, not centred.

   Scoped to --centred, which is Experiences alone. The same cards on Location sit in
   tab panels that are flex for real: those grids hold five and six cards, so the
   five-card panel genuinely has a part-full last row that flex is centring. Converting
   them would drop those two orphans to the left edge, so they keep the flex layout and
   the older min-height slot, and Chhatrapati Shivaji Maharaj Statue still pushes its
   pin a line down at around 1100px there. On a non-subgrid browser this whole block is
   skipped and Experiences falls back to exactly that same behaviour.

   The old .xp__fact min-height is released back to 0: it existed to stop a wrapping
   pin from pushing the prose down, which the shared rows now handle without
   reserving a second line on pins that do not need one. */
@supports (grid-template-rows:subgrid){
  .xp-grid--centred{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); }
  @media (max-width:991px){ .xp-grid--centred{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
  @media (max-width:639px){ .xp-grid--centred{ grid-template-columns:1fr; } }

  /* row-gap:0 on both subgrids. A subgrid inherits the parent's gap, and .xp-grid sets
     `gap:var(--space-6)` for the space BETWEEN cards -- which, once a card spans five
     tracks, was also being inserted between its own image, heading, pin, prose and
     link: four extra 24px gaps per card. Measured: the pin sat 64px under a one-line
     heading against 40px before. Zeroing it here leaves the gap between one band of
     cards and the next untouched, since that gap belongs to the parent grid and is
     not inside any card's span. */
  .xp-grid--centred > .xp{ display:grid; grid-row:span 5; grid-template-rows:subgrid; row-gap:0; }
  .xp-grid--centred > .xp > .xp__body{ display:grid; grid-row:2 / span 4; grid-template-rows:subgrid; row-gap:0; }
  .xp-grid--centred > .xp .xp__fact{ min-height:0; }

  /* Location's cards sit in tab panels that were flex for a real reason: one panel
     holds five cards, so its last row is part-full and flex was centring the two
     orphans. Subgrid needs a grid parent, so the panels become a grid that can centre
     a part-full row by itself -- the alignment is gained without losing the centring.

     Six tracks, each card spanning two. Three cards then fill the row exactly as three
     1fr columns did (2t+g per card against (W-2g)/3, which is the same number), and a
     part-full row can start on an EVEN track, which is what puts it in the middle.
     Four tracks at two-up, two at one-up, on the same principle.

     The two nth-child rules are counting, not hard-coded positions, so a card added to
     or removed from either panel still lands centred: `3n+1` is a card that starts a
     row, and how many follow it decides where that row begins -- one track in from the
     left when two remain, two when one does. Each is inside its own breakpoint,
     because a card that starts a row at three-up is rarely the one that starts it at
     two-up.

     grid-column, not grid-column-start: the longhand overwrites the `span 2` set
     above it, and the shifted card came out one track wide -- 180px against its
     neighbours' 384px -- which is how a "centred" row ended up sitting left of centre.
     */
  .tabs__panel .xp-grid{ display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); }
  .tabs__panel .xp-grid > .xp{
    grid-column:span 2; grid-row:span 5; display:grid; grid-template-rows:subgrid; row-gap:0;
  }
  .tabs__panel .xp-grid > .xp > .xp__body{
    display:grid; grid-row:2 / span 4; grid-template-rows:subgrid; row-gap:0;
  }
  .tabs__panel .xp-grid > .xp .xp__fact{ min-height:0; }
  @media (min-width:992px){
    .tabs__panel .xp-grid > .xp:nth-child(3n+1):nth-last-child(2){ grid-column:2 / span 2; }
    .tabs__panel .xp-grid > .xp:nth-child(3n+1):last-child{ grid-column:3 / span 2; }
  }
  @media (max-width:991px){
    .tabs__panel .xp-grid{ grid-template-columns:repeat(4,minmax(0,1fr)); }
    .tabs__panel .xp-grid > .xp:nth-child(2n+1):last-child{ grid-column:2 / span 2; }
  }
  @media (max-width:639px){
    .tabs__panel .xp-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
    .tabs__panel .xp-grid > .xp:nth-child(2n+1):last-child{ grid-column:1 / span 2; }
  }
}

/* --------------------------------------------------------------------------
   27. HEADER RESERVATIONS NUMBER and the FOOTER OPERATOR LINE (2026-09-28)
   --------------------------------------------------------------------------
   The number sits left of Book Now in the header's right-hand cell. It takes the nav
   link's colour treatment rather than its own, so it turns from white to ink with the
   rest of the bar when the header goes solid -- a number that stayed white would
   vanish against the cream once a guest scrolled.

   It is a tel: link with the number as its text. A guest on a phone taps it; a guest
   on a desktop still reads it, which is the whole point of putting it up there. */
.header-tel{
  /* 26px, not 44px. Stacked under the button the two heights add up, and a 44px row
     pushed the header past the 88px --header-h token that every in-page anchor offsets
     itself by -- headings would have landed under the bar. 26px still clears the 24px
     minimum for a pointer target. */
  display:inline-flex; align-items:center; gap:var(--space-2); min-height:var(--tel-row, 26px);
  font-size:var(--text-sm); font-weight:var(--w-bold); color:var(--on-dark);
  white-space:nowrap; transition:color var(--dur) var(--ease);
}
.header-tel .ic{ width:18px; height:18px; flex:0 0 auto; }
.header-tel:hover{ color:var(--accent-lt); }
.site-header.is-solid .header-tel{ color:var(--ink); }
.site-header.is-solid .header-tel:hover{ color:var(--accent-dk); }

/* The number sits UNDER Book Now (client, 2026-09-28). It is second in the markup as
   well as second on screen: column-reverse would have painted it there while leaving a
   keyboard user tabbing to it first. It is centred under the button, not aligned to its
   right edge -- see the rule below.

   Stacked, the pair is only as wide as the wider of the two -- 189px against the 338px
   the side-by-side row cost -- and the nav is a priority+ list, so that difference goes
   straight back into the visible nav.

   There is deliberately NO width at which the number hides. It had one, at 1199, and
   that is a zoom bug rather than a breakpoint: browser zoom shrinks the CSS viewport,
   so a 1440 screen reports 1152px at 125% and 1029px at 140% -- and the number a guest
   had just zoomed in to read was the one thing that disappeared. Letting it stay costs
   nothing structural, because the nav already knows how to give way. Below 768 the
   whole .header-actions block is hidden for the mobile layout, which is where the
   bottom booking bar takes over. */
.header-actions{
  /* What lands on the header's line -- and so on the nav text's line -- is the GAP
     between the button and the number, not the button itself (client, 2026-09-28), so
     the pair straddles the nav row rather than hanging below it.

     A stacked column centres its own BOX, so the trick is to pad the side that makes
     the gap the box's optical centre. The button is taller than the number, so the
     shortfall goes UNDERNEATH: padding-block-end of exactly (button - number) puts as
     much below the number as the button's extra height puts above the gap. At 44px and
     26px that is 18px, and the column then sits with 12px clear top and bottom.

     --tel-row is declared here and read by .header-tel below, so the padding and the
     thing it compensates for can never fall out of step. */
  --tel-row:26px; --btn-row:44px;
  /* align-items:center, so the number centres under the button rather than hanging
     off its right edge (client, 2026-09-28). The column is a flex column whose
     width is set by its widest child -- the button -- so the button still runs the
     full width and still ends on the gutter, and only the narrower number moves. */
  flex-direction:column; align-items:center; gap:var(--space-1);
  /* no spacer: the column is pinned to --hdr-top below, not centred. */
}

/* The header's own CTA is a step shorter than a standalone .btn. Stacked, the button
   (52px) plus the gap plus the number (26px) came to 82px inside an 88px bar, leaving
   3px above the button and 3px below the number -- the pair read as jammed against
   both edges. Trimming the button's vertical padding brings the stack to 74px and the
   clearance to 7px top and bottom, and 44px of button is still a full tap target. */
.header-actions .btn{ min-height:var(--btn-row); padding-block:var(--space-2); }

/* The top of Book Now lines up with the top of "Shirgaonkar" (client, 2026-09-28).

   Done by construction rather than by nudging the button down 20px: the header stops
   centring its cells and instead starts them, and the brand and the CTA take the SAME
   padding-block-start. Their content then begins on one line whatever either of them
   grows into -- a fourth brand line, a longer number, a taller button. A hard-coded
   offset would have aligned them today and drifted the first time one changed.

   The nav takes the same inset PLUS --nav-drop, which lands its words on the second
   brand line, "Beach Resort" (client, 2026-09-28), rather than on the first. So the
   button's top edge agrees with "Shirgaonkar" while the nav reads along the line
   below it.

   Desktop only. Below 768 the bar is 64px, the CTA column is hidden and the brand is
   centred in the shorter bar, so a 32px inset there would push the lockup out of it. */
@media (min-width:768px){
  .site-header{ align-items:start; }
  .brand,
  .header-actions{ padding-block-start:var(--hdr-top); }
  .nav{ padding-block-start:calc(var(--hdr-top) + var(--nav-drop)); }
}

/* The operator credit under the footer lockup. Type, weight and colour all come from
   the shared `.foot h3, .foot__by` rule above, so it reads exactly as "Quick Links"
   does (client, 2026-09-28) and the two cannot drift apart later. Only the spacing is
   its own: it belongs to the lockup above it, so it sits a step under the brand rather
   than taking the column's full .foot__col rhythm. */
.foot__by{ margin-block-start:var(--space-2); }
.foot__col > .foot__brand + .foot__by{ margin-block-start:var(--space-2); }

/* Section 28, the hover-revealed duration bar, was replaced by the control bar in
   section 31 and its rules removed with it. */

/* --------------------------------------------------------------------------
   29. WHERE YOU ARE — the nav marks the current page (2026-09-29)
   --------------------------------------------------------------------------
   aria-current="page" was already on the right link, on both the bar and the drawer,
   so a screen reader has always announced the current page. Nothing showed it, though,
   which left every sighted guest to work it out from the heading.

   The bar uses the underline the links already grow on hover, held permanently and in
   the accent. That reuses a language the nav has rather than introducing a second one,
   and it survives the white-to-ink switch because both states are defined.

   The drawer gets a left rule instead: its rows are full width, so an underline would
   run the whole way across and read as a divider. */
.nav__link[aria-current="page"]{
  color:var(--accent-lt);
  border-block-end-color:var(--accent-lt);
}
.site-header.is-solid .nav__link[aria-current="page"]{
  color:var(--accent-dk);
  border-block-end-color:var(--accent-dk);
}
/* When one of Rooms' children is the page you are on, the mark goes on the WORD and not
   on the chevron beside it (client, 2026-09-30). The rule used to run under both, which
   read as an underline with a stray tick on the end rather than as one marked item. The
   chevron still takes the accent colour, so the pair still reads as a set -- it just
   does not carry a rule of its own.

   .nav__link already carries a transparent bottom border for its hover state, so this
   only has to colour it; nothing shifts by a pixel when it turns on. */
.nav-sub[data-on-child] > .nav__link{
  color:var(--accent-lt);
  border-block-end-color:var(--accent-lt);
}
.nav-sub[data-on-child] > .nav-sub__btn{ color:var(--accent-lt); }
.site-header.is-solid .nav-sub[data-on-child] > .nav__link{
  color:var(--accent-dk);
  border-block-end-color:var(--accent-dk);
}
.site-header.is-solid .nav-sub[data-on-child] > .nav-sub__btn{ color:var(--accent-dk); }

.drawer__list a[aria-current="page"]{
  color:var(--accent-lt);
  border-inline-start:2px solid var(--accent-lt);
  padding-inline-start:var(--space-3);
}

/* The photograph on a room card is a link now (client, 2026-09-29): it already lifted
   on hover, which promised a click the card did not answer. The card's own "View Room"
   button stays the accessible link -- this one is tabindex="-1" and aria-hidden, so the
   keyboard and a screen reader still meet ONE link per card, not two to the same place. */
.card__media{ display:block; block-size:100%; }

/* --------------------------------------------------------------------------
   30. ONE CENTRED HEAD THAT READS LEFT ON A PHONE (2026-09-30)
   --------------------------------------------------------------------------
   The Rooms summary's "From Garden to Sea" head is centred on a wide screen and left
   on a phone, at the client's request. It is opt-in per block rather than a change to
   .center, because .center is used across the site and the other head on this very
   page -- "In Every Room" -- stays centred.

   The motif needs its own line: .center gives it margin-inline:auto, so without this
   the rule under the eyebrow would stay in the middle while the words moved left.
   Three classes deep so it outranks `.widget__head.center`, which is two. */
@media (max-width:767px){
  .widget__head.center.head--left-mobile{
    text-align:left; --align-x:flex-start; margin-inline:0;
  }
  .widget__head.center.head--left-mobile .motif,
  .widget__head.center.head--left-mobile .eyebrow{ margin-inline:0; }
}

/* --------------------------------------------------------------------------
   31. HERO VIDEO — the control bar (2026-09-30)
   --------------------------------------------------------------------------
   One bar across the hero, after the client's reference player: pause on the left, the
   progress line filling the middle, time remaining, then mute. The reference is red;
   these are the site's own accent tokens, so the bar belongs to this page rather than
   to the player it was copied from.

   It sits BELOW the booking form on a desktop and ABOVE it on a phone, which is where
   it has always been. The hero is a flex column, so that is an `order` swap rather than
   a second copy of the markup in the HTML -- one bar, one set of ids, one set of
   handlers.

   The old round buttons are gone; .hero__ctrls keeps its name only so the print rule
   and the file:// shim that already reference it keep working. */
.vctrl{
  display:flex; align-items:center; gap:var(--space-3);
  padding:var(--space-2) var(--gutter);
  border-radius:0;
  color:var(--on-dark);
  /* Hidden until the pointer is on the hero. See the note above for why this is
     opacity and not display or visibility. */
  opacity:0; pointer-events:none;
  transition:opacity var(--dur) var(--ease);
  /* No panel behind it now, so the controls sit on whatever frame the video happens to
     be showing. The shadow is what keeps them legible over a pale one. */
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.55));
}
.hero:hover .vctrl,
.vctrl:focus-within{ opacity:1; pointer-events:auto; }
@media (hover:none){
  .vctrl{ opacity:1; pointer-events:auto; }
}
@media (prefers-reduced-motion:reduce){
  .vctrl{ transition:none; }
}
.vctrl__btn{
  flex:0 0 auto; width:36px; height:36px;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--r-circle); color:var(--on-dark); background:transparent;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.vctrl__btn .ic{ width:20px; height:20px; }
.vctrl__btn:hover{ background:var(--accent); color:var(--ink); }
.vctrl__btn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
/* sound on is a held state, so the control shows it rather than only announcing it */
.vctrl__btn.is-on{ background:var(--accent); color:var(--ink); }

/* The readout is a fixed width and tabular, so the bar does not twitch as the seconds
   count down and the digits change width. */
.vctrl__time{
  flex:0 0 auto; min-width:52px; text-align:right;
  font-size:var(--text-xs); line-height:1; font-variant-numeric:tabular-nums;
  color:var(--on-dark); opacity:.85;
}

/* The progress line takes whatever is left. Same range-input reasoning as before: the
   arrow keys, Home/End and the slider role come free, and the filled part has to be
   painted by hand because no browser lets a range's progressed track be styled. */
.vctrl__seek{
  -webkit-appearance:none; appearance:none;
  flex:1 1 auto; min-width:0; height:28px;
  margin:0; padding:0; background:transparent; cursor:pointer;
  --p:0%;
}
.vctrl__seek::-webkit-slider-runnable-track{
  height:4px; border-radius:2px;
  background:linear-gradient(to right, var(--accent) 0 var(--p), rgba(247,234,220,.35) var(--p) 100%);
}
.vctrl__seek::-moz-range-track{
  height:4px; border-radius:2px;
  background:linear-gradient(to right, var(--accent) 0 var(--p), rgba(247,234,220,.35) var(--p) 100%);
}
.vctrl__seek::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  width:12px; height:12px; margin-block-start:-4px;
  border-radius:var(--r-circle); background:var(--on-dark); border:1px solid var(--accent-lt);
}
.vctrl__seek::-moz-range-thumb{
  width:12px; height:12px; border-radius:var(--r-circle);
  background:var(--on-dark); border:1px solid var(--accent-lt);
}
.vctrl__seek:hover::-webkit-slider-thumb{ background:var(--accent); }
.vctrl__seek:hover::-moz-range-thumb{ background:var(--accent); }
.vctrl__seek:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; border-radius:var(--r-1); }

/* Pinned to the foot of the video -- but only where there is room for it. On a phone
   the booking form stacks into a tall column that reaches the hero's bottom edge and
   covers that strip completely: measured, all three points along the bar returned the
   form as the topmost element. So below 768 the bar stays a flow item above the form,
   which is where the client asked for it on mobile anyway.

   z-index 3 sits under .bw's 4 on purpose: the form overhangs the hero's bottom edge
   and crosses the bar, and the form is the thing a guest came for. */
@media (min-width:768px){
  .vctrl{
    position:absolute; inset-inline:0; inset-block-end:0; z-index:3;
    width:auto; max-width:none; margin:0;
    padding-inline:var(--gutter); border-radius:0;
  }
}

/* Full screen shows the VIDEO. The booking form belongs to the page, not to the film,
   so it stands down until full screen ends. The
   hero also drops the padding it reserves for the cookie bar and the form's overhang --
   neither is on screen, and the video should reach the edges.

   Written twice on purpose: an unknown pseudo-class invalidates the whole selector
   list it sits in, so :fullscreen and :-webkit-full-screen cannot share one rule. */
.hero:fullscreen{ padding-block-end:0; }
.hero:fullscreen .bw{ display:none; }
.hero:fullscreen .vctrl{ inset-block-end:0; }
.hero:-webkit-full-screen{ padding-block-end:0; }
.hero:-webkit-full-screen .bw{ display:none; }
.hero:-webkit-full-screen .vctrl{ inset-block-end:0; }

/* VENDOR START — Swiper 11 (MIT). Grid and alignment lints skip this block.
   Master: vendor/swiper/. Do not edit here; edit the master and re-run build.py. */
/**
 * Swiper 11.2.10
 * Most modern mobile touch slider and framework with hardware accelerated transitions
 * https://swiperjs.com
 *
 * Copyright 2014-2025 Vladimir Kharlampidi
 *
 * Released under the MIT License
 *
 * Released on: June 28, 2025
 */

@font-face{font-family:swiper-icons;src:url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}:host{position:relative;display:block;margin-left:auto;margin-right:auto;z-index:1}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1;display:block}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;transition-timing-function:var(--swiper-wrapper-transition-timing-function,initial);box-sizing:content-box}.swiper-android .swiper-slide,.swiper-ios .swiper-slide,.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-horizontal{touch-action:pan-y}.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform;display:block}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide{transform-style:preserve-3d}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-css-mode.swiper-horizontal>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-css-mode.swiper-vertical>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-css-mode.swiper-free-mode>.swiper-wrapper{scroll-snap-type:none}.swiper-css-mode.swiper-free-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:none}.swiper-css-mode.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-css-mode.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center;scroll-snap-stop:always}.swiper-css-mode.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-css-mode.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-lazy-preloader{width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;transform-origin:50%;box-sizing:border-box;border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top-color:transparent}.swiper-watch-progress .swiper-slide-visible .swiper-lazy-preloader,.swiper:not(.swiper-watch-progress) .swiper-lazy-preloader{animation:swiper-preloader-spin 1s infinite linear}.swiper-lazy-preloader-white{--swiper-preloader-color:#fff}.swiper-lazy-preloader-black{--swiper-preloader-color:#000}@keyframes swiper-preloader-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.swiper-virtual .swiper-slide{-webkit-backface-visibility:hidden;transform:translateZ(0)}.swiper-virtual.swiper-css-mode .swiper-wrapper::after{content:'';position:absolute;left:0;top:0;pointer-events:none}.swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after{height:1px;width:var(--swiper-virtual-size)}.swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after{width:1px;height:var(--swiper-virtual-size)}:root{--swiper-navigation-size:44px}.swiper-button-next,.swiper-button-prev{position:absolute;top:var(--swiper-navigation-top-offset,50%);width:calc(var(--swiper-navigation-size)/ 44 * 27);height:var(--swiper-navigation-size);margin-top:calc(0px - (var(--swiper-navigation-size)/ 2));z-index:10;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--swiper-navigation-color,var(--swiper-theme-color))}.swiper-button-next.swiper-button-disabled,.swiper-button-prev.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}.swiper-button-next.swiper-button-hidden,.swiper-button-prev.swiper-button-hidden{opacity:0;cursor:auto;pointer-events:none}.swiper-navigation-disabled .swiper-button-next,.swiper-navigation-disabled .swiper-button-prev{display:none!important}.swiper-button-next svg,.swiper-button-prev svg{width:100%;height:100%;object-fit:contain;transform-origin:center}.swiper-rtl .swiper-button-next svg,.swiper-rtl .swiper-button-prev svg{transform:rotate(180deg)}.swiper-button-prev,.swiper-rtl .swiper-button-next{left:var(--swiper-navigation-sides-offset,10px);right:auto}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:var(--swiper-navigation-sides-offset,10px);left:auto}.swiper-button-lock{display:none}.swiper-button-next:after,.swiper-button-prev:after{font-family:swiper-icons;font-size:var(--swiper-navigation-size);text-transform:none!important;letter-spacing:0;font-variant:initial;line-height:1}.swiper-button-prev:after,.swiper-rtl .swiper-button-next:after{content:'prev'}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:var(--swiper-navigation-sides-offset,10px);left:auto}.swiper-button-next:after,.swiper-rtl .swiper-button-prev:after{content:'next'}.swiper-pagination{position:absolute;text-align:center;transition:.3s opacity;transform:translate3d(0,0,0);z-index:10}.swiper-pagination.swiper-pagination-hidden{opacity:0}.swiper-pagination-disabled>.swiper-pagination,.swiper-pagination.swiper-pagination-disabled{display:none!important}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction{bottom:var(--swiper-pagination-bottom,8px);top:var(--swiper-pagination-top,auto);left:0;width:100%}.swiper-pagination-bullets-dynamic{overflow:hidden;font-size:0}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transform:scale(.33);position:relative}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev{transform:scale(.33)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next{transform:scale(.33)}.swiper-pagination-bullet{width:var(--swiper-pagination-bullet-width,var(--swiper-pagination-bullet-size,8px));height:var(--swiper-pagination-bullet-height,var(--swiper-pagination-bullet-size,8px));display:inline-block;border-radius:var(--swiper-pagination-bullet-border-radius,50%);background:var(--swiper-pagination-bullet-inactive-color,#000);opacity:var(--swiper-pagination-bullet-inactive-opacity, .2)}button.swiper-pagination-bullet{border:none;margin:0;padding:0;box-shadow:none;-webkit-appearance:none;appearance:none}.swiper-pagination-clickable .swiper-pagination-bullet{cursor:pointer}.swiper-pagination-bullet:only-child{display:none!important}.swiper-pagination-bullet-active{opacity:var(--swiper-pagination-bullet-opacity, 1);background:var(--swiper-pagination-color,var(--swiper-theme-color))}.swiper-pagination-vertical.swiper-pagination-bullets,.swiper-vertical>.swiper-pagination-bullets{right:var(--swiper-pagination-right,8px);left:var(--swiper-pagination-left,auto);top:50%;transform:translate3d(0px,-50%,0)}.swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets .swiper-pagination-bullet{margin:var(--swiper-pagination-bullet-vertical-gap,6px) 0;display:block}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{top:50%;transform:translateY(-50%);width:8px}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{display:inline-block;transition:.2s transform,.2s top}.swiper-horizontal>.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{left:50%;transform:translateX(-50%);white-space:nowrap}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s left}.swiper-horizontal.swiper-rtl>.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s right}.swiper-pagination-fraction{color:var(--swiper-pagination-fraction-color,inherit)}.swiper-pagination-progressbar{background:var(--swiper-pagination-progressbar-bg-color,rgba(0,0,0,.25));position:absolute}.swiper-pagination-progressbar .swiper-pagination-progressbar-fill{background:var(--swiper-pagination-color,var(--swiper-theme-color));position:absolute;left:0;top:0;width:100%;height:100%;transform:scale(0);transform-origin:left top}.swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill{transform-origin:right top}.swiper-horizontal>.swiper-pagination-progressbar,.swiper-pagination-progressbar.swiper-pagination-horizontal,.swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,.swiper-vertical>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite{width:100%;height:var(--swiper-pagination-progressbar-size,4px);left:0;top:0}.swiper-horizontal>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-vertical,.swiper-vertical>.swiper-pagination-progressbar{width:var(--swiper-pagination-progressbar-size,4px);height:100%;left:0;top:0}.swiper-pagination-lock{display:none}.swiper-scrollbar{border-radius:var(--swiper-scrollbar-border-radius,10px);position:relative;touch-action:none;background:var(--swiper-scrollbar-bg-color,rgba(0,0,0,.1))}.swiper-scrollbar-disabled>.swiper-scrollbar,.swiper-scrollbar.swiper-scrollbar-disabled{display:none!important}.swiper-horizontal>.swiper-scrollbar,.swiper-scrollbar.swiper-scrollbar-horizontal{position:absolute;left:var(--swiper-scrollbar-sides-offset,1%);bottom:var(--swiper-scrollbar-bottom,4px);top:var(--swiper-scrollbar-top,auto);z-index:50;height:var(--swiper-scrollbar-size,4px);width:calc(100% - 2 * var(--swiper-scrollbar-sides-offset,1%))}.swiper-scrollbar.swiper-scrollbar-vertical,.swiper-vertical>.swiper-scrollbar{position:absolute;left:var(--swiper-scrollbar-left,auto);right:var(--swiper-scrollbar-right,4px);top:var(--swiper-scrollbar-sides-offset,1%);z-index:50;width:var(--swiper-scrollbar-size,4px);height:calc(100% - 2 * var(--swiper-scrollbar-sides-offset,1%))}.swiper-scrollbar-drag{height:100%;width:100%;position:relative;background:var(--swiper-scrollbar-drag-bg-color,rgba(0,0,0,.5));border-radius:var(--swiper-scrollbar-border-radius,10px);left:0;top:0}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-scrollbar-lock{display:none}.swiper-zoom-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center;text-align:center}.swiper-zoom-container>canvas,.swiper-zoom-container>img,.swiper-zoom-container>svg{max-width:100%;max-height:100%;object-fit:contain}.swiper-slide-zoomed{cursor:move;touch-action:none}.swiper .swiper-notification{position:absolute;left:0;top:0;pointer-events:none;opacity:0;z-index:-1000}.swiper-free-mode>.swiper-wrapper{transition-timing-function:ease-out;margin:0 auto}.swiper-grid>.swiper-wrapper{flex-wrap:wrap}.swiper-grid-column>.swiper-wrapper{flex-wrap:wrap;flex-direction:column}.swiper-fade.swiper-free-mode .swiper-slide{transition-timing-function:ease-out}.swiper-fade .swiper-slide{pointer-events:none;transition-property:opacity}.swiper-fade .swiper-slide .swiper-slide{pointer-events:none}.swiper-fade .swiper-slide-active{pointer-events:auto}.swiper-fade .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper.swiper-cube{overflow:visible}.swiper-cube .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1;visibility:hidden;transform-origin:0 0;width:100%;height:100%}.swiper-cube .swiper-slide .swiper-slide{pointer-events:none}.swiper-cube.swiper-rtl .swiper-slide{transform-origin:100% 0}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-next,.swiper-cube .swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-cube .swiper-cube-shadow{position:absolute;left:0;bottom:0px;width:100%;height:100%;opacity:.6;z-index:0}.swiper-cube .swiper-cube-shadow:before{content:'';background:#000;position:absolute;left:0;top:0;bottom:0;right:0;filter:blur(50px)}.swiper-cube .swiper-slide-next+.swiper-slide{pointer-events:auto;visibility:visible}.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-bottom,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-left,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-right,.swiper-cube .swiper-slide-shadow-cube.swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper.swiper-flip{overflow:visible}.swiper-flip .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1}.swiper-flip .swiper-slide .swiper-slide{pointer-events:none}.swiper-flip .swiper-slide-active,.swiper-flip .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-bottom,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-left,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-right,.swiper-flip .swiper-slide-shadow-flip.swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-creative .swiper-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden;transition-property:transform,opacity,height}.swiper.swiper-cards{overflow:visible}.swiper-cards .swiper-slide{transform-origin:center bottom;-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden}
/* VENDOR END */

/* ---- BOOK NOW POPUP --------------------------------------------------------
   Client request 2026-10-17: no booking engine. Every Book Now CTA opens this
   dialog with the reservations number and a Call Us button (built in site.js). */
.bk-modal{
  position:fixed; inset:0; z-index:110;
  display:flex; align-items:center; justify-content:center;
  padding:var(--space-6) var(--space-4);
  background:rgba(23,10,2,.72);
}
.bk-modal[hidden]{ display:none; }
.bk-modal__card{
  position:relative; width:100%; max-width:460px;
  padding:var(--space-12) var(--space-10) var(--space-10);
  background:var(--ground); color:var(--ink); text-align:center;
  border-radius:var(--r-2); box-shadow:var(--shadow-md);
}
.bk-modal__card h2{ margin:0; }
.bk-modal__card p:not(.eyebrow){ margin-block-start:var(--space-4); font-size:var(--text-body); line-height:var(--lh-body); }
.bk-modal__card .link{ white-space:nowrap; font-weight:var(--w-bold); }
.bk-modal__call{ margin-block-start:var(--space-8); }
.bk-modal__close{
  position:absolute; inset-block-start:var(--space-3); inset-inline-end:var(--space-3);
  width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center;
  border:1px solid var(--rule); border-radius:var(--r-circle); color:var(--ink); background:transparent;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.bk-modal__close:hover{ background:var(--ink); color:var(--on-dark); }
html.bk-open .mobar,
html.bk-open .fab,
html.bk-open .cookie{ display:none; }
@media (max-width:767px){
  .bk-modal__card{ padding:var(--space-12) var(--space-6) var(--space-8); }
  .bk-modal__call{ width:100%; }
}

/* Suite page hero: frame on the bed (bottom-right of the photo). On landscape
   screens 1024px+ (where the photo is taller than the banner) the view sits 60px
   higher than the photo's bottom edge. */
.page-hero > img.hero-img--suite{ object-position:100% 100%; }
@media (min-width:1024px) and (orientation:landscape){
  .page-hero > img.hero-img--suite{ object-position:right 0 bottom -60px; }
}
