/* ==========================================================================
   VANAASHRYA RESORT & SPA — Ranthambore, Sawai Madhopur, Rajasthan
   Structure, motion and interaction modelled on casangelina.com
   Cinzel (headings) + Montserrat (body/UI) + Allura (cursive accent)
   ========================================================================== */

/* ---------- 1. RESET ---------- */
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,
acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,
strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,
table,caption,tbody,tfoot,thead,tr,th,td,figure,figcaption,main,section,article,
aside,header,footer,nav{margin:0;padding:0;border:0;outline:0;font-size:100%;
vertical-align:baseline;background:transparent}
ol,ul{list-style:none}
blockquote,q{quotes:none}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}
:focus{outline:0}
ins{text-decoration:none}
del{text-decoration:line-through}
table{border-collapse:collapse;border-spacing:0}
input,select,img,iframe,video{vertical-align:middle}
h1,h2,h3,h4,h5,h6{font-weight:inherit}
*,*::before,*::after{box-sizing:border-box}

/* ---------- 2. TOKENS ---------- */
:root{
  /* palette */
  --forest:#1E5536;
  --deep:#0F3B27;
  --botanical:#127A3B;
  --orange:#E67817;
  --ivory:#F5F1E8;
  --sand:#E5D8C3;
  --earth:#403C31;
  --white:#FFFFFF;
  /* THE CARD GROUND — a LITTLE off white, and the third value it has had.
     Pure #FFFFFF against the #F5F1E8 page was a 23-point jump in the blue
     channel alone: the card did not read as raised, it read as a hole cut in
     the page with a light behind it. #FBF9F5 halved that to 13 and still
     dominated a page whose whole character is matte cream — a grid of nine
     cards became a grid of nine bright panels with the page showing between
     them, which inverts what should be figure and what should be ground.

     This is the same warm mix carried a QUARTER of the way to white rather
     than most of it: a 6-point lift, enough that a card is a distinct plane
     against the page, small enough that the photograph inside it is the
     brightest thing on the card. The hairline does the rest of the work.

     --white stays for form fields, where an editable box is expected to be
     the brightest thing on screen. */
  --card:#F8F5EE;

  /* the soft green the controls are built from, and its 10%-darker press */
  --sage:#4E7D5C;
  --sage-dark:#467054;
  /* --deep is the bar's Book now, and now every button's hover ground. Its
     press is a step DOWN from it, not up: a primary that lightens under the
     hand reads as releasing rather than as being pushed. */
  --deep-press:#0A2A1C;
  /* The same sage, laid over the page ground for bands that want a tint
     without becoming a filled section. Kept as an ALPHA rather than a mixed
     hex so its strength is one number to turn — as a flat #E4E8DB, "make it
     lighter" meant recomputing three channels by hand every time. */
  --wash:rgba(78,125,92,.07);
  /* The pale green a band paints edge to edge when it wants to separate
     itself from the page ground without becoming a filled section. Cooler
     than --ivory, so the two read as different papers rather than as one
     paper lit unevenly. */
  --wash-solid:#E2E4D2;

  /* roles */
  --bg:var(--ivory);
  --text:#272727;          /* body copy on a light ground */
  --text-muted:#6E6858;    /* labels, captions, meta — not paragraphs */
  --line:#D9CEB9;
  --line-on-dark:rgba(245,241,232,.22);
  --on-dark:var(--ivory);
  --on-dark-muted:rgba(245,241,232,.66);

  /* families — Cinzel for headings, Montserrat for everything read at length,
     Allura for the cursive accents that open a block */
  --display:"Cinzel",Georgia,"Times New Roman",serif;
  --ui:"Montserrat","Helvetica Neue",Arial,sans-serif;
  --accent:"Allura","Brush Script MT",cursive;

  /* Editorial display tier — matches the reference's ~11vw wordmarks.
     These sit ABOVE the brief's heading scale and are used only for the
     payoff and the big section wordmarks. Ordinary headings still use
     --h1…--h4 below, which stay inside the brief's ranges. */
  /* Cinzel is an all-caps face and runs noticeably wider than a garamond,
     so the display tier is pulled back to keep the nowrap lines on one line */
  --display-xl:clamp(38px,5.8vw,92px);     /* FIND */
  --display-lg:clamp(28px,4.4vw,68px);     /* section ghost wordmarks */
  --display-sub:clamp(16px,1.9vw,28px);
  --display-vert:clamp(24px,3.2vw,52px);   /* rotated rail labels */

  /* tint-on-tint display colours, drawn from the brand palette */
  --tint-on-light:rgba(30,85,54,.20);      /* forest, barely there on ivory */
  --tint-on-light-2:rgba(64,60,49,.42);    /* earth, for the smaller half */

  /* headings that read at full strength — forest green cut 50/50 with the
     earth grey, so it sits between the two rather than beside either */
  --heading:#2F4934;

  /* type scale — desktop 1440+. h1 is the property name at 45px; each step
     below is ~1.3 down from it, which keeps four ranks distinguishable
     without any of them turning monumental. */
  --h1:45px;
  --h2:34px;
  --h3:26px;
  --h4:20px;
  --body-lg:18px;
  --body:16px;
  --body-sm:15px;
  --body-lh:26px;          /* paragraph leading — 1.625 on the 16px body */
  --caption:13px;
  --caption-xs:11px;

  /* layout */
  /* the page's left/right inset — 100px on desktop, stepped down with the
     viewport. The header rides the same value so the bar's contents line up
     with the section contents below it. */
  --gutter:100px;
  --maxw:1440px;
  /* THE BAR DOES NOT RESIZE, and it is the SHORT one in both states.
     It used to float at 96px with a 210px wordmark, then drop to 74/152 once
     it took its ground — so the menu, the logo and the button all moved and
     changed size at the 20% line. Collapsing the two states could go either
     way; it goes to the smaller, because 96px of ivory across the top is a
     lot of bar to carry down a whole page, and the compact one is the state
     the page is in for all but its first screen.

     Only the GROUND changes now: transparent to ivory. */
  --header-h:74px;
  --header-x:var(--gutter);
  /* Kept as a token rather than deleted: several sections reserve the bar's
     height in their own padding, and one name for it means they cannot drift
     apart from it. */
  --header-h-solid:var(--header-h);

  /* Section rhythm — two numbers, and nothing else decides vertical space:
       --band-y   100px  a band's own top and bottom padding
       --band-in   80px  …but 80 once the band paints a colour or a photograph,
                         because the edge of a filled band is already a visible
                         boundary and does not need the full figure to read as
                         separate from what came before it.
     Both scale down on a phone, where 100px of dead space is most of a screen. */
  --band-y:100px;
  --band-in:80px;
  --sec-y:50px;              /* survives only as the payoff's top inset */

  /* ---- the three spacing numbers every band shares ----
     Before these existed each section invented its own: the head-to-content
     gap alone ran 0, 42, 52, 88 and 132px in five places, which is why no two
     bands kept the same rhythm. One token each, overridden nowhere. */
  --head-gap:clamp(30px,4vh,58px);    /* section head → the content it opens  */
  --col-gap:clamp(34px,4vw,64px);     /* the two-column split, head AND body  */
  /* BLOCK → BLOCK, and a stated figure rather than a clamp. --head-gap opens
     a head onto the content beneath it and is allowed to breathe with the
     viewport; this is the air between two finished blocks stacked on an inner
     page, and it reads as a measurement rather than as breathing room. It
     used to borrow --head-gap, which meant the same gap was 43px on a 1080
     screen, 36 on a 900 and 32 on an 800 — three different answers to one
     question. It matches the 50px already set BETWEEN the mission cards, so
     the air across the block and the air down it are the same figure. */
  --block-gap:50px;                   /* one finished block → the next        */

  /* motion */
  --ease-expo:cubic-bezier(.19,1,.22,1);
  --ease-inout:cubic-bezier(.575,.005,.285,1.005);

  /* viewport unit that survives mobile browser chrome */
  --vh:1vh;
  /* The bar is fixed and never scrolls away, so a "full viewport" of usable
     space is a viewport MINUS the bar. Anything sized to the screen, or
     centred against it, must measure with this — not with 100vh — or its
     top edge ends up underneath the header. */
  --vh-view:calc(var(--vh) * 100 - var(--header-h-solid));
}

/* ==========================================================================
   2c. SERVICES — the TideScape "Section - Service" block
   Geometry taken from the live reference at 1440: section padding 100/20,
   container max 1600 with an 80px gap, cards stacked 30px apart, each one a
   1px rule then a row of three equal columns (name · picture · copy) split
   by 300px gaps, the picture 370px tall and capped at 400 wide, and 40px of
   air under each card. Skinned to our own tokens rather than the reference's
   cream and Oranienbaum.
   ========================================================================== */
#services{
  /* flat and edge to edge — no inset sheet. The band separates itself with
     its own colour, so it runs the full width of the page. */
  background:var(--wash-solid);
  display:flex;justify-content:center;align-items:center;
  /* the band's own figure, not a bespoke clamp. The last card already ends on
     40px of its own bottom padding, so a full 100px under it stacked to ~140
     and opened a hole between this band and the green one below. */
  padding:var(--band-y) var(--gutter) calc(var(--band-y) - 40px);
  position:relative;z-index:3;
}
/* the same torn sheet the gallery lays down — same artwork, same inset, so
   the two bands read as pages from one book rather than two treatments */
.svcContainer{position:relative;z-index:1}
.svcContainer{
  max-width:1600px;width:100%;
  display:flex;flex-direction:column;align-items:center;
  /* head → first card. The reference runs 80px here, but its head is a heading
     alone; ours carries an eyebrow, copy and a button, so the block already
     ends on a good deal of air and 80 on top of it reads as a hole. */
  gap:clamp(24px,2.8vw,40px);
}
.svcHead{
  width:100%;text-align:center;
  display:flex;flex-direction:column;align-items:center;
}
.svcHead .secEyebrow{margin-left:0}
.svcLead{
  max-width:56ch;margin:clamp(10px,1.2vh,16px) 0 0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
.svcHeadCta{margin-top:clamp(14px,1.8vh,24px)}
.svcTitle{
  display:block;margin:0;
  font-family:var(--display);font-weight:300;
  font-size:var(--h1);line-height:1.14;letter-spacing:.03em;
  color:var(--heading);
}
.svcTitle .line{display:block;overflow:hidden;padding-bottom:.06em}

/* the stack. Cards overlap while they animate, so each later one paints over
   the one above it — which is exactly the "dealt out from behind" read. */
.svcCards{
  width:100%;display:flex;flex-direction:column;align-items:center;gap:30px;
}
.svcCard{
  width:100%;position:relative;
  display:flex;flex-direction:column;align-items:flex-start;
  gap:40px;padding:0 0 40px;
  /* Opaque, and stacked in REVERSE order — servicesCards() writes a
     descending z-index so card 1 paints over card 2, card 2 over card 3 and
     so on. That is what makes a card emerge from BEHIND the one above it as
     it slides down, instead of sliding over the top of it. Without the solid
     ground the pictures simply show through one another.

     --wash-solid, not --ivory: it has to be opaque for the stack to work, so
     it matches the band's own ground rather than being transparent to it —
     an ivory card on the green would read as a rectangle laid over it. */
  background:var(--wash-solid);
  will-change:transform,opacity;
}
.svcLine{display:block;width:100%;height:1px;background:var(--line);flex:none}

.svcRow{
  width:100%;display:flex;flex-direction:row;align-items:flex-start;
  gap:50px;                       /* name → picture → copy, 50px each */
}
/* A FIXED column, not auto — sized to the longest name. Letting each name take
   its natural width made every card's picture a different size, because the
   picture is what absorbs the leftover space. */
.svcName{
  /* wide enough for the longest name ("Rooftop Terrace") at this size — if the
     basis is narrower, nowrap forces the column wider and that card's picture
     ends up smaller than the others */
  flex:0 0 clamp(150px,21vw,300px);margin:0;white-space:nowrap;
  font-family:var(--display);font-weight:400;
  font-size:clamp(22px,2.2vw,32px);line-height:1.2;letter-spacing:.01em;
  color:var(--heading);
}
/* landscape, and identical on every card now that both text columns are fixed */
.svcImg{
  flex:1 1 auto;min-width:0;max-width:none;
  height:clamp(170px,18vw,260px);
  overflow:hidden;position:relative;background:var(--sand);
}
.svcImg img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1400ms var(--ease-expo);
}
.svcCard:hover .svcImg img{transform:scale(1.05)}
.svcCopy{flex:0 0 clamp(240px,22%,320px);min-width:0}
.svcCopy p{margin:0;font-size:var(--body-sm);line-height:1.65;color:var(--text)}
.svcCta{margin-top:22px}

@media (max-width:1199px){
  .svcRow{gap:34px}
  .svcCopy{flex-basis:clamp(210px,26%,280px)}
}
/* below the reference's own breakpoint the row becomes a column */
@media (max-width:809px){
  .svcRow{flex-direction:column;align-items:stretch;gap:16px}
  /* flex:none is the whole fix. The name carries flex:0 0 clamp(150px,21vw,300px)
     for the row layout, where that basis is a WIDTH — a fixed column sized to
     the longest name so every card's picture comes out the same size. Turn the
     row into a column and the same basis becomes a HEIGHT, so "Basil" sat in a
     box up to 300px tall and the card opened with a screenful of nothing
     between the name and the photograph. */
  .svcName{white-space:normal;flex:none}
  .svcImg{width:100%;height:auto;aspect-ratio:1.6/1}
  .svcCopy{flex:none;width:100%}
  .svcCta{margin-top:16px}
  .svcCards{gap:24px}
  .svcCard{gap:20px;padding-bottom:24px}
}

@media (prefers-reduced-motion:reduce){
  /* no slide — the cards are simply present */
  .svcCard{transform:none !important;opacity:1 !important}
}

/* ---------- 3. BASE ---------- */
html{font-size:100%;-webkit-text-size-adjust:100%}
body{
  font-family:var(--ui);
  font-size:var(--body);
  font-weight:400;
  line-height:var(--body-lh);
  color:var(--text);
  background:var(--bg);
  overflow:hidden;
  cursor:default;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
body::-webkit-scrollbar{display:none}
body *{-webkit-tap-highlight-color:rgba(0,0,0,0)}
body.exiting{opacity:0}

/* the scroll container keeps its own stacking context — the flock that used
   to sit behind it is gone, but the pinned sections still rely on this */
[data-scroll-container]{position:relative;z-index:1}
img{max-width:100%;display:block}

a{color:inherit;text-decoration:none}
button,input,select{font:inherit;color:inherit;background:none;border:0}
/* keep the palette to two families — no monospace creeps in via <code> */
code,kbd,samp{font-family:var(--ui);font-size:.94em;letter-spacing:.01em}
::selection{background:var(--orange);color:var(--white)}

/* ---------- 4. TYPOGRAPHY ---------- */
.d1,.d2,.h1,.h2,.h3,.h4{font-family:var(--display);font-weight:300;line-height:1.04;letter-spacing:.005em}
.h1{font-size:var(--h1)}
.h2{font-size:var(--h2)}
.h3{font-size:var(--h3);line-height:1.16}
.h4{font-size:var(--h4);font-weight:400;line-height:1.25}
.text-lg{font-size:var(--body-lg);line-height:1.65}
.text-sm{font-size:var(--body-sm);line-height:1.6}
.eyebrow{
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;line-height:1;
  color:var(--botanical);display:inline-flex;align-items:center;gap:14px;
}
.eyebrow::before{content:"";width:30px;height:1px;background:currentColor;flex:0 0 30px}
.eyebrow.no-rule::before{display:none}
.on-dark .eyebrow,.eyebrow.on-dark{color:var(--sand)}
/* paragraphs: 16/32 in #272727 unless they sit on a dark ground */
p{max-width:64ch;font-size:var(--body);line-height:var(--body-lh);color:var(--text)}
.on-dark p,.section-dark p,.section-forest p{color:var(--on-dark-muted)}
p+p{margin-top:1.15em}
.uppercase{text-transform:uppercase}
.italic{font-style:italic}
.center{text-align:center}

/* letterspaced editorial word — e t h o s / t a s t e */
.spaced{letter-spacing:.34em;text-transform:lowercase}
.spaced .ltr{display:inline-block}

/* ---------- 5. LAYOUT ---------- */
.wrap{width:100%;padding-left:var(--gutter);padding-right:var(--gutter)}
.inner{max-width:var(--maxw);margin:0 auto;width:100%}
.section{position:relative}
/* a band on the page ground pays 100; add .pad-filled when the band paints a
   colour or carries a photograph and it pays 80 instead */
/* Top 50, foot still the band figure. The three sections carrying this class
   run consecutively on the homepage — weddings, then CSR, then the guest book
   — so every join between them was paying --band-y twice, 200px of ivory
   between one heading and the next. Halving the top closes the join without
   touching the space the last of them leaves above the footer. */
.pad-band{padding-top:50px;padding-bottom:var(--band-y)}
.pad-band.pad-filled{padding-top:var(--band-in);padding-bottom:var(--band-in)}
/* the weddings band closes on 50 as well, so the gap between it and the CSR
   band below is 100 in total rather than 150 */
#weddings.pad-band{padding-bottom:50px}
.relative{position:relative}
.no_overflow{overflow:hidden}
.block{display:block}
.inline_block{display:inline-block}
.section-dark{background:var(--deep);color:var(--on-dark)}
.section-forest{background:var(--forest);color:var(--on-dark)}
.section-white{background:var(--white)}

/* ---------- 6. REVEAL ENGINE (mirrors the reference class system) ---------- */
.top_hidden{transform:translateY(101%)}
.top_translated{transform:translateY(25vw)}
.top_translated.less{transform:translateY(10vw)}
.top_single{transform:translateY(50px);opacity:0}
.top_low{transform:translateY(20px);opacity:0}
.top_ultralow{transform:translateY(5px);opacity:0}
.scaled{transform:scale(1.14)}
.no_opacity{opacity:0;pointer-events:none}

.top_hidden.show,.show .top_hidden,
.top_translated.show,.show .top_translated,
.scaled.show,.show .scaled{transform:none;pointer-events:auto}
.top_single.show,.show .top_single,
.top_low.show,.show .top_low,
.top_ultralow.show,.show .top_ultralow{transform:none;opacity:1}
.no_opacity.show,.show .no_opacity{opacity:1;pointer-events:auto}

.has_transition_400{transition:transform 400ms var(--ease-expo),opacity 400ms var(--ease-expo)}
.has_transition_800{transition:transform 800ms var(--ease-expo),opacity 800ms var(--ease-expo)}
.has_transition_1000{transition:transform 1000ms var(--ease-expo),opacity 1000ms var(--ease-expo)}
.has_transition_1500{transition:transform 1500ms var(--ease-expo),opacity 1000ms var(--ease-expo)}
.has_transition_2000{transition:transform 2000ms var(--ease-expo),opacity 2000ms var(--ease-expo)}
.has_transition_3000{transition:transform 3000ms var(--ease-expo),opacity 3000ms var(--ease-expo)}
.has_transition_1000_inout{transition:transform 1000ms var(--ease-inout),opacity 1000ms var(--ease-inout)}
.no_transition{transition:none !important}

.show .d50,.show.d50{transition-delay:50ms}
.show .d100,.show.d100{transition-delay:100ms}
.show .d150,.show.d150{transition-delay:150ms}
.show .d200,.show.d200{transition-delay:200ms}
.show .d250,.show.d250{transition-delay:250ms}
.show .d300,.show.d300{transition-delay:300ms}
.show .d350,.show.d350{transition-delay:350ms}
.show .d400,.show.d400{transition-delay:400ms}
.show .d450,.show.d450{transition-delay:450ms}
.show .d500,.show.d500{transition-delay:500ms}
.show .d550,.show.d550{transition-delay:550ms}
.show .d600,.show.d600{transition-delay:600ms}
.show .d650,.show.d650{transition-delay:650ms}
.show .d700,.show.d700{transition-delay:700ms}
.show .d750,.show.d750{transition-delay:750ms}
.show .d800,.show.d800{transition-delay:800ms}
.show .d900,.show.d900{transition-delay:900ms}
.show .d1000,.show.d1000{transition-delay:1000ms}
.show .d1100,.show.d1100{transition-delay:1100ms}
.show .d1200,.show.d1200{transition-delay:1200ms}

/* mask slide (gallery / experience picture swaps) */
.mask_top{transform:translateY(-50%)}
.mask_top .t_container{transform:translateY(50%)}
.mask_bottom{transform:translateY(50%)}
.mask_bottom .t_container{transform:translateY(-50%)}
.show>.mask_top,.show>.mask_top .t_container,
.show>.mask_bottom,.show>.mask_bottom .t_container{transform:none}

/* ---------- 7. BUTTONS ----------
   Two states, one component.

   · DEFAULT is hollow — a hairline rule and the label, nothing else. On hover
     it fills SOLID FOREST GREEN and leaves start falling inside it.
   · .is-primary is the booking control — filled from the start, so it needs
     no discovering. It takes the same green and the same falling leaves on
     hover; only its resting state differs.

   The green is --forest #1E5536, and it is chosen FOR the leaves rather than
   from the control system it replaced. It is the brand primary rather than a
   colour invented for one hover.

   THE LEAVES ARE RECOLOURED to #678850, a pale sage-olive, from the
   fluorescent olive-lime (~#A9BE33) the artwork shipped with. What kills the
   fluorescence is DESATURATION, not darkness: saturation drops from about
   .55 to .26 while lightness only comes down a little. Taking the lightness
   down instead was tried first and read as black.

   The lightness has to stay clear of the ground on ONE side or the other.
   Measured against #1E5536, a leaf near the ground's own lightness vanishes
   into it — 1.15:1 at 70% darker, 1.04:1 at 62%. Below the ground the leaves
   come back as silhouettes (1.94:1 at 81% darker); above it they come back
   as leaves (2.17:1 here). The dead zone is the middle, not the ends.

   The recolour is palette surgery on the GIF's 80 colour tables, not a
   re-encode, so every frame, delay and transparency flag is the original's.
   giphy.gif in the project root is the untouched source.

   What this replaced: a wood-grain fill wiped in on a circular clip-path,
   plus a leaf that unfurled into space opening after the label, plus a 2px
   lift. Three effects competing for one gesture. The animation is the whole
   effect now, so everything else gets out of its way — the fill is a plain
   cross-fade and the button does not move.

   One word per label. A button that has to say "step into our world" is a
   sentence wearing a border. */
.oButton{
  display:inline-flex;align-items:center;justify-content:center;gap:0;
  /* square-cut, like every other edge on the site.
     46/30/14 down to 42/26/13. At the old figures a two-word label like
     BOOK NOW came out about 160px wide and 46 tall, which is a control sized
     for a form rather than for a line of copy — on a phone it was the widest
     thing on the screen after the photographs. */
  height:42px;padding:0 26px;border-radius:0;
  font-family:var(--ui);font-size:13px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;line-height:1;
  cursor:pointer;position:relative;overflow:hidden;isolation:isolate;
  background:transparent;border:1px solid currentColor;color:var(--heading);
  /* ONE duration for all three, and background-COLOR rather than the
     background shorthand.

     They used to run 420 / 320 / 380ms. Under a pointer that stagger is
     invisible — the eye is already on the control and the fill is what it is
     watching. The bar's swap is involuntary, though: it happens while you are
     looking somewhere else entirely, and there the same stagger reads as a
     fault. The label turns white at 320ms, the ground goes green at 380, and
     the border is still cream until 420 — a bright 1px outline hanging round
     an already-dark button for a twentieth of a second, every time.

     The shorthand was the other half of it: `background` carries
     background-image, which is not interpolable, so every state change also
     restarted the leaf layer's image discretely. background-color leaves it
     alone. */
  transition:background-color 380ms var(--ease-expo),
             border-color 380ms var(--ease-expo),
             color 380ms var(--ease-expo);
}

/* THE FILL — solid forest, with the leaves falling on top of it.
   One layer, two backgrounds: the GIF is transparent everywhere but the
   leaves, so the green behind it is what they fall against.

   The GIF is drawn at 240px rather than stretched to the button. Its source
   is 480px square with leaves around 25px, so a `cover` fit on a 46px-tall
   control would have blown one or two leaves up past the label, while
   scaling it down to the button height would have ground them to under 3px.
   240 halves them to about 12px, and since that is wider than most buttons,
   the tile never repeats visibly across one.

   The number is sampled, not guessed. Measuring the share of a hovered
   button covered by leaf rather than green, six times a second apart:
   200px averaged 2.3% (busy, confetti-sized), 300px averaged 2.4% but dipped
   to 0.68% — a moment where the button is just a green rectangle. 240px
   averaged 3.4% and never fell below 1.7%, so there is always something
   falling. */
/* The GROUND now lives on the button itself and this layer carries only the
   leaves. They used to share one background — gif over --forest — and that
   made the leaves untouchable: a filter here would have hit the green as
   well. Split apart, the gif can be recoloured without moving the ground.

   z-index:0, not -1. At -1 the layer sits behind the element's own
   background, which was fine while this rule painted the green too; now that
   the button paints it, -1 would bury the leaves under it. The label is held
   above by `.oButton>*{z-index:1}`. */
.oButton::before{
  content:"";position:absolute;inset:-1px;z-index:0;border-radius:inherit;
  background:url("../assets/images/leaf-fall.gif") center / 240px 240px repeat;
  /* the artwork was tuned against --forest; --deep is darker and cooler, so
     the leaves lift a little and lose the last of their yellow to match it */
  filter:brightness(1.14) saturate(.86) hue-rotate(-6deg);
  /* HIDDEN, not merely transparent. An animated GIF at opacity:0 is still a
     running animation: the browser keeps advancing its frames and keeps
     marking the button dirty, thirty times a second, forever. Inside a FIXED
     bar that is also transitioning its own height and ground, those two
     repaint sources land on the same element and the button tears — which is
     the flicker on the header control specifically, and not on any other
     button on the page, because no other button lives in a bar that moves.

     visibility:hidden takes the layer out of painting entirely, so at rest
     there is nothing animating inside the header at all. The 0s delay is what
     makes the fade still work: on the way IN it flips immediately so the
     opacity has something to reveal; on the way OUT it waits the full 380ms
     so the leaves fade rather than vanish. */
  opacity:0;visibility:hidden;
  transition:opacity 380ms var(--ease-expo),visibility 0s linear 380ms;
}
.oButton:hover::before,.oButton:focus-visible::before{
  opacity:1;visibility:visible;
  transition:opacity 380ms var(--ease-expo),visibility 0s linear 0s;
}

/* the Book now green, now every button's hover ground — the leaves fall on it */
.oButton:hover,.oButton:focus-visible{
  background:var(--deep);border-color:var(--deep);color:var(--white);
}
/* hollow on a dark ground reads in ivory */
.on-dark .oButton,.section-dark .oButton,.section-forest .oButton,
#rooms .oButton,#hFacilitiesHead .oButton{color:var(--ivory)}

/* ---- the primary: the bar's green, on every Book now on the site ----
   ONE Book now, everywhere. This used to be an ivory chip that only became
   the green bar-button once the header went solid, so the same control had two
   appearances on one page — cream over the hero footage, green everywhere
   below it — and the booking bar's button and the overlay's button each
   disagreed with the bar above them. The bar's version is the one that reads,
   so it is now the definition and the header rule that used to impose it has
   been deleted rather than left as a second source of truth. */
.oButton.is-primary{
  background:var(--deep);border-color:var(--deep);color:var(--white);
}
/* Already --deep at rest, so its press goes DOWN to --deep-press rather than
   sideways to the same green. A primary that does not change under the hand
   reads as disabled; one that lightens reads as releasing. */
.oButton.is-primary:hover,.oButton.is-primary:focus-visible{
  background:var(--deep-press);border-color:var(--deep-press);color:var(--white);
}

/* the label sits above the fill; without this the leaves fall over the type */
.oButton>*{position:relative;z-index:1}

/* an animated GIF is motion, and motion is the thing being opted out of.
   The fill still arrives so the control still answers the pointer. */
@media (prefers-reduced-motion:reduce){
  .oButton::before{background-image:none}
}

.oLabelContainer{overflow:hidden;display:block}
.oLabel{display:block;position:relative;z-index:1}
.oButton.open .oLabel{transform:none}

/* text link with the logo swoosh as its underline */
.swooshLink{
  display:inline-block;position:relative;font-family:var(--ui);
  font-size:var(--caption);font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;padding-bottom:12px;cursor:pointer;
}
.swooshLink::after{
  content:"";position:absolute;left:0;bottom:0;width:100%;height:6px;
  background:var(--orange);
  -webkit-mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 10' preserveAspectRatio='none'%3E%3Cpath d='M0 8 C 26 -2 50 10 72 5 C 92 1 106 2 120 4 L120 7 C 104 5 92 4 74 8 C 50 13 24 3 0 10 Z' fill='%23000'/%3E%3C/svg%3E") no-repeat center/100% 100%;
  mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 10' preserveAspectRatio='none'%3E%3Cpath d='M0 8 C 26 -2 50 10 72 5 C 92 1 106 2 120 4 L120 7 C 104 5 92 4 74 8 C 50 13 24 3 0 10 Z' fill='%23000'/%3E%3C/svg%3E") no-repeat center/100% 100%;
  transform:scaleX(.4);transform-origin:left center;
  transition:transform 620ms var(--ease-expo);
}
.swooshLink:hover::after{transform:scaleX(1)}

/* ---------- 8. HEADER ---------- */
header{
  position:fixed;inset:0 0 auto 0;z-index:90;height:var(--header-h);
  display:flex;align-items:center;justify-content:space-between;
  /* the burger sits 100px off the left edge, BOOK NOW 100px off the right */
  padding-left:var(--header-x);padding-right:var(--header-x);
  color:var(--ivory);
  transition:color 600ms var(--ease-expo);
  pointer-events:none;
}
header.dark{color:var(--earth)}
header>*{pointer-events:auto}

/* ---------- 8a. SOLID BAR ----------
   Once 20% of the hero has scrolled past, the header stops floating and takes
   a ground of its own, shrinking a little as it does. */
header{
  --bar:transparent;
  background-color:var(--bar);
  /* The hairline is declared HERE, transparent, rather than appearing with
     .is-solid. Under border-box a border that arrives out of nowhere takes
     its 1px out of the content box, so everything centred in the bar — the
     wordmark, the button — jumped half a pixel the instant the class landed.
     Present and transparent, the box never changes size and the rule simply
     colours in.

     The ground runs at 380ms to match the button's swap: bar and control are
     one gesture, not two of different lengths overlapping. Height keeps its
     own longer curve — that is a separate axis and reads fine trailing. */
  border-bottom:1px solid transparent;

  /* NO DROP IN THE FLOATING STATE. There was a soft band under the bar meant
     to separate the white nav from a pale sky, but on the homepage's footage
     it reads as a smudge across the top of the film rather than as a lift, and
     the fold is the one place the picture should be uninterrupted.

     Removing it here removes it from the homepage ONLY: every inner page
     carries body.is-inner and is therefore .is-solid from the first frame, so
     the floating state is never drawn there and the rule below governs. */

  transition:color 600ms var(--ease-expo),
             background-color 380ms var(--ease-expo),
             border-color 380ms var(--ease-expo),
             box-shadow 380ms var(--ease-expo),
             height 500ms var(--ease-expo);
}
/* one ground, whatever is underneath — white rather than the page's ivory,
   so the bar separates from the ground it floats over instead of merging
   into it. Wordmark back in its original colours, type dark to suit. */
/* The bar takes the PAGE's own cream, not white. White was chosen so the bar
   would separate from the ground by colour, but the page is ivory everywhere
   and a pure-white strip across the top of it reads as a different, colder
   surface — the join was visible as a seam. With both on --ivory the bar is
   held apart by its edge alone, so the hairline and the drop below it carry a
   little more weight than they did. */
/* THE DROP, SOLID STATE.
   The bar and the page are both --ivory, so with no hairline and no shadow
   nothing at all stated the edge: on an inner page the bar simply merged into
   the sheet and the breadcrumb looked like it was floating under the
   wordmark. This was a deliberate earlier decision and it is being reversed
   here — a drop separates the two without drawing a line across the page, and
   it is the one thing that works on BOTH grounds, so the bar keeps the same
   device whether it is over footage or over the page.

   Two layers, because one blurred shadow on a warm ground goes muddy: a 1px
   contact edge that does the work the hairline used to do, and a wide soft
   fall that gives the lift. Both are drawn from --earth rather than black, so
   the shadow is warm and sits in the palette instead of greying the ivory. */
header.is-solid{
  height:var(--header-h-solid);
  --bar:var(--ivory);
  color:var(--earth);
  box-shadow:0 1px 0 rgba(64,60,49,.045),
             0 5px 18px rgba(64,60,49,.045);
  /* The border stays declared transparent on the base rule above — it has to,
     or the box would change size the moment the class lands and everything
     centred in the bar would shift half a pixel. It is still never coloured
     in: the drop states the edge now, and a rule on top of a shadow says the
     same thing twice. */
}

/* The drawer rides UNDER the bar, and the bar sits on its dark ground while
   it is open. A drop shadow there would be a band of ivory-tinted haze lying
   across the top of the menu, so it is taken off for the length of the
   overlay and comes back with it. */
body.nav-open header{box-shadow:none}

/* the wordmark returns to full colour — this has to beat the header.dark
   swap above. Its WIDTH no longer changes: only the ink does, so the mark
   stays exactly where it was and the same size it was. */

/* ONE COLOUR, BOTH STATES. The booking control used to invert — a white chip
   over the footage, a green one over the ivory bar — on the reasoning that a
   white chip cannot be seen against a white bar. True, but it made the most
   important button on the page change colour underneath the reader as they
   scrolled, and a control that keeps changing identity reads as two different
   controls. It is green everywhere now, which works on both grounds: on the
   ivory bar by contrast, and on the footage because the label is white on a
   dark ground rather than dark on a light one.

   The colours themselves have moved UP to .oButton.is-primary, which the bar's
   button already carries — so the booking bar's submit and the overlay's
   submit now match it too, instead of each stating a different green. There is
   nothing left for the header to declare; an id rule repeating what the class
   already says is a second place to forget to change. */

/* No entry state on any of the fold's chrome. These all used to start at
   opacity:0 and wait for a .show class the intro sequence added on a timer —
   which is why a cold load showed bare footage with no header for a second
   or two. They are simply present now. */
/* ---- the one control on the left: three rules, then the word MENU ----
   White over the footage. It was briefly the button's deep green, but the
   hero is a dark forest for most of its run and the control disappeared into
   it — a menu you cannot find is worse than one that does not match. It
   takes the bar's own colour, so it inverts to the dark ink once the header
   lands on its ivory ground. */
/* The rules and the word are ONE control, so they take one colour. The lines
   paint with currentColor and the label inherits it, which means every state
   below only has to set `color` on the container and both halves move
   together — a label that went orange while the rules stayed dark read as two
   separate things sitting next to each other. */
.menuController{
  display:flex;align-items:center;gap:12px;
  position:relative;cursor:pointer;flex:0 0 auto;
  color:var(--white);
  transition:color 400ms var(--ease-expo);
}
header.is-solid .menuController{color:var(--earth)}
.menuController .bars{
  position:relative;display:block;width:18px;height:10px;flex:0 0 auto;
}
/* 2px, not 1.5. The label beside these is Montserrat 600 at 13px, whose stems
   land around 2px — at 1.5 the rules read lighter than the word they sit next
   to, so the control looked like two different weights bolted together. A hair
   under a full pixel of difference is enough to see at this size. */
.menuController .line{
  position:absolute;left:0;height:2px;width:18px;background:currentColor;
  transition:transform 500ms var(--ease-expo),width 500ms var(--ease-expo),
             opacity 300ms var(--ease-expo),background 400ms var(--ease-expo);
}
/* the stack is 2 + 2 + 2 with 2px of air between, so it still fills the 10px
   .bars box exactly and the open-state rotation keeps its 4px travel */
.menuController .line:nth-child(1){top:0}
.menuController .line:nth-child(2){top:4px}
.menuController .line:nth-child(3){top:8px}
/* the middle rule is the one that moves on hover, so the control has a
   tell without the whole stack twitching */
.menuController:hover .line:nth-child(2){width:12px}
.menuLabel{
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;white-space:nowrap;
}

/* hover, keyboard focus and open all land on the same orange, in both the
   transparent and the .is-solid header. The `header ...` prefix is what lets
   these outweigh the .is-solid colour above rather than lose to it. */
header .menuController:hover,
header .menuController:focus-visible,
body.nav-open header .menuController{color:var(--orange)}
/* colour alone is not a focus indicator — the ring stays, offset clear of the
   rules so it frames the whole control rather than clipping the top line */
.menuController:focus-visible{outline:2px solid var(--orange);outline-offset:6px}

/* open: the outer two rules cross, the middle one goes */
body.nav-open .menuController .line:nth-child(1){transform:translateY(4px) rotate(45deg)}
body.nav-open .menuController .line:nth-child(2){opacity:0;width:0}
body.nav-open .menuController .line:nth-child(3){transform:translateY(-4px) rotate(-45deg)}

#headerLeft{display:flex;align-items:center;gap:38px;flex:0 1 auto;min-width:0}
#headerRight{display:flex;align-items:center;gap:14px;flex:0 0 auto;min-width:0;justify-content:flex-end}

/* the wordmark is centred on the PAGE, not on the space left between the
   two flex ends — so it is taken out of flow and pinned to 50% */
#mainLogo{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  /* the compact wordmark, in both states — it used to be 210 floating and
     152 once solid, and this is that 152. It has to stay clear of the 74px
     bar it sits in: the mark is 1034x306, so 152 wide is 45 tall inside 74. */
  width:152px;
  transition:opacity 1000ms var(--ease-expo),transform 1000ms var(--ease-expo),
             width 500ms var(--ease-expo);
}
#mainLogo.hidden{opacity:0;transform:translate(-50%,-50%) translateY(-14px);pointer-events:none}
/* one image, no swap and no filter — the mark keeps its own colours whatever
   is behind it */
#mainLogo img{width:100%;height:auto;display:block}

#bookNow{flex:0 0 auto}

/* the header rides above the drawer so the hamburger stays live as the close
   control — and it always reads light while the drawer is open */
body.nav-open header{color:var(--ivory)}
body.nav-open #mainLogo.hidden{opacity:1;transform:translate(-50%,-50%);pointer-events:auto}

/* ---------- 9. NAV OVERLAY ---------- */
#mainNav{
  position:fixed;inset:0;z-index:80;visibility:hidden;pointer-events:none;
}
#mainNav.active{visibility:visible;pointer-events:auto}
#navBackground{
  position:absolute;inset:0;background:var(--deep);
  opacity:0;transition:opacity 700ms var(--ease-expo);
}
#mainNav.active #navBackground{opacity:1}
#navImage{
  position:absolute;inset:0;overflow:hidden;opacity:0;
  transition:opacity 1400ms var(--ease-expo);
}
#mainNav.active #navImage{opacity:.2}
#navImage img{width:100%;height:100%;object-fit:cover;transform:scale(1.08)}
/* The menu sits in the MIDDLE of the screen now, not at the top of it.
   It used to be pinned under the bar with the list growing downwards and an
   address line pushed to the floor — a layout that only looks composed when
   the list happens to be tall enough to reach the bottom. Fifteen short links
   never were, so the drawer read as a block of type stranded at the top of a
   screen of empty space.

   Symmetric padding is what makes the centring true: with header+40 on top and
   60 at the foot, "centred" was thirty-eight pixels high of centre. */
#navScroller{
  position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;
  padding:calc(var(--header-h) + 40px) var(--gutter);
  display:flex;flex-direction:column;justify-content:center;
}
/* flex:0 0 auto, not 1 0 auto — a growing child fills the box and there is
   nothing left for justify-content to centre */
#navInner{max-width:var(--maxw);margin:0 auto;width:100%;flex:0 0 auto}
/* THE MENU IS THE SITEMAP, and nothing else.
   It used to be four themed groups whose entries were mostly deep links into
   the homepage — a heading like "Stay" over four room types, another over
   three venues. That is a summary of the homepage, not a way to reach the
   site: the same page appeared under three headings and several pages did not
   appear at all. It is now the fifteen pages the information sheet lists, in
   the sheet's own order, with no group headings and no descriptions under the
   links. One entry per page, one page per entry.

   grid-auto-flow:column with a fixed row count, rather than columns:3 — a
   multi-column box would reflow the list every time an item's length changed,
   and the reading order has to stay top-to-bottom within each column. */
/* max-content columns, and the block centred as a whole. At 1fr the three
   columns stretched to the full 1440 measure, which put the first link hard
   against the left gutter and the third stranded at the right — the menu was
   as wide as the page whatever it contained. Sized to their content and
   centred, the three columns read as one object in the middle of the screen. */
/* 1fr columns inside a CAPPED, centred block — not max-content columns.
   max-content was how the list got centred, but a max-content column is as
   wide as its longest item and cannot be talked out of it, so once every link
   grew a 62px thumbnail the three columns simply outgrew the screen: at 1280
   they wanted about 1350px and had 1167. The cap does the centring instead,
   and 1fr columns divide whatever is actually there. */
.navList{
  display:grid;
  grid-auto-flow:column;
  grid-template-rows:repeat(5,auto);
  grid-auto-columns:minmax(0,1fr);
  max-width:min(1180px, 100%);
  margin:0 auto;
  gap:clamp(12px,1.5vh,20px) clamp(24px,3vw,56px);
  color:var(--ivory);
}
/* the long ones — "Accommodations / Rooms" — wrap rather than push the column */
.navList li{min-width:0}
.navList li{
  opacity:0;transform:translateY(18px);
  transition:opacity 900ms var(--ease-expo),transform 900ms var(--ease-expo);
}
#mainNav.active .navList li{opacity:1;transform:none}
/* Each link is a picture and a name. A drawer of fifteen identical lines of
   type is a table of contents; the thumbnail is what turns it into a way of
   choosing. They are 62x46 drawn from a 180px file, so the set costs about
   95KB in total, and they are lazy inside a hidden overlay — nothing is
   fetched until the menu is opened for the first time. */
.navList a{
  display:flex;align-items:center;gap:16px;
  font-family:var(--display);font-size:clamp(20px,1.55vw,26px);
  font-weight:300;line-height:1.25;
  transition:color 400ms var(--ease-expo),transform 500ms var(--ease-expo);
}
.navThumb{
  flex:none;width:62px;height:46px;overflow:hidden;
  background:rgba(245,241,232,.10);
}
.navThumb img{
  width:100%;height:100%;object-fit:cover;display:block;
  /* held back a little so fifteen photographs behind the type do not compete
     with the one the drawer already carries as its ground */
  opacity:.78;filter:saturate(.85);
  transition:opacity 500ms var(--ease-expo),transform 900ms var(--ease-expo);
}
.navList a:hover .navThumb img,
.navList a:focus-visible .navThumb img{opacity:1;transform:scale(1.08)}
.navName{display:block}
.navList a:hover,.navList a:focus-visible{color:var(--orange);transform:translateX(7px)}
/* the page you are already on states itself rather than offering itself */
.navList a[aria-current="page"]{color:var(--sand)}
/* #navFoot is gone. It carried the address, the telephone and the Instagram
   link across the foot of the drawer, all three of which the footer already
   states on every page of the site — and it was the thing forcing the menu to
   the top, because a block pinned to the floor has to push the list up off
   the centre to get there. */

/* ---------- 11. HERO (#hTop) ----------
   The hero pins itself for one viewport of scroll, then its clip window
   contracts into the payoff slot. All of it is written from one progress
   value in main.js; nothing here depends on position:fixed or sticky,
   which Locomotive's transformed container would break. */
#hTop{position:relative;z-index:2;height:calc(var(--vh)*100)}
#hImgContainer{
  position:absolute;inset:0;height:calc(var(--vh)*100);
  /* The pin translates this DOWN by up to a full viewport, so once the hero
     is held it physically covers the payoff below it — and #hTop outranks
     #hPay on z-index, so it was eating every hover and click down there. The
     footage is decorative; nothing in it is clickable. The booking bar is a
     SIBLING of this box, not a child, so it keeps its own pointer events. */
  pointer-events:none;
  will-change:transform;
}
#hImgLayer{position:absolute;inset:0;height:calc(var(--vh)*100)}
#hImgCover{
  position:absolute;inset:0;overflow:hidden;
  will-change:clip-path;
}
/* a <video> here, but it behaves exactly as the still did — object-fit and
   object-position apply the same way, so the handoff maths is unchanged */
#hImg{
  width:100%;height:100%;object-fit:cover;display:block;
  transform-origin:center center;will-change:transform;
  background:var(--deep);
}
/* No scrim on the hero. The fold carries no headline — only the bar above it
   and the booking pane at its foot, and both bring their own ground — so the
   footage is left exactly as shot. main.js still writes --heroScrim as the
   frame contracts; with nothing to fade it is simply inert. */

/* ---------- 11b. THE BOOKING BAR ----------
   Held to the foot of the fold, sitting on the footage rather than in a band
   of its own: a pane of smoked glass with the controls drawn straight onto
   it — no boxes inside boxes. It rides ABOVE the hero's clip window, so it
   stays put while the frame behind it contracts into the payoff slot.

   The tint is BLACK, not green. Tinting glass with the same hue as the
   forest behind it just muddied both — a neutral smoke lets the footage
   keep its own colour and puts the type on a genuinely neutral ground. */
.heroBook{
  /* one inset governs the whole pane — the fields row, the CTA's gap and the
     foot all read from it, so the left and right margins cannot drift apart */
  --book-x:20px;
  position:absolute;left:50%;bottom:clamp(20px,4vh,40px);
  transform:translateX(-50%);
  /* wide enough that "Rooms & guests" clears its column on one line */
  width:min(100% - 2 * var(--gutter), 1060px);
  z-index:12;display:flex;flex-direction:column;
  background:rgba(10,10,10,.44);
  -webkit-backdrop-filter:blur(22px) saturate(1.1);
  backdrop-filter:blur(22px) saturate(1.1);
  border:1px solid rgba(245,241,232,.16);
  border-radius:0;overflow:hidden;
  box-shadow:0 26px 60px -28px rgba(0,0,0,.72);
  color:var(--ivory);
}
.heroBookFields{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr)) auto;
  align-items:center;padding:11px var(--book-x);
}
.heroBookField{
  display:flex;flex-direction:column;gap:3px;min-width:0;
  padding:2px var(--book-x);border-left:1px solid rgba(245,241,232,.16);
}
/* the pane's own inset already spaces the first field — its own padding on
   top of that was what made the left margin wider than the right */
.heroBookField:first-child{border-left:0;padding-left:0}
/* the label outranks the value it carries — at this size small caps need the
   contrast, and a date reads perfectly well held back.
   line-height:1 is doing the real work: without it the label inherits the
   body's 32px paragraph leading, so every one-line label stood 32px tall and
   "Rooms & guests" — which wraps — stood 64px. That, not the paddings, is
   what made the pane grow when I meant to shrink it. */
.heroBookLabel{
  font-family:var(--ui);font-size:12px;font-weight:600;line-height:1;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ivory);
  white-space:nowrap;
}
/* the controls ARE the glass, not boxes sitting on it */
.heroBookField input,
.heroBookField select{
  width:100%;min-width:0;background:none;border:0;padding:0;
  font-family:var(--ui);font-size:13px;color:rgba(245,241,232,.72);
  cursor:pointer;line-height:1.2;
}
.heroBookField input::placeholder{color:rgba(245,241,232,.45)}
.heroBookField select option{color:var(--text);background:var(--ivory)}
.heroBookField input:focus-visible,
.heroBookField select:focus-visible{outline:2px solid var(--orange);outline-offset:4px}
/* the native date icons are dark on dark */
.heroBookField input[type="date"]::-webkit-calendar-picker-indicator{
  filter:invert(1);opacity:.7;cursor:pointer;
}
/* the booking CTA is the same control as every other button — it only adds
   its own margin and the arrow. Everything else comes from .oButton, so the
   canopy wipe and the unfurling leaf are identical here. */
/* THE ONE PRIMARY BUTTON THAT IS NOT GREEN.
   Everywhere else on the site a primary control sits on the cream page, so
   --deep is what separates it. This one sits inside the booking bar's dark
   glass, over footage — green on a dark green panel is a button you have to
   look for, on the one control the fold exists to offer. Ivory inverts it
   and makes it the brightest thing in the bar.

   Hover still takes the falling-leaf fill like every other button, so the
   gesture is the same one the rest of the site teaches; only the resting
   state differs. */
.heroBookCta{margin-left:var(--book-x);flex:none}
.oButton.is-primary.heroBookCta{
  background:var(--ivory);border-color:var(--ivory);color:var(--deep);
}
.oButton.is-primary.heroBookCta:hover,
.oButton.is-primary.heroBookCta:focus-visible{
  background:var(--forest);border-color:var(--forest);color:var(--white);
}
.heroBookFoot{
  display:flex;align-items:center;justify-content:space-between;
  /* the same inset as the fields above it — 40px here was what pushed the
     foot's links out of line with the row they sit under */
  padding:9px var(--book-x);border-top:1px solid rgba(245,241,232,.14);
  /* same trap as the label — the foot inherited 32px leading too */
  font-family:var(--ui);font-size:12px;line-height:1.3;color:rgba(245,241,232,.8);
}
.heroBookFoot a{transition:color 400ms var(--ease-expo)}
.heroBookFoot a:hover{color:var(--orange)}

@media (max-width:1023px){
  .heroBook{--book-x:16px}
  .heroBookFields{grid-template-columns:repeat(3,minmax(0,1fr)) auto}
  .heroBookField:nth-child(4){display:none}      /* promo code */
  /* the CTA keeps the shared .oButton padding — its grid column is `auto`,
     so it simply takes the width it needs rather than being pinched */
}
@media (max-width:767px){
  /* THE WHOLE PANE GOES ON A PHONE. It had already been reduced to a single
     button here — every field hidden, the foot links hidden — so what was left
     was a glass bar across the bottom of the fold carrying one Book now, which
     the header already offers two centimetres above it. Two identical actions
     on one screen is not a choice, it is a duplicate.

     Hidden rather than deleted: the markup stays, so the desktop pane is
     untouched, and heroFrame() in main.js already guards for the element being
     absent from the layout. */
  .heroBook{display:none}
}
/* ---------- 11b. THE PARK, DRAWN (#parkFrieze) ----------
   A painted Ranthambore panorama, edge to edge, between the payoff and the
   rooms film.

   NO GUTTER and NO CROP, for the same reason the footer frieze has neither:
   the horizon it draws IS the width of the page, and the art is one
   continuous scene. Cropping it to a fixed band height would cut the fort
   off its ridge at one width and float it in empty sky at another, so the
   picture keeps its own 2.83:1 ratio and the section is whatever height that
   comes to — about 508px at 1440, 138px at 390.

   Shipped as WebP, which is the one place on the site that pays off: the art
   holds 435,000 distinct colours, and an indexed PNG has 256. Quantising it
   banded the sky and the mountain wash (mean error 3.14/2.06/4.18 per
   channel) for 300KB; WebP at q92 costs 335KB and errs 1.24/0.96/2.33. More
   colour for 35KB, on the only hand-painted image on the page.

   It carries its own transparency, so it needs no plate behind it; the page
   ivory IS the sky and the paper. --band-y beneath it separates the feathered
   reflection from the hard top edge of the rooms footage. Nothing above it:
   #hPay already pays --band-y below itself, and two of them stacked leave a
   dead strip of ivory.

   The reveal is a plain fade. The site's other bands come in on .scaled,
   which starts at scale(1.14) — on a full-bleed element that is 14% of the
   viewport hanging off both sides, and it would drive a horizontal scrollbar
   for the length of the transition unless the section clipped it. A fade
   costs nothing and cannot overflow. */
#parkFrieze{
  position:relative;z-index:1;
  padding:0 0 var(--band-y);
  background:transparent;   /* the flock drifts behind the drawing */
}
/* Desktop only: the frieze feathers out into ivory at its own foot, so the
   full band figure below it read as a longer gap than every other join on the
   page. 80 closes it without touching --band-y, which governs the whole site.
   Tablet and phone keep the token (80 and 30) and are unchanged. */
@media (min-width:1024px){
  #parkFrieze{padding-bottom:80px}
}
.frieze{position:relative;width:100%;line-height:0}
/* Held at 70% so the painting sits back into the ivory rather than shouting
   between two photographic bands — the greens in particular were reading
   louder than anything else above the fold.

   The opacity belongs to the IMAGE, not to .frieze. The wrapper is the thing
   the reveal drives: it carries .no_opacity and Locomotive takes it 0 -> 1
   on .show, so an opacity here would be overwritten the moment the band
   came into view. Split across the two elements they multiply — the wrapper
   fades in, the picture stays at .7. */
.frieze img{display:block;width:100%;height:auto;opacity:.7}

/* PHONES CROP, because the alternative is worse. 2.83:1 across 390px is a
   138px strip, and at that height the fort, the tiger and the jeep are four
   or five pixels tall each — the whole scene is present and none of it is
   legible. Given a floor height the picture fills the band instead and shows
   its middle: the fort on its ridge, the chhatri, the near bank. Losing the
   ends of a drawing is a smaller loss than keeping all of it at a size
   nobody can read. The floor is in vw so the crop eases off as the screen
   widens, and by ~640px the full scene is back. */
@media (max-width:767px){
  .frieze img{height:clamp(150px,46vw,220px);object-fit:cover;object-position:center}
}

/* ---------- 12. PAYOFF (#hPay) — the overlapping collage ----------
   Mirrors the reference: an oversized pale wordmark with a smaller, darker
   half overlapping its baseline; a framed image to the right; a band that
   bleeds off the left edge; then the closing line running wide. */
/* the hero photo sits above and the green Experiences band below, so both
   of this section's edges are colour changes — 100px each */
/* no top padding — the payoff catches the hero frame as it lands. The bottom
   is the ordinary band figure, so payoff→rooms reads like every other join;
   at --sec-band it was 100px against the 50 everything else paid. */
#hPay{position:relative;z-index:1;padding:0 0 var(--band-y)}
#hPayStage{position:relative;width:100%}

.payRow{position:relative;width:100%}
.payLine{display:block;overflow:hidden;white-space:nowrap}
.payoff{margin:0}
.payoff .word,.payoff .ltr{display:inline-block}

/* the pale ghost wordmarks of the collage (row 2 / row 3) */
.payBig{
  font-family:var(--display);font-weight:300;
  font-size:var(--display-xl);line-height:.92;
  color:var(--tint-on-light);letter-spacing:.02em;
}
/* the smaller half, overlapping the big one's baseline */
.paySub{
  font-family:var(--display);font-weight:300;
  font-size:var(--display-sub);line-height:1;
  color:var(--tint-on-light-2);letter-spacing:.06em;
}
/* the closing line, wide and a shade stronger */

/* row 1 — the property name at the left, the landed frame at the right */
.payRow--1{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,46%);
  align-items:start;gap:clamp(20px,3vw,54px);
  /* clears the bar — the nav links sit at the same height as the eyebrow, so
     any overlap here lands the cursive straight on top of STAY / CONTACT US */
  padding:calc(var(--header-h) + var(--sec-y)) var(--gutter) 0;
  max-width:calc(var(--maxw) + var(--gutter) * 2);margin:0 auto;
}
.payRow--1 .payoff{position:relative;padding-bottom:clamp(2px,.3vw,6px)}
/* the h1 is the property name: one size off the scale, full opacity, in the
   green/charcoal mix. FIND and TRUE STILLNESS keep the pale ghost treatment. */
.payRow--1 .payBig,
.payRow--1 .paySub{
  display:block;font-size:var(--h1);line-height:1.14;
  letter-spacing:.03em;color:var(--heading);
}
.payRow--1 .payoff .payLine{padding-bottom:.06em}

/* THE HEADLINE WRAPS AT EVERY WIDTH.
   .payLine is white-space:nowrap with overflow:hidden — the nowrap holds the
   ghost wordmarks in rows 2 and 3 on one line, and the overflow is the mask
   the words rise out of. Together they mean a line that outgrows its column
   is not just unwrapped but CUT, with no ellipsis and no scrollbar to say so.

   Wrapping used to be allowed only below 1100px, on the reasoning that the
   column is narrowest there. That was true of the headline it was written
   for; a longer one overflows on a wide desktop too, which is a worse place
   to lose the end of a sentence. The permission is unconditional now, so the
   line can never be clipped by a copy change.

   It stays scoped to .payRow--1 — rows 2 and 3 keep the nowrap they need.
   The mask is unaffected: .payLine is display:block and sizes to its content,
   so it grows to two lines and translateY(101%) still hides the lot.

   text-wrap:balance splits it evenly rather than leaving one word stranded. */
.payRow--1 .payoff .payLine{white-space:normal}
.payRow--1 .payoff .word{text-wrap:balance}
#hPaySlot{
  width:100%;aspect-ratio:2.4/1;pointer-events:none;
  align-self:start;margin-top:clamp(30px,4vw,72px);
}

/* the cursive eyebrow that opens the lockup — Allura needs a good deal more
   size than an italic serif to read at the same optical weight */
/* An eyebrow's job is to introduce the heading, not to compete with it.
   Allura runs small for its point size, so it still needs a bigger figure
   than an italic serif would — but kept to roughly 2/3 of the h1 so the
   hierarchy is unmistakable, and given real air rather than an overlap.
   A connected script tangled into cap-height letterforms just reads as a
   collision; the charm comes from the contrast, which needs space to land. */
.payEyebrow,.secEyebrow{
  display:block;font-family:var(--accent);font-weight:400;
  font-size:clamp(24px,2.1vw,32px);
  line-height:1.15;                 /* Allura's descenders need the room */
  margin-bottom:clamp(4px,.7vw,10px);
  /* the capital F opens with a swash that hangs left of its stem — a small
     optical correction, not a visible indent, keeps it flush with the h1 */
  margin-left:-.05em;
  color:var(--botanical);
}
/* Allura's descenders drop well below the baseline — give them room to hang
   into the heading without the reveal mask cropping them */
.payHead .payoff{overflow:visible}

/* the copy and the action that close the lockup. The hero's lead is the page's
   primary paragraph and keeps the full 16/32; a SECTION lead is a caption to
   its heading, so it takes one step down — the same step in every band, rather
   than 16/32 in one and 15/1.7 in the next. */
.payLead,.secLead{
  margin-top:clamp(10px,1.1vw,16px);
  max-width:50ch;color:var(--text);
  font-size:var(--body);line-height:var(--body-lh);
}
.secLead{max-width:58ch;font-size:var(--body-sm);line-height:1.65}
.payActions,.secActions{
  margin-top:clamp(16px,1.8vw,26px);
  display:flex;align-items:center;gap:clamp(20px,2.4vw,36px);flex-wrap:wrap;
}

/* ---------- 12b. SECTION LOCKUP — one header shape for every band ----------
   eyebrow (Allura) → title (Cinzel at --h1) → lead (16/32, 50ch) → pill.
   The payoff lockup above is the reference; every section head below reuses
   the same faces, sizes and gaps, so no two of them read differently.
   Add .on-dark to the block when the section paints a green ground. */
.secHead{margin-bottom:var(--head-gap)}

/* ---- the split head: eyebrow + title on the left, the copy and its action
   on the right. Centred against each other rather than bottom-aligned — the
   two halves are rarely the same height (a one-line title against three lines
   of copy and a button), and pinning them to a shared baseline just pushes
   the shorter one down and opens a hole above it. ---- */
/* TOP-aligned, not centred. The right half is always the taller of the two
   (three lines of copy plus a button against an eyebrow and a title), so
   centring made it overhang the left at BOTH ends: the copy started above
   the eyebrow, and the head's height was set by a column that ran past the
   title, leaving a dead band underneath it. One shared top edge fixes both. */
/* The columns are the SAME columns the content below uses — 1fr / 1.05fr on
   the same gap. At 42% the right half started 5% further in than the list it
   introduced, so the copy and its button hung off an axis nothing else shared.
   A head that splits must split where its content splits. */
.secHead.is-split{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  gap:var(--col-gap);
  align-items:start;
}
.secHead.is-split .secHeadText{min-width:0}
.secHead.is-split .secHeadMeta{min-width:0}
/* size and measure now live on .secLead itself — the only thing the split
   changes is that the copy starts at the head's top edge, not below a title */
.secHead.is-split .secLead{margin-top:0}
@media (max-width:1023px){
  /* stacked, the two halves read as one column again */
  .secHead.is-split{grid-template-columns:minmax(0,1fr);gap:20px;align-items:start}
}
.secTitle{
  display:block;margin:0;
  font-family:var(--display);font-weight:300;
  font-size:var(--h1);line-height:1.14;letter-spacing:.03em;
  color:var(--heading);
}
/* the mask each line rises out of — Allura's descenders in the eyebrow hang
   above it, so the head itself must not clip */
.secTitle .line{display:block;overflow:hidden;padding-bottom:.06em}
.secHead .payoff,.secHead .secTitle{overflow:visible}

/* on the green bands the same lockup, restated in the dark palette */
/* On a dark ground the whole lockup reads white at full strength — the sand
   eyebrow and the 66%-alpha copy were being lost against the photograph
   behind the guest-services band. The `p` rule above is more specific than
   the element itself, so the lead has to be named to win. */
.secHead.on-dark .secEyebrow,
.secHead.on-dark .secTitle,
.secHead.on-dark .secLead,
.secHead.on-dark p{color:var(--white)}

/* everything after the payoff must paint over the pinned hero */
#hFacilities,#weddings,#rooms,#gallery,#voices,#clocks,footer{
  position:relative;z-index:3;
}

/* ---------- 13. THE CURSIVE ACCENT ----------
   Cinzel has no italic, so the accent face carries it where an accent is
   wanted. It is NOT wanted in headings.

   Page titles used to set their second word in it — "Our *Story*", "Rooms &
   *Villas*", "Dining & *Drinks*" — which gave every inner page a two-faced,
   two-coloured heading while the homepage's own h1 is one face, one colour.
   The inner pages were the odd ones out, so they now read as the homepage
   does and the spans are gone from their markup.

   The rule below is a GUARD, not a style. These pages are regenerated by
   tooling that puts the spans back, and unwrapping them by hand only holds
   until the next rebuild — so a heading that finds itself carrying .cAlt
   again renders in the heading's own face rather than reverting to cursive
   orange. Anywhere that is not a heading, the accent still works. */
.cAlt{
  font-family:var(--accent);color:var(--orange);
  font-size:1.28em;line-height:1.15;letter-spacing:.01em;text-transform:none;
  /* Allura's descenders sit well below the baseline and the reveal mask
     clips at the line box — pad the child so they are inside it */
  padding-bottom:.22em;
}
:is(h1,h2,h3,h4,.pgTitle,.secTitle,.svcTitle,.payoff) .cAlt{
  font-family:inherit;color:inherit;font-size:inherit;line-height:inherit;
  letter-spacing:inherit;text-transform:inherit;padding-bottom:0;
}

/* ---------- 16. GUEST SERVICES — horizontal scroller ---------- */
/* a 260vh stage; the inner panel is held against the viewport by main.js
   (position:sticky cannot be used — Locomotive transforms the container) */
#hFacilities{background:var(--deep);color:var(--on-dark);overflow:hidden;height:260vh}
#hFacilitiesSticky{
  position:absolute;top:0;left:0;width:100%;
  /* The bar's height is paid on BOTH sides. It used to be top-only, which
     sounds harmless under justify-content:center but is not: the padding
     shrinks the content box from one end, so the centre of that box sits
     half the bar's height below the panel's real centre — a ~74px gap above
     the lockup against ~37px under the rail. Equal padding, equal gaps. */
  height:calc(var(--vh)*100);
  padding-top:var(--header-h-solid);padding-bottom:var(--header-h-solid);
  display:flex;flex-direction:column;justify-content:center;
  overflow:hidden;will-change:transform;
}
/* full-bleed photographic ground, washed to the brand green */
#hFacilitiesBg{position:absolute;inset:0;z-index:0;overflow:hidden}
#hFacilitiesBg img{width:100%;height:100%;object-fit:cover;transform:scale(1.06)}
/* A WARM NEUTRAL SHADOW, NOT A GREEN ONE.
   This used to be --deep over --forest, and green was the wrong instinct
   twice over. The photographs underneath are graded warm — --photo-tone is
   sepia(.34), a gold cast — so a saturated green wash pulls against it and
   the two average out to the mud that was showing. And the canopy in the
   picture is ALREADY green: tinting green over green flattens the one thing
   the photograph was contributing.

   Built on --earth #403C31 instead, dropped to #201C15 at the ends. It reads
   as shade rather than as a colour, so the canopy keeps its own greens, the
   warm grade survives, and the band finally belongs to the same page as the
   ivory and the sand. Darkest at top and bottom where the lockup and the
   frame captions sit, lightest across the middle where the picture is.

   DEPTH IS A LUMINANCE GAP, and it has to be a big one. Measured on the
   rendered band — mean luminance of the frames against mean luminance of the
   gaps between them, which are pure background — the wash needed to take the
   ground to roughly a QUARTER of the frames before the two stopped reading
   as one surface:

     .58/.44/.66   1.55x   frames and forest merge (what this was)
     .70/.56/.76   1.92x   better, trees still competing
     .78/.66/.84   2.29x   frames read, ground still busy
     .86/.76/.90   4.10x   frames sit clearly in front      <- here

   The photograph is not wasted at this strength: the trunks and the canopy
   are still legible, they just sit back where a background belongs. Losing
   them entirely to a flat panel would be the other failure, and .90 is
   short of it. */
/* Deep enough that the white lockup and the cards' own captions never have to
   compete with whatever the canopy is doing behind them. The middle stop is
   the one that matters: it sits behind the rail, and at .76 a bright gap in
   the leaves came through hard enough to eat a caption. */
#hFacilitiesBg::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(26,22,16,.90),rgba(40,36,28,.84) 45%,rgba(26,22,16,.93));
}

/* the lockup rides in the flow above the rail, so it takes the same shape
   here as it does on every other band rather than floating off to one side */
#hFacilitiesHead{
  padding-left:var(--gutter);padding-right:var(--gutter);
  position:relative;z-index:3;width:100%;
}
#hFacilitiesHead .inner{text-align:left}
/* the head's own margin is spent by the rail below it instead, so the pinned
   panel keeps one gap rather than two stacked */
#hFacilitiesHead .secHead{margin-bottom:0}
/* the rail: alternating tall and wide frames at staggered heights.
   Both are sized from the VIEWPORT HEIGHT, because the head above them and
   the rail together have to clear one pinned screen — width is not what is
   scarce inside this section. */
#hFacilitiesScroller{
  position:relative;z-index:2;
  display:flex;align-items:center;gap:40px;
  padding:0 var(--gutter);width:max-content;will-change:transform;
  margin-top:var(--head-gap);   /* the one head→content gap, as everywhere */
}
.hFacilitiesItem{position:relative;display:block;cursor:pointer;flex:0 0 auto}
/* the phone slider's counter and dots — the strip is not a slider above 767px,
   it is driven by the page scroll, so there is nothing there to indicate */
.facSlide{display:none}
.facFrame{display:block;overflow:hidden;position:relative;z-index:2}
.hFacilitiesItem img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1600ms var(--ease-expo);
}
.hFacilitiesItem:hover img{transform:scale(1.06)}
.hFacilitiesItem.is-tall .facFrame{width:auto;height:clamp(330px,64vh,720px);aspect-ratio:1/1.55}
.hFacilitiesItem.is-wide .facFrame{width:auto;height:clamp(215px,40vh,455px);aspect-ratio:1.62/1}
.hFacilitiesItem.is-low{margin-top:clamp(22px,4vh,58px)}

/* on a short screen the lockup and the rail compete for the same pinned
   panel, and the rail is what gives way */
@media (min-width:768px) and (max-height:760px){
  .hFacilitiesItem.is-tall .facFrame{height:clamp(280px,54vh,500px)}
  .hFacilitiesItem.is-wide .facFrame{height:clamp(180px,34vh,320px)}
  #hFacilitiesHead .secTitle{font-size:calc(var(--h1) * .86)}
  #hFacilitiesHead .secLead{max-width:46ch}
}


/* The giant label — vertical beside a tall frame, flat under a wide one, and
   drawn OVER the photograph so a long word is never swallowed by the frame it
   belongs to.

   Full ivory, and NO screen blend. The blend was what kept these faint: screen
   lightens toward whatever is behind it, so a label crossing a bright part of
   a photograph washed out to nothing while the same label over the dark green
   read fine — the weight changed word by word. A flat colour with a soft
   shadow behind it holds at the same strength over both. */
.facGhost{
  position:absolute;z-index:3;pointer-events:none;white-space:nowrap;
  font-family:var(--display);font-weight:300;font-size:var(--display-vert);
  line-height:1;color:var(--ivory);text-transform:uppercase;letter-spacing:.06em;
  text-shadow:0 2px 18px rgba(9,22,15,.55), 0 1px 3px rgba(9,22,15,.35);
}
/* ONE SIZE FOR BOTH ORIENTATIONS, at the midpoint of the two it replaces.
   The upright labels were sized from viewport HEIGHT (4.4vh, because a
   rotated label runs down the frame's height) and the flat ones from WIDTH
   (3.2vw). Each was right about its own axis and wrong about the set: CAMEL
   CART came out visibly larger than CULTURE beside it, so the rail read as
   two different labellings rather than one.

   `2.1vw + 1.4vh` carries both axes in a single value, and the coefficients
   are fitted to the midpoint of the old pair rather than guessed — measured
   against them at four widths:

       1920   vertical 46.0  flat 52.0   midpoint 49.0   this 49.0 (capped)
       1536   vertical 38.0  flat 49.2   midpoint 43.6   this 44.4
       1440   vertical 39.6  flat 46.1   midpoint 42.8   this 42.8
       1280   vertical 35.2  flat 41.0   midpoint 38.1   this 38.1

   Because it still carries a vh term, a short wide window shrinks it — which
   is what stopped a two-word upright label running off the top of its frame
   and into the section head in the first place. */
.facGhost{font-size:clamp(22px, 2.1vw + 1.4vh, 49px)}
.facGhost:not(.is-flat){
  writing-mode:vertical-rl;transform:rotate(180deg);
  left:-.30em;bottom:-.10em;
  /* the frame is the ceiling: a label can never be taller than what it labels */
  max-height:100%;
}
/* the flat ones run across the frame's width, so vw is right for them */
.facGhost.is-flat{left:-.18em;bottom:-.30em;max-width:100%}

#hFacilitiesTarget{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}

/* ---------- 17. WEDDINGS (#weddings) ----------
   Still built on the .diningGrid pattern — a held title column beside a
   column of cards. The class names kept the old name; the section did not. */
#weddings{position:relative}
/* the title column carries a full lockup now, so it takes an even share of
   the grid — at .85fr the section title wrapped where no other one does */
.diningGrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:var(--col-gap);align-items:start}
/* held against the viewport by main.js while the venue column scrolls past —
   position:sticky cannot be used, Locomotive transforms the container */
#hDiningSticky{align-self:start;will-change:transform;margin-bottom:0}
#hDiningScrollWrap{position:relative}
.hDiningItem{
  position:relative;display:block;overflow:hidden;cursor:pointer;
  aspect-ratio:1.45/1;background:var(--sand);
}
.hDiningItem+.hDiningItem{margin-top:26px}
.hDiningItem img{width:100%;height:100%;object-fit:cover;transition:transform 1800ms var(--ease-expo)}
.hDiningItem:hover img{transform:scale(1.06)}
/* the same warm shadow the experiences band uses, for the same reason: the
   card only needs its lower third darkened so the name and the note read,
   and green did that by recolouring the picture rather than by shading it */
.hDiningItem::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(32,28,21,0) 42%,rgba(32,28,21,.82) 100%);
}
.hDiningItemContainer{display:block;position:absolute;left:30px;right:30px;bottom:28px;z-index:2;color:var(--ivory)}
.hDiningItemContainer .name{display:block;font-family:var(--display);font-size:var(--h3);font-weight:300;line-height:1.12}
.hDiningItemContainer .note{display:block;font-size:var(--body-sm);color:var(--on-dark-muted);margin-top:6px}

/* ==========================================================================
   17c. GALLERY — "THE CLOUD"
   Eight frames stacked dead centre and closed to nothing, which expand into
   a ring as the section arrives.

   The mechanic is one progress value doing two jobs at once:
     transform  : translate3d(x * p, y * p, 0)   — the frame travels out
     clip-path  : inset((1 - p) * 50%)           — the frame opens up
   At p = 0 every frame is at the origin, clipped to a point, so the title is
   alone on the page. At p = 1 they rest on the ring with the clip fully open.
   Because the clip closes toward the frame's own centre, a picture appears to
   GROW out of the words rather than slide out from under them — which is the
   whole reason it is a clip and not a scale.

   EXACTLY ONE SCREEN. It used to be a 230vh stage with the panel pinned for
   the extra 130vh, which cost two and a bit screens of scrolling for one
   composition. The expansion is now driven by the section's own approach —
   it completes as the section reaches the top of the viewport — so the whole
   thing occupies a single screen and no pin is needed.
   ========================================================================== */
.cloud{
  position:relative;
  height:calc(var(--vh) * 100);
  overflow:hidden;
}

/* ---- the wash ----
   A single torn-edge block of pale sage laid behind the ring, INSET from the
   section on all four sides so the deckle edge is visible the whole way
   round — it reads as a sheet of hand-torn paper placed on the page, not as
   a band bleeding off it.

   It replaces a three-layer mask (brush edge top, brush edge bottom, plain
   fill between). Two things were wrong with that. The repeating scallop of
   the SVG path read as generic stock waviness rather than a made mark, and
   the plain fill was sized `calc(100% - var(--brush-h) * 2)`: at fractional
   section heights that arithmetic lands a sliver off the edge shapes, and
   the ivory behind showed through as a hairline seam straight across the
   page. One image has no seams to misalign.

   The artwork carries its own colour (sage at ~31% alpha, which composites
   over the ivory to about --wash), so it goes in as a background rather than
   a mask — a mask would multiply that 31% against the token and wash out.
   `100% 100%` stretches it to the box: the source is 1672x1031 (1.62) and
   the box is close to the viewport ratio, so the edge keeps its weight. */
.cloud__sheet{
  position:absolute;z-index:0;pointer-events:none;
  left:clamp(18px,3vw,56px);right:clamp(18px,3vw,56px);
  /* centred rather than inset top and bottom, because main.js overrides the
     height on portrait screens: the ring there is capped in elongation, so the
     sheet is pulled in to the ring instead of standing half empty. Unset, this
     is exactly the old `inset` shorthand. */
  top:50%;transform:translateY(-50%);
  height:var(--cloud-sheet-h, calc(100% - clamp(20px,3.2vh,52px) * 2));
  background:url("../assets/images/wash-block.png") center / 100% 100% no-repeat;
  /* the artwork is already sage at ~31% alpha; this takes another fifth off
     it so the sheet sits under the photographs rather than beside them */
  opacity:.8;
}

.cloud__container{
  position:relative;z-index:1;width:100%;height:100%;
  display:grid;place-items:center;
  overflow:hidden;
}

/* ---- the words, dead centre and above the frames ---- */
.cloud__content{
  position:relative;z-index:3;grid-area:1 / 1;
  text-align:center;padding:0 var(--gutter);
  display:flex;flex-direction:column;align-items:center;
  /* nothing here is clickable any more, so the whole lockup lets the pointer
     through to the frames behind it */
  pointer-events:none;
}
.cloud__content .secEyebrow{margin-left:0;margin-bottom:clamp(6px,1vh,12px)}
.cloud__title{
  font-family:var(--display);font-weight:400;
  font-size:var(--h1);line-height:1.14;letter-spacing:.03em;
  color:var(--heading);
  /* --cloud-corridor is the clear width main.js measured between the two side
     frames. On a wide screen the ch count is far the smaller of the two and
     governs as it always did; on a narrow one the corridor takes over and the
     words break rather than run under a photograph. */
  max-width:min(16ch, var(--cloud-corridor, 100%));
}

/* ---- the frames ---- */
.cloud__medias{
  grid-area:1 / 1;position:relative;z-index:1;
  width:100%;height:100%;
  display:grid;place-items:center;
}
.cloud__expander{
  grid-area:1 / 1;
  /* --cloud-fit is written by main.js: the ring is drawn at the breakpoint's
     own scale, then shrunk by one factor until it clears the torn sheet on
     every side. Frame width and ring radius take the SAME factor, so the
     composition keeps its proportions — it just gets smaller. */
  width:calc(var(--cloud-w) * var(--cloud-fit, 1));
  /* the resting state, before main.js has run a frame */
  transform:translate3d(0,0,0);
  clip-path:inset(50%);
  will-change:transform,clip-path;
}
/* Square, every one of them. On height:auto each frame took its own photo's
   proportions — the fort stood tall, the sunset sat short — so the ring read
   as a pile of odd offcuts rather than a set of plates. The frame is the
   constant now and the picture crops into it. */
.cloud__media{overflow:hidden;background:var(--sand);aspect-ratio:1/1}
.cloud__media img{
  width:100%;height:100%;object-fit:cover;display:block;
  /* a slight counter-scale so the picture settles as the clip opens */
  transform:scale(1.06);
  transition:transform 1200ms var(--ease-expo);
}
.cloud.is-open .cloud__media img{transform:scale(1)}

/* FRAME SIZE ONLY. The ring's radius is no longer set here — main.js
   inscribes the ellipse in the torn sheet, one axis at a time, and runs an
   overlap pass that writes --cloud-fit if the frames need trimming to clear
   each other. There used to be a --cloud-scale ladder beside these widths;
   it guessed at a radius the sheet now supplies by measurement, and the two
   disagreed at every breakpoint that was not exactly 1440. */
.cloud{--cloud-w:clamp(112px,11.5vw,165px)}
@media (max-width:1023px){
  .cloud{--cloud-w:clamp(94px,13vw,130px)}
  .cloud__title{font-size:calc(var(--h1) * .92)}
}
@media (max-width:767px){
  /* ---- THE PHONE RING ----
     Same composition as the desktop: eight frames around a centred lockup on
     a torn sheet. Only three numbers change, and each one is set by the same
     arithmetic the desktop uses rather than by eye.

     Frame width. The corridor left for the title between the two side frames
     is `sheet width - 2 * margin - 2 * frame`. At 390 the sheet is 350 wide,
     so a 78px frame leaves 142px of clear middle — enough for the longest
     word in the heading at this size with room to spare. Every px added to
     the frame takes two off the title. 20vw keeps that relationship as the
     screen grows; the 104px ceiling meets the tablet rule above at 768.

     The ring's proportion is main.js's job, not a rule here: it caps the
     vertical radius against the horizontal one and pulls the sheet in to
     match, so the portrait ellipse never stretches into a needle. */
  .cloud{--cloud-w:clamp(72px,20vw,104px)}
  /* all eight frames. The pair that used to be hidden here was hidden because
     the ellipse had nowhere to put a diagonal; the capped ring does. */
  .cloud__title{font-size:calc(var(--h1) * .8)}
  /* the sheet pulls in to the 20px phone gutter, so the torn edge still has
     ivory to sit against on the left and right */
  .cloud__sheet{
    left:20px;right:20px;
    height:var(--cloud-sheet-h, calc(100% - clamp(16px,2.4vh,32px) * 2));
  }
}

/* no JS: show the ring at rest so the section still reads as a composition */
@media (prefers-reduced-motion:reduce){
  .cloud__expander{clip-path:inset(0%) !important}
}

/* ==========================================================================
   17d. TESTIMONIALS — "VOICES"
   One guest at a time, centred on nothing but ivory, flanked by the same
   hairline-ring arrows the rooms rail uses. Stars, the quote, the name —
   nothing else. The outgoing quote fades while the incoming one rises out
   of its own mask, so a change reads as somebody else starting to speak.
   ========================================================================== */
/* the arrows sit OUTSIDE the quote, one either side, and the whole block is
   centred — the one band on the page that is, because a quotation is a single
   voice rather than a column of content */
/* The middle column is 1fr, so without a ceiling it swallowed every spare
   pixel of the section and shoved the arrows out to the gutters — a 200px
   void either side of a quote that is only ~52ch wide. Capping the block and
   centring it puts the arrows back alongside the words they page through. */
.voices{
  position:relative;
  max-width:58rem;margin-inline:auto;
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:clamp(14px,2.4vw,48px);
}
/* Every quote occupies the SAME grid cell, so they overlap instead of
   following one another down the page. That is what removes the jerk: with
   the quotes in normal flow, revealing the incoming one pushed it below the
   outgoing one and hiding the outgoing one then snapped it back up — the
   text visibly dropped, slid, and rose again on every change. Stacked, the
   row is simply as tall as the longest quote and nothing ever moves. */
/* Explicit placement. The grid declares three columns for arrow · quote ·
   arrow, and when the arrows were moved out to a control bar the stage fell
   into column ONE — the narrow `auto` track — which is why the quote centred
   on a different axis from the heading above it. Naming the cell means the
   quote cannot drift again whatever else joins the grid. */
.voices>.voicesArrow:first-of-type{grid-column:1;grid-row:1}
.voices>.voicesArrow:last-of-type{grid-column:3;grid-row:1}
.voicesStage{
  grid-column:2;grid-row:1;
  display:grid;grid-template-columns:minmax(0,1fr);
  position:relative;z-index:1;text-align:center;min-width:0;
}
.voicesStars{grid-row:1}

/* the rating, above the words.
   flex, not inline-flex: as a grid child an inline-flex box shrink-wraps to
   its five stars and sits wherever the column starts, and the stage's
   text-align cannot move the box itself. Filling the column is what gives
   justify-content something to centre against. */
.voicesStars{
  display:flex;justify-content:center;align-items:center;gap:6px;
  margin-bottom:clamp(18px,2.4vh,30px);
}
.voicesStars svg{
  width:17px;height:17px;fill:var(--orange);stroke:none;display:block;
}

/* all of them in row 2, column 1 — one on top of the other */
.voice{margin:0;grid-row:2;grid-column:1}
/* NOT the display face. Cinzel is a capitals-only Roman — it has no true
   lowercase, so a thirty-word quote set in it renders as an inscription
   shouting at the reader. The display face belongs on two or three words;
   a sentence somebody actually said belongs in the text face, set light
   and large, on a measure short enough to read in one sweep. */
.voice p{
  max-width:52ch;margin:0 auto;
  font-family:var(--ui);font-weight:300;
  /* six pixels down across the whole ramp — at 23px a light weight this wide
     read as a headline rather than as something somebody said */
  font-size:clamp(15px,1.18vw,17px);line-height:1.65;
  letter-spacing:.005em;color:var(--heading);
}
.voice footer{
  margin-top:clamp(20px,2.6vh,32px);
  display:flex;flex-direction:column;gap:5px;align-items:center;
}
/* the same ink as the quote it signs — in orange the attribution outshouted
   the words above it, which is backwards for a signature */
.voiceName{
  font-family:var(--accent);font-size:clamp(24px,2.2vw,34px);line-height:1.1;
  color:var(--heading);
}

/* ---- the change of voice ----
   A cross-fade and nothing else. The old keyframes carried translateY(22px)
   on the incoming quote, so the new words rose into place from below while
   the layout was already shifting underneath them — two movements at once,
   which is what read as the jerk. Opacity alone cannot move anything.
   visibility is switched instantly on the way in and held until the fade has
   finished on the way out, so a faded quote is out of the a11y tree without
   ever collapsing the row it shares. */
.voice{
  opacity:0;visibility:hidden;
  transition:opacity 700ms var(--ease-expo),visibility 0s linear 700ms;
}
.voice.is-active{
  opacity:1;visibility:visible;
  transition:opacity 700ms var(--ease-expo),visibility 0s;
}

/* a centred lockup — used only where the band itself is centred */
.secHead.is-centred{text-align:center}
.secHead.is-centred .secEyebrow{margin-left:0}
.secHead.is-centred .secLead{margin-left:auto;margin-right:auto}

/* ---- the chrome: the SAME 01 / 03, dashes and arrows the rooms rail uses.
   Centred here rather than pushed to the ends, because the band it sits under
   is centred. Everything is restated for a LIGHT ground — the glass arrows
   pick up their own overrides further down. ---- */
.voicesCtrl{
  /* its own row, spanning arrow to arrow, so the counter and dashes centre on
     the same axis as the quote above them */
  grid-column:1 / -1;grid-row:2;
  display:flex;align-items:center;justify-content:center;
  gap:clamp(16px,2vw,30px);
  margin-top:clamp(26px,3.4vh,44px);
}
.voicesCount{
  font-family:var(--display);font-size:16px;line-height:1;
  color:var(--text-muted);letter-spacing:.08em;
  font-variant-numeric:tabular-nums;flex:0 0 auto;
}
.voicesCount b{font-weight:400;color:var(--heading);font-size:23px}
.voicesCount i{font-style:normal;margin:0 6px;opacity:.45}
.voicesCount em{font-style:normal}

.voicesDots{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.voicesDot{
  width:16px;height:2px;padding:0;cursor:pointer;border:0;border-radius:0;
  background:rgba(39,39,39,.28);
  transition:width 620ms var(--ease-expo),background 500ms var(--ease-expo);
}
.voicesDot:hover{background:rgba(39,39,39,.6)}
.voicesDot.is-active{width:40px;background:var(--sage)}

/* ==========================================================================
   18. ROOMS & VILLAS (#rooms)
   One screen of footage, edge to edge, with everything else laid over it.
   The film is full-bleed; the 100px inset belongs to the CONTENT only, so
   the picture never inherits a margin it was not meant to have.

   30 / 70. The lockup on the left is the same component every other band
   uses; the rail on the right shows two and a half cards, so the third is
   visibly cut at the column's edge. That cut is the whole affordance — no
   scrollbar, no dot row, no "swipe" label. The counter and two hairline
   arrows are the only chrome.
   ========================================================================== */
#rooms{
  position:relative;height:calc(var(--vh) * 100);
  background:var(--deep);color:var(--on-dark);overflow:hidden;
  padding:0;
}

/* ---- the film ---- */
.roomsFilm{position:absolute;inset:0;z-index:0;overflow:hidden}
.roomsFilm video,
.roomsFilm img{width:100%;height:100%;object-fit:cover;display:block}
/* Readability, applied where it is actually needed and nowhere else: a
   left-weighted wash under the lockup, and a shallow floor under the rail's
   captions. A flat scrim over the whole frame would have killed the footage
   for the sake of two columns of text. */
/* Two layers doing two jobs. The angled one is still weighted to the left,
   where the lockup sits, but it no longer falls away to .12 on the right —
   the cards' own captions were being asked to hold themselves against bare
   daylight. A flat base under both puts a floor on the whole frame, so no
   part of the picture is ever bright enough to swallow what is over it. */
.roomsFilm::after{
  content:"";position:absolute;inset:0;
  background:
    linear-gradient(100deg,rgba(10,26,18,.90) 0%,rgba(10,26,18,.74) 26%,
                    rgba(10,26,18,.50) 52%,rgba(10,26,18,.40) 100%),
    linear-gradient(0deg,rgba(10,26,18,.66) 0%,rgba(10,26,18,0) 40%),
    linear-gradient(0deg,rgba(10,26,18,.30),rgba(10,26,18,.30));
}

/* ---- the stage: 30 / 70, inset from the edges by the page gutter ---- */
.roomsStage{
  position:relative;z-index:2;height:100%;
  display:grid;grid-template-columns:30% 70%;
  /* stretch, not centre: the rail then fills the 80px-inset box exactly,
     and the picture takes whatever height is left after the caption. Centred,
     the content floated in the middle and left ~200px of dead film above it */
  align-items:stretch;column-gap:clamp(30px,4vw,80px);
  padding:80px var(--gutter);
}
/* the lockup has nothing to stretch into, so it sits on the vertical centre */
.roomsIntro{align-self:center}

/* ---- 30%: the lockup ---- */
.roomsIntro{margin-bottom:0;min-width:0}
.roomsIntro .secLead{max-width:34ch}

/* ---- 70%: the rail ----
   min-height:0 matters: without it a grid child refuses to shrink below its
   content, so a card taller than the stage pushes out through the bottom of
   the section instead of being sized to fit it. */
.roomsRail{
  min-width:0;min-height:0;max-height:100%;
  display:flex;flex-direction:column;
  gap:clamp(14px,2vh,28px);
}
/* the viewport is what cuts the third card — the track runs past it */
.roomsViewport{overflow:hidden;width:100%;flex:1 1 auto;min-height:0}
.roomsTrack{
  --card-gap:clamp(16px,1.6vw,30px);
  display:flex;gap:var(--card-gap);height:100%;
  will-change:transform;
  transition:transform 980ms var(--ease-expo);
}
/* two and a half across the viewport, so the third is half-shown */
.roomCard{
  flex:0 0 calc((100% - 1.5 * var(--card-gap)) / 2.5);
  min-width:0;margin:0;
  display:flex;flex-direction:column;
  /* A GROUND. The captions were sitting straight on the footage, and a moving
     forest is the worst possible backdrop for small type — the contrast under
     any given word changes frame to frame. A low wash plus a slight blur fixes
     the type's background without turning the card into a box: no border, no
     shadow, and the picture still bleeds to the card's own edges. */
  background:rgba(9,22,15,.5);
  -webkit-backdrop-filter:blur(7px) saturate(1.05);
  backdrop-filter:blur(7px) saturate(1.05);
  transition:background 620ms var(--ease-expo);
}
.roomCard:hover{background:rgba(9,22,15,.62)}

/* the picture takes whatever height is left once the caption has been paid */
.roomCardPic{
  display:block;overflow:hidden;flex:1 1 auto;min-height:120px;
  background:rgba(245,241,232,.08);
}
.roomCardBody{padding:16px clamp(16px,1.3vw,22px) clamp(18px,2vh,24px)}
.roomCardPic img{
  width:100%;height:100%;object-fit:cover;display:block;
  transform:scale(1.04);
  transition:transform 1700ms var(--ease-expo);
}
.roomCard:hover .roomCardPic img{transform:scale(1.11)}

/* the name opens the card now that the area label is gone */
.roomCardName{
  font-family:var(--display);font-weight:400;font-size:var(--h3);
  line-height:1.18;color:var(--ivory);margin-top:0;
}
/* held to two lines. The rail has a fixed height budget, and one room whose
   description runs to three lines would push every card past the fold */
.roomCardNote{
  margin-top:8px;max-width:34ch;
  /* full-strength ivory — at .72 over a photograph the copy was doing the
     work of a caption when it is the card's only description */
  font-size:var(--body-sm);line-height:1.6;color:var(--ivory);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
  overflow:hidden;
}

/* a text CTA, not a button — the section already has one button */
.roomCardGo{
  display:inline-flex;align-items:center;gap:9px;margin-top:10px;
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--ivory);
  /* the rule belongs to the words, not to the block — 7px read as a gap
     rather than as an underline */
  padding-bottom:3px;position:relative;
  transition:color 420ms var(--ease-expo);
}
.roomCardGo::after{
  content:"";position:absolute;left:0;bottom:0;width:100%;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:right center;
  transition:transform 600ms var(--ease-expo);
}
.roomCardGo svg{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform 560ms var(--ease-expo);
}
.roomCard:hover .roomCardGo,.roomCardGo:focus-visible{color:var(--white)}
.roomCard:hover .roomCardGo::after,.roomCardGo:focus-visible::after{
  transform:scaleX(1);transform-origin:left center;
}
.roomCard:hover .roomCardGo svg{transform:translateX(5px)}

/* ---- the chrome: 01 / 04, a row of dashes, two arrows ----
   No wash of its own — the film's floor gradient already darkens this strip,
   and a second panel under the cards read as a grey band across the section. */
.roomsCtrl{
  display:flex;align-items:center;gap:clamp(16px,2vw,30px);
  flex:0 0 auto;padding:4px 2px 0;
}
.roomsCount{
  font-family:var(--display);font-size:16px;line-height:1;
  color:rgba(245,241,232,.55);letter-spacing:.08em;
  font-variant-numeric:tabular-nums;flex:0 0 auto;
}
.roomsCount b{font-weight:400;color:var(--ivory);font-size:23px}
.roomsCount i{font-style:normal;margin:0 6px;opacity:.45}
.roomsCount em{font-style:normal}

/* dashes rather than circles — they read as a strip of film, and the active
   one lengthens instead of merely changing colour, so position is legible
   from the shape alone */
.roomsDots{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.roomsDot{
  width:16px;height:2px;padding:0;cursor:pointer;border:0;
  background:rgba(245,241,232,.32);
  transition:width 620ms var(--ease-expo),background 500ms var(--ease-expo);
}
.roomsDot:hover{background:rgba(245,241,232,.7)}
.roomsDot.is-active{width:40px;background:var(--sand)}

/* ---- GLASS ARROWS ----
   Shared by the rooms rail and the guest book. A hairline ring alone was
   invisible half the time on the film: a 1px border has nothing behind it, so
   wherever the footage went pale the arrow went with it. Real glass fixes it
   without a solid chip — a translucent ground the blur pulls DOWN out of
   whatever is behind, a brighter rim, and an inner top highlight so the disc
   reads as a lens rather than a flat circle. */
/* the voices arrows are no longer wrapped — they flank the quote as direct
   grid children, so only the rooms rail still pairs them in a row */
.roomsArrows{display:flex;gap:10px;flex:0 0 auto}
.roomsArrows{margin-left:auto}
.roomsArrow,.voicesArrow{
  width:50px;height:50px;border-radius:50%;padding:0;cursor:pointer;
  display:grid;place-items:center;
  background:rgba(245,241,232,.16);
  -webkit-backdrop-filter:blur(14px) saturate(1.3) brightness(.82);
  backdrop-filter:blur(14px) saturate(1.3) brightness(.82);
  border:1px solid rgba(245,241,232,.44);
  box-shadow:0 8px 26px rgba(9,22,15,.30),
             inset 0 1px 0 rgba(255,255,255,.34);
  color:var(--ivory);
  transition:background 480ms var(--ease-expo),border-color 480ms var(--ease-expo),
             color 420ms var(--ease-expo),opacity 400ms var(--ease-expo);
}
.roomsArrow svg,.voicesArrow svg{
  width:17px;height:17px;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
  transition:transform 520ms var(--ease-expo);
}
.roomsArrow:hover:not(:disabled),
.roomsArrow:focus-visible:not(:disabled){
  background:var(--sage);border-color:var(--sage);
}
/* the guest book's glass sits on IVORY, so it tints dark instead of light —
   same construction, inverted, or the disc would vanish into the page */
.voicesArrow{
  background:rgba(39,39,39,.07);
  -webkit-backdrop-filter:blur(12px) saturate(1.1);
  backdrop-filter:blur(12px) saturate(1.1);
  border-color:rgba(47,73,52,.26);
  box-shadow:0 6px 20px rgba(15,59,39,.10),
             inset 0 1px 0 rgba(255,255,255,.65);
  color:var(--heading);
}
.voicesArrow:hover,.voicesArrow:focus-visible{
  background:var(--sage);border-color:var(--sage);color:var(--ivory);
}
.voicesArrow[data-step="1"]:hover svg{transform:translateX(3px)}
.voicesArrow[data-step="-1"]:hover svg{transform:translateX(-3px)}

.roomsArrow[data-step="1"]:hover:not(:disabled) svg{transform:translateX(3px)}
.roomsArrow[data-step="-1"]:hover:not(:disabled) svg{transform:translateX(-3px)}
/* a spent direction dims its ring rather than greying into a filled block */
.roomsArrow:disabled{opacity:.24;cursor:default}

/* ---- narrower: the split gives way before the cards do ---- */
@media (max-width:1279px){
  .roomsStage{grid-template-columns:34% 66%}
  .roomCard{flex-basis:calc((100% - 1.2 * var(--card-gap)) / 2.2)}
}
@media (max-width:1023px){
  /* stacked: the lockup above, the rail below, still one screen of film */
  #rooms{height:auto}
  .roomsStage{
    grid-template-columns:minmax(0,1fr);
    align-content:start;row-gap:clamp(30px,5vh,54px);
    padding-top:calc(var(--header-h-solid) + var(--band-y,44px));
    padding-bottom:var(--band-y,44px);
  }
  .roomsIntro .secLead{max-width:52ch}
  .roomCard{flex-basis:calc((100% - 1.5 * var(--card-gap)) / 2.5)}
  .roomsFilm::after{
    background:linear-gradient(0deg,rgba(10,26,18,.86) 0%,rgba(10,26,18,.62) 55%,rgba(10,26,18,.5) 100%);
  }
}
@media (max-width:767px){
  /* one and a half — two and a half cards on a phone is unreadable */
  .roomCard{flex-basis:calc((100% - .5 * var(--card-gap)) / 1.5)}
  .roomsDots{display:none}   /* the counter and arrows are enough on a phone */
  .roomsArrow{width:40px;height:40px}

  /* THE GUEST BOOK'S CHROME, at phone scale.
     The arrows are grid columns either side of the quote, so at 50px they
     were taking 100px of a 390px screen before the gap — the quote had about
     230px left and the discs sat on top of the words. At 34 they cost a third
     less, and the gap closes to match. The counter comes down with them: 23px
     figures under a 15px quote made the tally the largest thing in the band. */
  .voices{gap:8px}
  .voicesArrow{width:34px;height:34px}
  .voicesArrow svg{width:13px;height:13px}
  .voicesCtrl{gap:12px;margin-top:22px}
  .voicesCount{font-size:12px}
  .voicesCount b{font-size:17px}
  .voicesCount i{margin:0 4px}
  .voicesDot{width:12px}
  .voicesDot.is-active{width:26px}
}

/* ---------- 19. CLOCKS (#clocks) ---------- */
#clocks{border-top:1px solid var(--line)}
.clockGrid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:30px}
.clockItem{text-align:left}
.clockCity{
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--text-muted);display:block;margin-bottom:10px;
}
.clockTime{font-family:var(--display);font-size:34px;font-weight:300;color:var(--forest);line-height:1;
  font-variant-numeric:tabular-nums}
.clockItem.is-home .clockTime{color:var(--orange)}
.clockItem.is-home .clockCity{color:var(--forest)}

/* ==========================================================================
   20. FOOTER — the frieze and the directory
   A drawn band of Ranthambore runs the full width, and the directory sits
   under it on the page's own ivory. Line art, not a photograph: the page has
   had five photographic bands by this point, and a sixth would just be more
   of the same. An engraving reads as an endpaper instead.
   ========================================================================== */
footer{position:relative;color:var(--text);overflow:hidden}

/* ---- the frieze ----
   Drawn edge to edge with no gutter, because the horizon it draws IS the
   width of the page. The art is a transparent PNG of sage-green line work,
   so it needs no plate behind it and no mask over it. */
/* no padding of its own — the band above already pays --band-y below itself,
   and two of them stacked put 200px of blank ivory above the drawing */
.footerFrieze{position:relative;width:100%;line-height:0}
/* the drawing runs edge to edge and is NEVER cropped — it is one continuous
   scene from the park gate to the signpost, and cutting the top off it takes
   the sky, the birds and the tops of the trees with it. Full width, full
   height, its own ratio. */
.footerFrieze img{display:block;width:100%;height:auto}

/* ---- the directory ---- */
.footerCols{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.15fr) minmax(0,1.9fr);
  gap:clamp(28px,3.4vw,64px);
  padding-top:clamp(46px,5.2vh,74px);
  align-items:start;
}

/* every column opens with the same small caps label */
/* the column headings are the ONLY bold thing in the footer */
.footerColTitle{
  font-family:var(--ui);font-size:var(--caption);font-weight:700;
  letter-spacing:.18em;text-transform:uppercase;line-height:1;
  color:var(--heading);
  margin-bottom:clamp(13px,1.7vh,19px);
}

/* One size, one weight, one colour for every line of footer copy. The address
   ran 1.75 leading, the contact list 1.6 and the links 1.5, and the phone
   label was a different colour again — three columns that should have read as
   one block instead read as three different kinds of text. */
/* ONE type treatment for every word in the footer body — the address, the
   contact lines, every link, the sitemap and the signature. Same face, same
   size, same weight, same colour, full opacity. Nothing here outranks
   anything else; only the small-caps column labels sit apart. */
.footerAddress,
.footerContact li,
.footerCol a,
.footerMeta,
.footerSignature,
.sigMark,
.sigLine{
  font-family:var(--ui);
  /* a step below body copy — a directory is scanned, not read, and at the
     paragraph size it was competing with the sections above it */
  font-size:14px;font-weight:400;line-height:1.65;
  letter-spacing:0;color:var(--text);opacity:1;
}
.footerAddress{font-style:normal}

.footerContact{display:flex;flex-direction:column;gap:clamp(5px,.8vh,9px)}
.footerContact span{color:var(--text)}
.footerContact a{color:var(--text);transition:color 400ms var(--ease-expo)}
.footerContact a:hover{color:var(--orange)}

/* Quick links is itself three columns, as the reference has it */
.footerQuick{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(16px,2vw,36px);
}
.footerCol ul{display:flex;flex-direction:column;gap:clamp(5px,.7vh,8px)}
.footerCol a{transition:color 400ms var(--ease-expo)}
.footerCol a:hover,.footerCol a:focus-visible{color:var(--orange)}

/* ---- the base: FOLLOW US one side, the legal line the other ---- */
/* the two bands that made this footer feel empty were both here: 76px of
   margin above the rule and a full --band-y (100px) of dead ivory below the
   legal line. Neither was doing any work — the rule already separates the
   directory from the base, and the page ends immediately after. */
.footerBase{
  display:flex;align-items:flex-end;justify-content:space-between;
  flex-wrap:wrap;gap:18px 30px;
  margin-top:clamp(38px,4.2vh,60px);
  /* The foot pad. It was clamp(18px,2vh,26px) and the signature line sat
     almost on the bottom edge of the page — a line of type with nothing
     under it reads as cut off rather than as finished.

     Twenty more, at every screen height rather than at one: both stops go up
     by 20 AND the vh term doubles, so the middle of the range moves with the
     ends instead of staying where it was. 18→38 and 26→46, 2vh→4vh. On a
     1030px window that is 20.6px more; on a 900px one, 20; on a tall 1440px
     one, 20 again. */
  padding:clamp(30px,3.4vh,46px) 0 clamp(38px,4vh,46px);
  border-top:1px solid var(--line);
}
.footerIcons{display:flex;flex-wrap:wrap;gap:clamp(6px,.7vw,9px)}
/* the ring and the mark both in the brand green — at --line the circles were
   a pale sand hairline that read as disabled rather than as links */
.footerIcons a{
  width:40px;height:40px;border-radius:50%;flex:0 0 auto;
  display:grid;place-items:center;
  border:1px solid var(--sage);color:var(--sage);
  transition:background 460ms var(--ease-expo),border-color 460ms var(--ease-expo),
             color 460ms var(--ease-expo),transform 460ms var(--ease-expo);
}
.footerIcons svg{
  width:19px;height:19px;
  fill:none;stroke:currentColor;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
}
/* the marks that read as solid shapes rather than outlines */
.footerIcons a[aria-label="Facebook"] svg,
.footerIcons a[aria-label="LinkedIn"] svg,
.footerIcons a[aria-label="Google"] svg,
.footerIcons a[aria-label="YouTube"] svg path:last-child{fill:currentColor;stroke:none}
/* hover fills the ring it already wears rather than switching to a second
   accent — the same green, solid */
.footerIcons a:hover,.footerIcons a:focus-visible{
  background:var(--sage);border-color:var(--sage);color:var(--white);
  transform:translateY(-2px);
}
/* ---- the base row's right end: Credits · Sitemap · the signature ----
   Credits and Sitemap are colophon, not destinations — they belong beside
   the signature rather than in the directory of places a guest can go. */
.footerSign{
  display:flex;align-items:baseline;flex-wrap:wrap;
  gap:clamp(14px,1.6vw,26px);
}
.footerMeta{transition:color 400ms var(--ease-expo)}
.footerMeta:hover,.footerMeta:focus-visible{color:var(--orange)}

/* ---- the Simplotel signature ----
   The mark carries the weight and the descriptor sits behind it, so the line
   reads as one credit rather than as another row of footer links. */
/* size, weight and colour all come from the shared footer-body rule above —
   the mark no longer sets itself apart in bold or in the heading colour */
.footerSignature{
  display:inline-flex;align-items:baseline;gap:6px;flex-wrap:wrap;
  transition:color 400ms var(--ease-expo);
}
.sigLine::before{content:"— "}
.footerSignature:hover,.footerSignature:focus-visible{color:var(--orange)}
.footerSignature:hover .sigMark,
.footerSignature:hover .sigLine{color:var(--orange)}

/* ---- BACK TO TOP ----
   A disc, held to the bottom-right corner, that arrives once there is enough
   page behind you for it to be worth offering. It sits BELOW the header's
   z-index so the drawer and the overlays cover it, and above everything in
   the page, so it never lands on top of a control it would block. */
.toTop{
  position:fixed;z-index:80;
  right:clamp(16px,2.2vw,34px);bottom:clamp(16px,2.2vw,34px);
  width:clamp(46px,3.4vw,56px);height:clamp(46px,3.4vw,56px);
  border-radius:50%;border:0;padding:0;cursor:pointer;
  display:grid;place-items:center;
  background:var(--deep);color:var(--ivory);
  box-shadow:0 6px 20px rgba(9,22,15,.24);
  /* hidden AND inert until it arrives — opacity alone would leave an
     invisible 56px target sitting over the corner of the page */
  opacity:0;transform:translateY(14px) scale(.9);pointer-events:none;
  transition:opacity 460ms var(--ease-expo),
             transform 460ms var(--ease-expo),
             background 400ms var(--ease-expo);
}
.toTop.is-in{opacity:1;transform:none;pointer-events:auto}
.toTop:hover,.toTop:focus-visible{background:var(--forest)}
.toTop:focus-visible{outline:2px solid var(--orange);outline-offset:4px}
.toTop svg{
  width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
  transition:transform 420ms var(--ease-expo);
}
.toTop:hover svg{transform:translateY(-2px)}

@media (max-width:1023px){
  .footerCols{grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(30px,4vw,48px)}
  /* Quick links needs the full row once the grid halves */
  .footerCol--links{grid-column:1 / -1}
}
@media (max-width:767px){
  /* THE FRIEZE, AT A SIZE YOU CAN SEE. It is 2400x377 — 6.37:1 — so at full
     width on a 390px phone it renders 61px tall, and a scene containing a
     gate, a tiger, a fort, a temple and a jeep becomes a green smudge.

     Cropped HORIZONTALLY, never vertically. The note on the base rule is right
     that cutting the top off takes the sky, the birds and the treetops with
     it; object-fit:cover against a taller box scales the drawing up and trims
     the ENDS instead, so the full height survives and the middle of the scene
     is drawn at about 1.6x the size it was. */
  .footerFrieze img{
    height:clamp(88px,26vw,124px);
    object-fit:cover;object-position:center;
  }

  .footerCols{grid-template-columns:minmax(0,1fr);gap:34px}

  /* QUICK LINKS GOES ON A PHONE. Fifteen entries in two columns is a 300px
     block of small type, and it is a second copy of the drawer that is one
     tap away at the top of the same screen — the footer's job here is the
     address, the contacts and the social row. Desktop keeps it: there the
     three columns cost one row of a wide footer rather than half the screen.
     The rule is display:none, not removed markup, so it is one line to undo
     and the links stay in the document for a crawler. */
  .footerCol--links{display:none}

  .footerBase{
    flex-direction:column;align-items:flex-start;
    /* the back-to-top disc is fixed to this corner and was sitting on the
       Simplotel line — this is its diameter plus its inset */
    padding-bottom:calc(36px + 34px);
  }
  .footerIcons a{width:46px;height:46px}
  .footerIcons svg{width:22px;height:22px}

  /* the credit stacks instead of running as one line. At 14px "Simplotel —
     Hotel Website Design & Booking Engine" is wider than a phone, so it
     wrapped mid-phrase and left the em dash stranded at the head of the
     second line, reading as a bullet rather than as punctuation. Stacked
     deliberately, the dash has no work to do and comes off. */
  .footerSign{flex-direction:column;align-items:flex-start;gap:10px}
  .footerSignature{flex-direction:column;align-items:flex-start;gap:3px}
  .sigLine::before{content:""}
  .sigLine{font-size:12.5px;color:var(--text-muted)}
}

/* ==========================================================================
   20b. CSR — the responsibility wheel
   The ring carries a mark and a name. The sentence that explains each one
   sits OUTSIDE the wheel, on the side its pillar occupies: Environmental to
   the left, Social to the right, Governance beneath. Text set to fit a
   40-degree wedge is text written to a shape rather than to be read.
   ========================================================================== */
#csr{background:transparent}
.csrHead{max-width:64ch;margin:0 auto;text-align:center}
.csrHead .secEyebrow{margin-left:0}
.csrHead .secLead{margin-left:auto;margin-right:auto}

/* three columns: copy, wheel, copy — with Governance spanning underneath.
   The wheel column is sized off the VIEWPORT HEIGHT as well as the width, so
   a section whose whole point is one circle never costs more than a screen. */
.csrStage{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,min(74vh,780px)) minmax(0,1fr);
  gap:clamp(24px,3vw,56px);
  /* stretch, not centre: the side columns take the wheel's height so that
     space-between has a height to distribute their items across */
  align-items:stretch;
  margin-top:var(--block-gap);
}
.csrWheelWrap{grid-column:2}
.csrWheel{display:block;width:100%;height:auto}
/* no hover dimming: the wheel is not clickable, so it should not answer the pointer */
.csrHub{fill:var(--ivory);stroke:var(--line);stroke-width:1.5}
.csrMark{opacity:.9}

/* set in px against the 1000-unit viewBox, so the type scales with the
   drawing rather than with the page — a clamp here would grow the labels
   while their segments stayed put */
.csrPillar{font-family:var(--ui);font-size:24px;font-weight:600;letter-spacing:.22em}
.csrName{font-family:var(--ui);font-size:19px;font-weight:700;letter-spacing:.04em}
.csrHubName{
  font-family:var(--display);font-size:42px;font-weight:400;letter-spacing:.02em;
  fill:var(--heading);
}

/* ---- the copy beside the wheel ----
   Five on the left, four on the right, each on the side its own segment
   occupies. The left column is ranged right and the right ranged left, so
   both run up against the wheel and the block reads as one object.

   BALANCED BY CONSTRUCTION, not by counting. Nine items cannot split evenly,
   and stacking them from the top left one column a whole item taller than
   the other. Both columns are instead stretched to the wheel's own height
   and their items distributed across it, so the two sides start and finish
   together whatever the copy does. The column of four simply breathes more
   than the column of five. */
.csrSide{
  display:flex;flex-direction:column;justify-content:space-between;
  height:100%;
}
.csrSide--left{grid-column:1;text-align:right}
.csrSide--right{grid-column:3;text-align:left}
.csrItemName{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h4);line-height:1.2;letter-spacing:.02em;color:var(--heading);
}
.csrItemCopy{
  margin:7px 0 0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}

@media (max-width:1023px){
  /* nine marks around a circle stop being readable well before this width,
     and a screen reader cannot walk a circle at any width */
  .csrWheelWrap{display:none}
  .csrStage{grid-template-columns:repeat(2,minmax(0,1fr))}
  .csrSide--left,.csrSide--right{grid-column:auto;text-align:left}
  /* no wheel to match, so no height to fill — items go back to flowing */
  .csrSide{height:auto;justify-content:flex-start}
  .csrItem + .csrItem{margin-top:clamp(20px,2.6vh,32px)}
}
@media (max-width:767px){
  .csrStage{grid-template-columns:minmax(0,1fr);gap:30px}

  /* THE WHEEL COMES BACK ON A PHONE, AS A GRAPHIC.
     It is hidden from 1023 down because its labels stop being readable, and
     that is still true: they are SVG text in a 1000-unit viewBox, so at 350px
     wide the 19px names render at about 6.7px. No amount of scaling fixes it
     either — sized up enough to read, they collide with the segments and each
     other, because the ring was laid out around 19.

     So the labels go and the drawing stays. Everything they said is in the
     list underneath, in full, at reading size — which is what makes dropping
     them honest rather than lossy. What is left is the ring, the segments and
     the wordmark at its centre: a circle that carries the section's shape
     without pretending to carry its content. */
  .csrWheelWrap{
    display:block;grid-column:auto;order:-1;
    width:min(84vw,420px);margin:0 auto;
  }
  .csrPillar,.csrName{display:none}
  /* the hub is the one label big enough to survive the scale — 42 in the
     viewBox is about 15px on screen, and 64 brings it to a legible 22 */
  .csrHubName{font-size:64px}
}

/* ---------- 21. OVERLAYS ---------- */
.overlay{
  position:fixed;inset:0;z-index:120;visibility:hidden;pointer-events:none;
}
.overlay.active{visibility:visible;pointer-events:auto}
.overlayBg{position:absolute;inset:0;background:rgba(15,59,39,.92);opacity:0;
  transition:opacity 600ms var(--ease-expo)}
.overlay.show .overlayBg{opacity:1}
.overlayPanel{
  position:absolute;inset:0;display:grid;place-items:center;padding:var(--gutter);
  opacity:0;transform:translateY(26px);
  transition:opacity 700ms var(--ease-expo),transform 700ms var(--ease-expo);
}
.overlay.show .overlayPanel{opacity:1;transform:none}
.overlayCard{
  width:100%;max-width:660px;background:var(--ivory);padding:52px;position:relative;
}
.overlayCard h3{font-family:var(--display);font-size:var(--h3);font-weight:300;color:var(--forest);margin-bottom:8px}
.overlayCard .sub{color:var(--text-muted);font-size:var(--body-sm);margin-bottom:30px}
.overlayClose{
  position:absolute;right:22px;top:22px;width:40px;height:40px;border-radius:0;
  display:grid;place-items:center;cursor:pointer;border:1px solid var(--line);
  transition:background 400ms,border-color 400ms,color 400ms;
}
.overlayClose:hover{background:var(--forest);border-color:var(--forest);color:var(--ivory)}
.overlayClose svg{width:14px;height:14px;stroke:currentColor;stroke-width:1.5;fill:none}
.bookRow{display:grid;grid-template-columns:1fr 1fr 1fr;gap:18px;margin-bottom:26px}
.bookField label{
  display:block;font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--text-muted);margin-bottom:8px;
}
.bookField input,.bookField select{
  width:100%;height:52px;border:1px solid var(--line);background:var(--white);
  padding:0 14px;font-size:var(--body-sm);color:var(--text);
}
.bookField input:focus,.bookField select:focus{border-color:var(--forest)}
.bookNote{font-size:var(--caption);color:var(--text-muted);margin-top:20px;line-height:1.65}

/* ---------- 22. SHARED PICTURE HELPERS ---------- */
.covered{overflow:hidden}
.fit{width:100%;height:100%;object-fit:cover}
.picture{position:relative;overflow:hidden;background:var(--sand)}
.picture img{width:100%;height:100%;object-fit:cover}

/* ==========================================================================
   24. RESPONSIVE — designed at 1440, 1280, 1024, 768, 414, 390
   ========================================================================== */

/* --- 1280 --- */
@media (max-width:1439px){
  :root{
    --h1:42px;--h2:32px;--h3:25px;--h4:19px;
    --gutter:72px;--maxw:1200px;--header-h:70px;
  }
  #mainLogo{width:142px}
  #headerLeft{gap:30px}
}

/* --- 1024 --- */
@media (max-width:1279px){
  :root{--h1:39px;--h2:30px;--h3:24px;--maxw:100%;--gutter:56px}
  .clockGrid{grid-template-columns:repeat(3,minmax(0,1fr));gap:34px}
  /* three footer columns get tight once the split narrows */

  /* two columns from here down. Three of them, each carrying a thumbnail and
     a name as long as "Meetings & Celebrations", stop fitting well before the
     tablet breakpoint — the third column was the one that broke. */
  .navList{grid-template-rows:repeat(8,auto);max-width:min(880px, 100%)}
  #navScroller{padding-left:clamp(20px,4vw,40px);padding-right:clamp(20px,4vw,40px)}
}

/* --- tablet / 1024 down --- */
@media (max-width:1023px){
  :root{
    --h1:36px;--h2:28px;--h3:23px;--h4:18px;
    --body-lg:17px;--body:16px;--body-lh:26px;--gutter:32px;--header-h:66px;
    --sec-y:44px;
    --band-y:80px;--band-in:64px;
  }

  #mainLogo{width:136px}
  #headerLeft{gap:22px}
  #headerRight{gap:18px}

  .payRow--1{grid-template-columns:minmax(0,1fr) minmax(0,50%)}
  #hDiningSticky{position:static}
  .diningGrid{grid-template-columns:minmax(0,1fr);gap:44px}
  /* still two columns, but the thumbnail and the type both come down so the
     pair fits the tablet without the names wrapping to three lines */
  .navList{grid-template-rows:repeat(8,auto);gap:12px 24px}
  .navList a{font-size:20px;gap:12px}
  .navThumb{width:50px;height:38px}
  .hFacilitiesItem.is-tall .facFrame{height:clamp(210px,34vh,340px)}
  .hFacilitiesItem.is-wide .facFrame{height:clamp(150px,21vh,240px)}
  .bookRow{grid-template-columns:1fr 1fr}
  .overlayCard{padding:38px}
}

/* --- mobile / 768 down --- */
@media (max-width:767px){
  :root{
    --h1:32px;--h2:26px;--h3:21px;--h4:18px;
    --body-lg:17px;--body:16px;--body-sm:15px;--body-lh:26px;
    --gutter:20px;--header-h:60px;
    --sec-y:36px;
    /* 100px of dead space is a third of a phone screen — the ratio holds, the
       figures do not */
    /* 30 AND 30. Every band pays 30 at the head and 30 at the foot, so any two
       consecutive sections are 60px apart — one figure, applied everywhere,
       instead of 60 top against 50 or 100 bottom depending on the band.
       100px of dead space is a third of a phone screen; 30 is a gap you read
       as a break rather than as an intermission. */
    --band-y:30px;--band-in:30px;
    /* …and the gap BETWEEN blocks inside a page comes down with them. It was
       still 50 here, so a phone showed bands 30 apart but the blocks within a
       band 50 apart — the smaller division was the wider one, which reads as
       the page having no rhythm at all. One figure now governs both.
       This token drives twenty-four blocks: the mosaic, the card grids, the
       galleries, the spec strips, the forms, the document column and every
       closing button, so it is the only edit the mobile pass needs. */
    --block-gap:30px;
  }

  /* --- the three bands that do not take the token straight --- */
  /* .pad-band was 50/100. Both halves come to 30 here, and #weddings' own
     50px foot goes with them. */
  .pad-band,
  #weddings.pad-band{padding-top:30px;padding-bottom:30px}
  /* #services subtracts 40 from the token to allow for the last card's own
     bottom padding. At --band-y:30 that is calc(30px - 40px), a negative
     padding, which invalidates the whole shorthand and would have left the
     band with none at all. Stated outright instead: the card still pays its
     own 40 underneath, so 0 here is the 30-plus-a-bit the band wants. */
  #services{padding:30px var(--gutter) 0}

  /* ---- THE PHONE HEADER ----
     The word MENU goes. Three rules in the corner of a phone are already the
     most recognisable control on the screen, and spelling it out cost about
     70px of a 390px bar — which is why the wordmark was running under the
     booking button.

     With the label gone the wordmark can come out of the centre and sit next
     to the rules, which is also the only way it fits: centred, it had to
     share the middle with a button on one side and the burger on the other.
     It goes back into the flow with margin-right:auto doing the pushing, so
     the bar reads burger, 20px, wordmark ......... BOOK NOW. */
  .menuLabel{display:none}
  #headerLeft{gap:0}
  #mainLogo{
    position:static;transform:none;
    width:124px;margin-left:20px;margin-right:auto;
  }
  /* the entry/exit states were written against the centred translate and have
     to be restated, or the mark jumps to the middle to animate */
  #mainLogo.hidden{transform:translateY(-14px)}
  body.nav-open #mainLogo.hidden{transform:none}

  #headerRight{gap:14px}
  /* the bar's own control comes down again — at 42 it was still half the
     height of a 60px header */
  .oButton{height:36px;padding:0 18px;font-size:12px}

  /* the letterspaced wordmarks have to give some track back on a phone */
  .spaced{letter-spacing:.2em}

  /* the lockup stacks on a phone: wordmark, then the frame under it */
  .payRow--1{grid-template-columns:minmax(0,1fr);gap:22px}
  #hPaySlot{aspect-ratio:1.7/1;margin-top:6px}
  .payLead,.secLead{margin-top:12px;font-size:var(--body-sm)}

  /* one column, all fifteen: the drawer scrolls, so a single ordered run is
     easier to read than two short columns fighting for a phone's width */
  /* Names only below the tablet. Fifteen thumbnails one under another is a
     column of postage stamps down the left of a phone, and it pushes the names
     in off the edge for no gain — the list is short enough to read as words. */
  /* THE DRAWER HAS TO SCROLL ON A PHONE, and justify-content:center is what
     stopped it. Fifteen links at 20px plus the gaps come to roughly 590px,
     and with the old symmetric padding the block was taller than an 844px
     screen. A centred flex child that overflows its container overflows in
     BOTH directions, and the part above the start edge cannot be scrolled to
     — so the list was clipped at the foot with Terms & Conditions off-screen
     and no way to reach it.

     flex-start instead: the list begins at the top and overflows downward
     only, which is a scroll the browser can actually perform. The top pad
     also comes down from 40px of visible air to 16 — centring is what needed
     the head room, and the list is not centred any more. */
  #navScroller{
    justify-content:flex-start;
    padding:calc(var(--header-h) + 16px) var(--gutter) 40px;
    -webkit-overflow-scrolling:touch;
    /* the page behind must not take over once the list hits its end */
    overscroll-behavior:contain;
  }
  .navList{grid-auto-flow:row;grid-template-rows:none;gap:14px;max-width:none}
  .navList a{font-size:20px;gap:0}
  .navThumb{display:none}
  /* both edges, so the centring stays true on a phone as well. 18px, not the
     page gutter — the drawer holds nothing but a list of words and does not
     need the margin a column of body copy does. */
  #navScroller{padding:calc(var(--header-h) + 24px) 18px}
  .oButton{font-size:12px}
  /* stacked, the two halves are read one after the other — the one-screen
     lock belongs to the side-by-side split only */
  .clockGrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:26px}
  .clockTime{font-size:28px}

  #hFacilities{height:auto}
  /* the stage stops being a pinned 260vh panel here and becomes an ordinary
     coloured band, so it starts paying band padding */
  /* un-pinned here, so it becomes an ordinary filled band and pays --band-in */
  #hFacilitiesSticky{position:relative;height:auto;padding:var(--band-in) 0;transform:none !important}
  #hFacilitiesHead{position:relative;top:auto}
  #hFacilitiesHead .secHead{margin-bottom:var(--head-gap)}
  #hFacilitiesScroller{
    /* width:auto is the fix. The base rule sets width:max-content because on
       desktop the strip is not a scroller at all — main.js translates it and
       it has to be as wide as its contents for that to work. Keep max-content
       here and overflow-x:auto has nothing to do: the BOX is already as wide
       as the pictures, so the strip pushed the page sideways instead of
       scrolling within itself. At auto the box is the phone's width and the
       overflow happens inside it, which is a scroll the browser can perform. */
    width:auto;max-width:100%;
    overflow-x:auto;-webkit-overflow-scrolling:touch;align-items:flex-start;
    /* Was clamp(46px,14vw,80px) — on a 390px phone that is 55px of empty
       page between two pictures, so the next one barely peeks in and the
       strip reads as one cropped image rather than as a row. */
    gap:clamp(18px,5vw,30px);
    /* The upright label sits OUTSIDE its frame by .3em, which at this type
       size is about 13px — so a 20px gutter left it 7px from the screen
       edge, which is what looked clipped. The padding carries the label's
       overhang as well as the inset, so the LABEL clears 20px. */
    padding-left:34px;padding-right:var(--gutter);
    scrollbar-width:none;transform:none !important;margin-top:0;
    /* the page behind should not start moving when the strip runs out */
    overscroll-behavior-x:contain;
    /* each picture comes to rest at the left inset rather than mid-swipe */
    scroll-snap-type:x mandatory;scroll-padding-left:34px;
  }
  .hFacilitiesItem{scroll-snap-align:start}
  #hFacilitiesScroller::-webkit-scrollbar{display:none}
  /* the band is no longer pinned to one screen here, so the frames go back
     to being sized by width */
  .hFacilitiesItem.is-tall .facFrame{width:64vw;height:auto;aspect-ratio:1/1.4}
  .hFacilitiesItem.is-wide .facFrame{width:76vw;height:auto;aspect-ratio:1.5/1}
  .hFacilitiesItem.is-low{margin-top:52px}
  .facGhost{font-size:clamp(30px,9vw,52px)}

  /* ---- the slider's own indicator, phone only ----
     Borrowed wholesale from the room gallery's counter and dots, because a
     reader who has met one on this site should not have to learn a second. */
  .facSlide{
    /* POSITIONED, and that is not decoration. #hFacilitiesBg is absolute with
       z-index:0, and a positioned z-index:0 element paints ABOVE the inline
       content of static siblings — so with .facSlide left static the
       photograph and its scrim painted straight over the counter and the
       dots. Everything computed correctly and nothing was on screen. The
       scroller above carries z-index:2 for the same reason. */
    position:relative;z-index:2;
    display:flex;align-items:center;justify-content:center;
    gap:16px;margin-top:clamp(20px,3vh,30px);padding:0 var(--gutter);
  }
  .facSlideCount{
    font-family:var(--ui);font-size:var(--caption);font-weight:600;
    letter-spacing:.14em;color:var(--on-dark-muted);
  }
  .facSlideCount b{color:var(--ivory);font-weight:600}
  .facSlideCount i{font-style:normal;margin:0 5px;opacity:.5}
  .facSlideDots{display:flex;align-items:center;gap:8px}
  .facSlideDots button{
    width:20px;height:2px;background:rgba(245,241,232,.34);
    border:0;padding:0;cursor:pointer;
    transition:background 380ms var(--ease-expo),width 380ms var(--ease-expo);
  }
  .facSlideDots button.is-on{background:var(--ivory);width:30px}

  .hDiningItemContainer{left:20px;right:20px;bottom:20px}

  .bookRow{grid-template-columns:1fr;gap:14px}
  .overlayCard{padding:28px}
}

/* --- small phones / 414 & 390 --- */
@media (max-width:430px){
  :root{--h1:29px;--h2:24px;--h3:20px;--body-lg:16px;--body:16px;--body-sm:15px;--body-lh:26px}
  .spaced{letter-spacing:.16em}
  /* this used to restate 46px and, being the later block, it silently undid
     the phone size set at 767 — the smallest screen ended up with the biggest
     button on the site */
  .oButton{height:36px;padding:0 16px;font-size:12px}
  .hFacilitiesItem{width:78vw}
  .clockGrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:22px}
}

/* ---------- 25. REDUCED MOTION ---------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .top_hidden,.top_translated,.top_single,.top_low,.top_ultralow,.scaled{transform:none !important;opacity:1 !important}
  /* the mosaic's wipe is the one reveal that hides content rather than moving
     it, so it needs its own reset — without this the frames stay clipped away */
  .mosaicItem{clip-path:none !important}
  .no_opacity{opacity:1 !important}
  #hImgLayer,#hImgCover,#hImg,#hFacilitiesScroller{transform:none !important}
  .scrollLine::after{animation:none !important}
}

/* ==========================================================================
   26. INNER PAGES
   Every page that is not the homepage is built from the components below.
   They invent NOTHING: the gutter, the band rhythm, the type scale, the
   heading colour and the button are the homepage's own tokens, so an inner
   page is the homepage's design system applied to a different subject.

   The one thing an inner page cannot inherit is the hero. The homepage bar
   floats in ivory over a full-screen film; here there is no film, and an
   ivory bar over an ivory page is an invisible bar. So inner pages carry
   `body.is-inner`, which pins the header to the solid state it would
   otherwise take on scroll — the same bar, in the state it already has for
   nine-tenths of the homepage.
   ========================================================================== */

/* ---- 26a. the page head: breadcrumb, title, lead ----
   The reference sets these three as one centred stack with the crumb pulled
   out to the left margin. Its top padding has to clear the fixed bar, which
   is why the header height is IN the sum rather than being left to a magic
   number: change --header-h-solid and every inner page follows it. */
/* --band-y is the gap BETWEEN bands. Everything on an inner page below is one
   band, so its internal gaps are --head-gap, exactly as the homepage's heads
   and content use it. Getting that wrong is what made the first cut of this
   page read as five separate pages stacked: 174px above the crumb and then
   four 100px holes inside a single section. The band figure survives in ONE
   place — the space above the footer. The space under the bar is a gap, not a
   band, and is set on .pgHead below. */
.pgSection{padding-bottom:var(--band-y)}
/* The header height is why the sum looks odd. The bar is fixed and opaque, so
   the first 74px of this padding sits underneath it and is never seen; only
   what is left shows as space above the breadcrumb. The header therefore has
   to be ADDED to the figure rather than counted as part of it.

   NOT --band-y. Paying the full band figure here made a 100px void between the
   bar and the breadcrumb — symmetric with the foot of the page on paper, and
   wrong on screen, because the bottom of a page ends in nothing while the top
   of one ends in a fixed bar 34px away. The eye reads the two as air of the
   same kind and they are not: one is a margin, the other is a gap. 34px is
   what it takes for the bar's shadow to land clear of the crumb. */
/* DO NOT PUT --band-y BACK HERE. It has been restored to the band figure
   twice and reverted twice, each time on the client's own instruction that
   the gap is too large. The argument for 100px is that it matches what
   .pgSection pays at the foot, and that is true and is not the point: the
   foot of a page ends in nothing, while the top of one ends in a fixed bar
   34px away. Equal numbers do not read as equal air when one of them is a
   margin and the other is a gap. */
.pgHead{
  padding-top:calc(var(--header-h-solid) + clamp(20px,3vh,34px));
  padding-bottom:0;
}
.crumb{
  display:flex;align-items:center;flex-wrap:wrap;
  gap:10px;margin:0;padding:0;
  font-family:var(--ui);font-size:var(--caption);font-weight:500;
  letter-spacing:.02em;line-height:1;
  color:var(--text-muted);
}
/* THE TRAIL IS THE CONTROL, THE DESTINATION IS THE LABEL.
   This is the reverse of what it was, and it is a deliberate swap: the links
   now carry the weight and the underline, and the current page sits quiet in
   the muted grey.

   The reasoning is that only one item in this row is clickable. The page you
   are on is already named by the heading directly beneath it, so stating it
   twice in the strongest ink on the page spends emphasis on the one word
   nobody needs — while the way BACK, which is the only thing the component
   can actually do for you, was the faint one. Weight and a rule are how the
   rest of the site marks something you can press. */
.crumb a{
  color:var(--heading);font-weight:600;
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:4px;
  transition:color 380ms var(--ease-expo),
             text-decoration-color 380ms var(--ease-expo);
}
.crumb a:hover,.crumb a:focus-visible{color:var(--orange)}
/* The separator is DRAWN, not typed. It was a \203A single angle quote, which
   is a punctuation glyph: it carries Montserrat's own side bearings, sits on
   the text baseline rather than on the optical centre of the line, and comes
   out of the font at whatever weight the designer chose — at 13px that reads
   as a thin tick floating above the words. Two borders on a rotated square
   give a chevron whose weight, size and vertical position are ours to set.

   The rotated box is a square standing on its corner, so its visual centre
   sits a little below the box's centre; top:-1px puts it back on the row. */
.crumb li + li::before{
  content:"";
  flex:0 0 auto;
  width:6px;height:6px;
  margin-right:11px;
  border-top:1.6px solid var(--text-muted);
  border-right:1.6px solid var(--text-muted);
  transform:rotate(45deg);
  position:relative;top:-1px;
}
.crumb li{display:flex;align-items:center;list-style:none}
/* the page you are on: no link, no rule, no extra weight. It inherits the
   row's own 500 and muted grey, which is what makes the link beside it read
   as the thing to press. */
.crumb li[aria-current]{color:var(--text-muted);font-weight:500}

/* the crumb → title gap, and the title → lead gap. Two tokens, used by every
   inner page, so the head reads identically from page to page. */
.pgTitleWrap{
  margin-top:var(--head-gap);
  text-align:center;
  position:relative;
}
/* The oversized ghost wordmark that used to sit behind the title is gone.
   It read as a watermark rather than as type: the wordmark is already stated
   in full colour in the bar directly above it, so saying it again at 8.5%
   ink, overlapping the heading, was the same word twice with the second one
   half-erased. The title carries the page on its own.
   (.pgTitleWrap keeps position:relative — the title still stacks above the
   page ground, and anything added to the head later can position against it.) */
.pgTitle{
  margin:0;position:relative;z-index:1;
  font-family:var(--display);font-weight:400;
  font-size:var(--h1);line-height:1.14;letter-spacing:.03em;
  color:var(--heading);
}
/* the cursive half of the title, as the homepage sets its accents */
.pgTitle .cAlt{display:inline-block;vertical-align:baseline}
.pgLead{
  margin:clamp(14px,1.8vh,24px) auto 0;
  max-width:64ch;position:relative;z-index:1;
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}

/* ---- 26a-i. the outlet tab strip ----
   Sibling pages of one family, listed across the top so you can cross from
   one to the other without going back to the summary. It sits INSIDE
   .pgHead rather than above the section: .pgHead already pays the fixed
   bar's height in its top padding, so putting the strip anywhere else means
   paying that height twice and hunting the difference later.

   The active tab is marked by aria-current in the markup, so the rule that
   draws it and the fact a screen reader announces are the same fact — there
   is no separate .is-active class to fall out of step with it. */
.pgTabs{
  display:flex;justify-content:center;flex-wrap:wrap;
  gap:clamp(20px,2.6vw,42px);
  border-bottom:1px solid var(--line);
  margin-bottom:clamp(18px,2.4vh,30px);
}
.pgTabs a{
  display:block;padding:0 2px clamp(10px,1.4vh,16px);
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-muted);
  /* the underline is on the tab, pulled down over the strip's own rule, so
     the active mark sits ON the line rather than below it */
  border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color 340ms var(--ease-expo),border-color 340ms var(--ease-expo);
}
.pgTabs a:hover,.pgTabs a:focus-visible{color:var(--heading)}
.pgTabs a[aria-current="page"]{color:var(--heading);border-bottom-color:var(--forest)}

/* the one line that states what the outlet is for, between the name and the
   paragraph — display face, so it reads as part of the title rather than as
   a first sentence that happens to be short */
.pgTagline{
  margin:clamp(12px,1.5vh,20px) auto 0;max-width:48ch;
  position:relative;z-index:1;
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.3;letter-spacing:.01em;
  color:var(--heading);
}

/* ---- 26b. mission / vision / values ----
   Three boxes on the pale green, side by side. The fill is --wash-solid, the
   same colour the dining band paints, so the boxes belong to the site's
   palette rather than introducing a green of their own — and it is the fill
   that draws them now, not an outline, so nothing on the page is enclosed by a
   rule. They stay inside the page's own measure: the colour is in the boxes,
   never edge to edge. */
.mvv{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  /* a flat 50, not a clamp: the boxes are what carry the colour, so the air
     between them is a stated figure rather than something that drifts with
     the viewport */
  gap:50px;
  margin-top:var(--block-gap);
}
.mvvCard{
  background:var(--wash-solid);
  padding:clamp(28px,2.6vw,42px);
  display:flex;flex-direction:column;
  /* a floor, not a height — it exists so the three match when their bodies
     differ, nothing more */
  min-height:clamp(150px,14vw,190px);
}
.mvvCard h2{
  margin:0 0 clamp(14px,1.7vh,22px);
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.2;letter-spacing:.02em;
  color:var(--heading);
}
.mvvCard p{margin:0;font-size:var(--body);line-height:var(--body-lh);color:var(--text)}
/* The contact page reuses these boxes, and there a phone number or an email
   address IS the point of the box. The site's global rule strips colour and
   underline from every anchor, which leaves those two with nothing to say they
   can be tapped — so inside a box they take a rule under them, and the accent
   under the pointer. Elsewhere these boxes carry no links and this does
   nothing. */
.mvvCard a{
  color:var(--heading);
  border-bottom:1px solid rgba(47,73,52,.28);
  transition:color 380ms var(--ease-expo),border-color 380ms var(--ease-expo);
}
.mvvCard a:hover,.mvvCard a:focus-visible{color:var(--orange);border-color:var(--orange)}

/* ---- 26c. the mosaic ----
   Three columns, two of them carrying one double-height frame each, on
   opposite rows. That offset is the whole point: a grid of equal rectangles
   reads as a contact sheet, and the two tall frames break the rows without
   any frame needing a border or a caption to earn its place. */
.mosaic{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  /* The row unit sets the whole collage: three rows of it plus two gaps. At
     240px that came to roughly 770px, so the mosaic alone was taller than the
     copy above it and pushed the closing action off the screen. 180 brings the
     block to about 580 — still two full-height frames, in half a screen less. */
  grid-auto-rows:clamp(120px,12.5vw,180px);
  gap:clamp(14px,1.5vw,24px);
  margin-top:var(--block-gap);
}
.mosaicItem{
  overflow:hidden;position:relative;display:block;
  background:var(--sand);
  /* Each frame wipes UP into place rather than fading. The gallery ring opens
     with a clip-path for the same reason: a picture that grows out of the page
     reads as arriving, where an opacity fade reads as a page still loading.
     The stagger below is per COLUMN, not per item — seven separate delays turn
     a composition into a queue. */
  clip-path:inset(0 0 100% 0);
  transition:clip-path 1250ms var(--ease-expo);
}
.show .mosaicItem{clip-path:inset(0 0 0 0)}
.show .mosaicItem:nth-child(2),.show .mosaicItem:nth-child(4){transition-delay:120ms}
.show .mosaicItem:nth-child(3),.show .mosaicItem:nth-child(5),
.show .mosaicItem:nth-child(7){transition-delay:240ms}
.mosaicItem img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1600ms var(--ease-expo);
}
.mosaicItem:hover img{transform:scale(1.05)}
/* placed explicitly rather than left to auto-flow: the tall frames have to
   land in columns 1 and 2 on DIFFERENT rows, and auto-flow packs them both
   against the top the moment the copy above changes height */
.mosaic > :nth-child(1){grid-column:1;grid-row:1 / span 2}
.mosaic > :nth-child(2){grid-column:2;grid-row:1}
.mosaic > :nth-child(3){grid-column:3;grid-row:1}
.mosaic > :nth-child(4){grid-column:2;grid-row:2 / span 2}
.mosaic > :nth-child(5){grid-column:3;grid-row:2}
.mosaic > :nth-child(6){grid-column:1;grid-row:3}
.mosaic > :nth-child(7){grid-column:3;grid-row:3}

/* ---- 26d. the closing action ---- */
.pgCta{
  margin-top:var(--block-gap);
  /* Every page closes on one button. The gap and the wrap cost nothing with
     a single child and mean a second action can be added to any page without
     the two colliding on a narrow screen — which is what happened the one
     time a page did carry two. */
  display:flex;justify-content:center;align-items:center;
  flex-wrap:wrap;gap:clamp(14px,1.6vw,22px);
}

/* ==========================================================================
   27. ROOMS — the summary grid and the room page
   Both are built to the reference's geometry, measured off it rather than
   guessed: a three-column card grid at a 38px gutter, cards on white inside a
   hairline, a picture filling the card's width at roughly 3:2, then the name,
   the copy and a text link. The reference's own page inset (228px) and type
   sizes are NOT carried over — those come from this site's tokens, so the
   rooms pages sit on the same 100px gutter and the same scale as the homepage
   and the about page.
   ========================================================================== */

/* ---- 27a. the summary list — alternating rows ----
   One row per room type: a photograph on one side, the name and the facts on
   the other, and the side SWAPS every row. That alternation is the whole
   device — four identical rows read as a table, and a card grid put the four
   room types in a shape built for six, leaving the fourth stranded on its own
   line. Alternating rows carry any number of rooms, one to a screen, and give
   each one a picture large enough to be worth looking at.

   No card, no fill, no border. A hairline between rows is how the rest of this
   site separates one thing from the next. */
.roomList{margin-top:var(--block-gap)}
/* The picture always takes the WIDER column, and that is why there are two
   templates rather than one. A single 1.1fr/1fr gave the extra width to
   column ONE — so on an odd row it went to the picture and on an even row,
   where the picture moves to column two, it went to the text instead. The
   rows were not variations on a layout, they were two different layouts, and
   the eye reads that as something being slightly wrong without being able to
   say what. Swapping the template with the picture keeps the proportion fixed
   to the thing it belongs to. */
/* 1.12/.88 gave the picture a column half again as wide as the text and, with
   a 1.2 frame on top of it, a photograph about 630px tall against a panel of
   maybe 500 — the row had a clear winner and it was not the words. 1.04/.96
   is still the picture's row, just no longer by a margin you can measure by
   eye, and the panel gets the width back. */
/* NO COLUMN GAP, and that is what makes the padding work out.
   The panel has to sit flush against the photograph — visibly they touch, and
   the panel then carries on a little way underneath. With a gap between the
   columns the air on the picture side of the text would be the gap PLUS the
   padding, while the air on the far side would be the padding alone, so the
   two could never be equal without a negative number. At gap:0 the body's edge
   IS the picture's edge, and one padding value gives the same air on both. */
.roomRow{
  display:grid;
  grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:0;
  /* stretch, so the body fills the row and the panel can be sized off the
     PHOTOGRAPH rather than off the length of the copy */
  align-items:stretch;
}
.roomRow:nth-child(even){grid-template-columns:minmax(0,.96fr) minmax(0,1.04fr)}

/* ---- the panel behind the copy ----
   A block of --wash-solid that the text sits inside and the photograph sits on
   top of. Three numbers describe it and nothing else does:

   --pad        ONE value, all four sides. The text is centred in the panel, so
                the air above it equals the air below and the air left equals
                the air right. Earlier cuts had four different paddings and a
                lifted picture, which made the panel a shape you had to read
                rather than one you could take in.
   --oversail   how far the panel stands proud of the PHOTOGRAPH, top and
                bottom — 30px each, symmetric.
   --panel-in   how far it carries on under the picture's edge, so the two
                overlap instead of merely abutting.

   The panel is sized off the photograph, not off the copy. The row stretches,
   the picture keeps its own aspect ratio at the top of it, so the body fills
   the row's height and the panel is that height plus 30 at each end. A short
   paragraph and a long one therefore produce the same panel — which is the
   point, because four rows of different copy have to look like four of the
   same thing. */
.roomRow{
  --pad:clamp(28px,3.4vw,64px);
  --oversail:clamp(16px,2.2vh,30px);
  --panel-in:clamp(24px,2.6vw,52px);
}
/* the copy is centred against the picture rather than sitting at the top of a
   stretched column — flex is doing what align-items:center used to */
.roomRowBody{
  position:relative;z-index:0;
  display:flex;flex-direction:column;justify-content:center;
  padding:var(--pad);
}
.roomRowBody::before{
  content:"";position:absolute;z-index:-1;
  background:var(--wash-solid);
  top:calc(-1 * var(--oversail));
  bottom:calc(-1 * var(--oversail));
  /* odd row: picture in column one, so the tuck is on the body's left */
  left:calc(-1 * var(--panel-in));right:0;
}
.roomRow:nth-child(even) .roomRowBody::before{
  left:0;right:calc(-1 * var(--panel-in));
}
/* No rule between rows. With the picture changing sides every row, the swap
   is already the boundary — a hairline on top of it states twice what the
   layout says once. Space does the separating instead, which is why the gap
   here is larger than the one it replaced: a rule can be tight because it is
   visible, and space cannot.

   1.7 became 2.4 when the panels arrived: each panel stands 30px proud of its
   photograph at both ends, so consecutive rows come 60px closer than the
   figure suggests. 120px leaves them clear. */
.roomRow + .roomRow{margin-top:calc(var(--block-gap) * 2.4)}
/* order:2 on the picture, not on the text — moving the picture keeps the
   heading first in the source, so the reading order stays name-then-picture
   for a screen reader however the row is drawn */
.roomRow:nth-child(even) .roomRowPic{order:2}
/* THE PICTURE SETS THE ROW'S HEIGHT, which is why it must not stretch with it.
   The row is align-items:stretch so the body can fill it; align-self:start
   opts the photograph out, leaving it free to take its height from its own
   1.34 ratio. The row is then as tall as the picture, the body matches it, and
   the panel is that height plus the oversail at each end.

   z-index:1 is what keeps the panel under the photograph rather than over it.
   The two are siblings and the body comes second in the document, so without
   it the wash would paint across the picture's edge. */
.roomRowPic{
  display:block;overflow:hidden;position:relative;z-index:1;
  align-self:start;
  background:var(--sand);aspect-ratio:1.34/1;
}
.roomRowPic img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1600ms var(--ease-expo);
}
.roomRow:hover .roomRowPic img{transform:scale(1.04)}
/* Bigger than --h2, deliberately. This is the only heading in its row and it
   has a picture the height of four paragraphs sitting beside it — at 34px it
   was the smallest thing on its own half of the row and the eye went to the
   photograph first every time. It stops short of --h1 so the page title above
   still outranks it: 42px against 45. */
.roomRowName{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:clamp(28px,2.9vw,42px);line-height:1.14;letter-spacing:.02em;
  color:var(--heading);
}
/* a short hairline closing the name off from the paragraph. The site already
   separates with hairlines and nothing else, so this adds an edge to the
   block without introducing a device the page does not otherwise own */
/* No rule under the row heading. It ran under every name on the rooms, dining
   and meetings pages — a 54px stub that separated a heading from the paragraph
   it belongs to, which is the one join in the block that never needed marking.
   The space between them already does it. */
/* No 46ch cap here any more. The column IS the measure now — at .88fr it comes
   out near the same 46ch on a desktop — and the cap was the reason the
   paragraph stopped roughly 200px short of its own column, leaving a ragged
   void between the text and the picture that read as unfinished rather than
   as space. */
.roomRowCopy{
  margin:clamp(14px,1.8vh,20px) 0 0;
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}
/* the four figures that decide a booking. No rule above them either: the
   label-over-value pairs are already a different texture from the paragraph,
   so the change of setting is the separation. */
.roomRowSpecs{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,2vh,22px) clamp(20px,2.4vw,40px);
  margin:clamp(24px,3vh,34px) 0 0;
}
/* THE COLUMN COUNT FOLLOWS THE NUMBER OF FACTS. Two columns is right for the
   rooms, which state four things and fall into a tidy 2x2. The venues state
   three, and two columns broke them 2 + 1 — leaving a hole beside the last
   one, which reads as a missing figure rather than as a layout that ran out.
   Three abreast fits the row and has no gap in it. */
#pgEvents .roomRowSpecs{grid-template-columns:repeat(3,minmax(0,1fr))}
/* The term is the heading of its pair, so it takes the heading family; the
   value is read as ordinary copy, so it takes the body one.

   The two were the other way round, and Cinzel is the reason it has to be
   this way: it is an all-caps face with no true lowercase, so it drew
   "735 sq ft" and "3 adults, 2 children" as small capitals — a value you read
   letter by letter. Montserrat gives those figures a normal lowercase, and
   Cinzel does what it is good at on a four-letter uppercase label.

   Tracking comes down from .18em because Cinzel already sets wide, and the
   value drops to --body-lg because Montserrat's larger x-height makes 18px
   here match the optical size the 20px Cinzel had. */
.roomRowSpecs dt{
  font-family:var(--display);font-size:var(--body-lg);font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;line-height:1.3;
  color:var(--heading);
}
.roomRowSpecs dd{
  margin:6px 0 0;
  font-family:var(--ui);font-weight:400;
  font-size:var(--body);line-height:1.5;letter-spacing:0;
  color:var(--text);
}
.roomRowCta{margin-top:clamp(24px,3vh,36px)}
/* ---- 27a2. FACILITIES — one picture, then cards three across ----
   Measured off the reference: a full-width plate above, then 462-wide cards on
   a 38px gutter, each a picture over a body inside a hairline. Its 228px page
   inset is not carried over; these sit on this site's own --gutter. */
.pgShot{
  margin-top:var(--block-gap);
  overflow:hidden;background:var(--sand);
  aspect-ratio:2/1;
}
.pgShot img{width:100%;height:100%;object-fit:cover;display:block}

.facGrid{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(22px,2vw,38px);
  margin-top:var(--block-gap);
}
/* THE ONE CARD, used by offers, experiences, facilities and the blog.
   Those four ran three different components: .facCard here, .expCard on
   experiences and .offerCard on offers — the same idea drawn three ways, so
   a reader crossing between the pages met three different objects doing one
   job. The other two are gone and their pages use this.

   The card ground is --card with a hairline: it lifts off the ivory page
   without the glare of pure white, and the border is what carries the hover.
   It used to be --wash-solid, the pale green, which read as a filled panel
   rather than as a card and left the sage placeholders invisible against
   their own cards on the facilities page. */
.facCard{
  display:flex;flex-direction:column;
  background:var(--card);
  border:1px solid var(--line);
  transition:border-color 500ms var(--ease-expo);
}
.facCard:hover{border-color:var(--sage)}
/* flex:none, for the same reason the room tiles need it: the grid stretches
   every card in a row to the tallest, and without it the picture is a
   shrinkable item whose aspect-ratio loses to flex-shrink */
.facCardPic{
  display:block;overflow:hidden;flex:none;
  background:var(--sand);aspect-ratio:1.5/1;
}
.facCardPic img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1600ms var(--ease-expo);
}
.facCard:hover .facCardPic img{transform:scale(1.05)}
/* Left-aligned, and no ground of its own. It was centred on its own ivory
   panel while facilities was the only page using it — right for a card that
   is a name and one word, wrong the moment three of the four pages carry a
   paragraph and a link, because centred body copy over four lines is read
   twice as slowly. The card's own ground shows through now, so a card is one
   surface rather than a panel inside a panel. */
.facCardBody{
  display:flex;flex-direction:column;align-items:flex-start;flex:1 1 auto;
  padding:clamp(20px,1.9vw,28px) clamp(18px,1.7vw,26px) clamp(22px,2.2vw,30px);
}
.facCardName{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h4);line-height:1.25;letter-spacing:.02em;color:var(--heading);
}
/* complimentary or chargeable — the only thing a facilities list is ever
   really asked, so it is stated and nothing else is */
.facCardNote{
  margin:8px 0 0;
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--text-muted);
}
.facCardCopy{
  margin:clamp(12px,1.6vh,18px) 0 0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
/* a text link with an arrow, pushed to the foot of a card so that a row of
   cards has its links on one line however unevenly the copy above them runs */
.goLink{
  margin-top:auto;padding-top:clamp(20px,2.4vh,28px);
  display:inline-flex;align-items:center;gap:9px;align-self:flex-start;
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--forest);
  transition:color 400ms var(--ease-expo);
}
.goLink svg{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform 520ms var(--ease-expo);
}
.goLink:hover,.goLink:focus-visible{color:var(--orange)}
.goLink:hover svg{transform:translateX(5px)}

/* ---- 27a4. THE DOCUMENT COLUMN ----
   Measured off the reference's terms page: its body runs x514-1402 on a 1462
   content width, so the column is roughly 60% of the measure and centred, not
   the full width. That is the whole trick of a readable document page — the
   gutter does not set the line length, the line length does.

   Used by the FAQs and by the terms page, which are the same shape: a run of
   heading-and-body pairs with a rule between them and nothing else. */
.docCol{
  max-width:min(820px, 100%);
  margin:var(--block-gap) auto 0;
}
.docItem + .docItem{
  margin-top:clamp(26px,3.4vh,42px);
  padding-top:clamp(26px,3.4vh,42px);
  border-top:1px solid var(--line);
}
.docQ{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h4);line-height:1.3;letter-spacing:.02em;color:var(--heading);
}
.docA{
  margin:clamp(10px,1.4vh,16px) 0 0;
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}
.docA + .docA{margin-top:clamp(8px,1vh,12px)}

/* ---- 27a4b. THE FAQ ACCORDION ----
   Eight closed cards you open one at a time. The FAQ used to share .docCol
   with the terms page — a run of heading-and-body pairs, every answer
   showing at once. That is right for terms, which are read straight through,
   and wrong for a FAQ, which is scanned for ONE question. Terms keeps
   .docCol; the FAQ has its own shape now.

   <details>/<summary> and no JavaScript: the browser supplies the toggle,
   the keyboard support and the right semantics for a screen reader, and the
   page still works if the script fails — which on the page carrying the
   phone number is worth more than an animation. */
.faqList{
  max-width:min(880px, 100%);
  margin:var(--block-gap) auto 0;
  display:flex;flex-direction:column;gap:clamp(10px,1.2vh,16px);
}
.faqItem{
  background:var(--card);border:1px solid var(--line);
  transition:border-color 320ms var(--ease-expo);
}
.faqItem[open],.faqItem:hover{border-color:var(--sand)}
.faqQ{
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  padding:clamp(18px,2.2vh,24px) clamp(20px,2.2vw,30px);
  cursor:pointer;list-style:none;
  /* the question is BODY face, not display: it is a sentence somebody asked,
     and Cinzel is an all-caps face that turns one into a monument */
  font-family:var(--ui);font-size:var(--body);font-weight:500;
  line-height:1.45;color:var(--heading);
}
/* both are needed — WebKit uses the pseudo-element, everyone else the property */
.faqQ::-webkit-details-marker{display:none}
.faqQ::marker{content:""}
.faqQ:focus-visible{outline:2px solid var(--forest);outline-offset:-2px}
.faqChev{
  width:20px;height:20px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform 320ms var(--ease-expo);
}
.faqItem[open] .faqChev{transform:rotate(180deg)}
.faqA{
  padding:0 clamp(20px,2.2vw,30px) clamp(18px,2.2vh,24px);
}
.faqA p{
  margin:0;font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}
.faqA p + p{margin-top:clamp(8px,1vh,12px)}
/* the lead carries the phone number and the address — they have to look like
   links without turning the paragraph into a row of them */
.pgLead a{color:var(--heading);text-decoration:underline;text-underline-offset:3px}

/* ---- 27a5. THE ARTICLE — a blog post ----
   The reference centres a plate 1129 wide inside its 1462 measure, then runs
   the body under it. Its body is 1097px, which at this site's 16px Montserrat
   would be about 130 characters a line — roughly twice a comfortable measure.
   The plate width is copied; the line length is not. Long-form body copy is
   the one place where matching a reference pixel for pixel would make the page
   worse, so this column is set from the type instead. */
.artShot{
  max-width:min(1130px, 100%);
  margin:var(--block-gap) auto 0;
  overflow:hidden;background:var(--sand);
  aspect-ratio:1.78/1;
}
.artShot img{width:100%;height:100%;object-fit:cover;display:block}
.artCol{
  max-width:70ch;
  margin:var(--block-gap) auto 0;
}
.artCol p{
  margin:0 0 clamp(14px,1.8vh,22px);
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}
.artCol p:last-child{margin-bottom:0}
.artCol h2{
  margin:clamp(28px,3.6vh,44px) 0 clamp(10px,1.4vh,16px);
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.25;letter-spacing:.02em;color:var(--heading);
}
.artCol h2:first-child{margin-top:0}
/* A run of place-and-distance pairs. It is a small table, so it is built
   like one: a rule above the first row and below every row, which CLOSES the
   block. It used to carry border-top only, which left the last row open at
   the foot and the paragraph after it sitting straight on the final name —
   the list looked like it had bled into the prose rather than ended.

   The figure is set as quiet running text, not as an uppercase tracked
   label. At 13px/600 in .12em caps it was the loudest thing in the row, so
   "12 KM · 20 MINUTES" shouted over "Mallana Dam", which is the name you are
   actually scanning for. Tabular numerals line the digits up down the
   column; a nowrap keeps "1 hour 45 minutes" off two lines. */
.artList{
  margin:0;padding:0;list-style:none;
  border-top:1px solid var(--line);
}
.artList li{
  padding:clamp(10px,1.4vh,15px) 0;
  border-bottom:1px solid var(--line);
  /* grid, not space-between: the figures form a real right-hand column
     instead of each one drifting to wherever its own name happens to end */
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  align-items:baseline;gap:4px 28px;
}
.artList li b{
  font-weight:500;color:var(--heading);
  font-size:var(--body);line-height:1.5;
}
.artList li span{
  font-family:var(--ui);font-weight:400;
  font-size:var(--body-sm);line-height:1.5;letter-spacing:0;
  color:var(--text-muted);text-align:right;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
/* the list is a block that ends; whatever follows starts clear of it */
.artList + p,.artList + h2{margin-top:clamp(20px,2.6vh,30px)}

@media (max-width:520px){
  /* the figure drops under its name — at this width the two columns leave
     about eight characters for a temple's name */
  .artList li{grid-template-columns:minmax(0,1fr)}
  .artList li span{text-align:left}
}
.artMeta{
  margin:clamp(14px,1.8vh,22px) 0 0;
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--text-muted);
}

/* ---- 27a3. the map on the contact page ----
   An iframe has no intrinsic ratio, so without one stated here it collapses to
   150px — the browser's default for a replaced element with no dimensions. */
.pgMap{
  margin-top:var(--block-gap);
  overflow:hidden;background:var(--sand);
  aspect-ratio:2.06/1;
}
.pgMap iframe{
  width:100%;height:100%;display:block;border:0;
  /* the embed ships a bright, saturated map; this settles it onto the page's
     own quieter register without making it hard to read */
  filter:saturate(.82) contrast(.97);
}

/* ---- 27b. the room page: the spec strip under the lead ----
   Four figures on one centred row, divided by rules rather than boxed. */
.specStrip{
  margin:var(--block-gap) auto 0;
  display:flex;justify-content:center;align-items:stretch;
  flex-wrap:wrap;
  gap:0;
}
.specStrip div{
  padding:0 clamp(20px,2.4vw,42px);
  text-align:center;
  border-left:1px solid var(--line);
}
.specStrip div:first-child{border-left:0}
/* the same pairing as the listing rows above — label in the heading family,
   value in the body one — so a figure reads the same wherever it appears.

   The label is the header of its pair and is sized like one: --body-lg against
   a --body value. At 13px it was the smaller of the two and the pair read
   upside down — a caption sitting on top of the thing it introduced. One step
   is all it needs, because case, family, weight and colour are already
   carrying the rank; --h4 made a four-letter word like SIZE shout over the
   figure it labels. The heading colour comes with the promotion; the value
   steps back to --text. */
.specStrip dt{
  font-family:var(--display);font-size:var(--body-lg);font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;line-height:1.3;color:var(--heading);
}
.specStrip dd{
  margin:6px 0 0;
  font-family:var(--ui);font-weight:400;
  font-size:var(--body);line-height:1.5;letter-spacing:0;color:var(--text);
}

/* ---- 27c. the room gallery — three across, the middle one large ----
   Three slots on one row: a wide centre frame with a narrower, shorter frame
   either side of it. The sides are not decoration — they are the previous and
   next pictures, held at reduced height and opacity so the row reads as a
   sequence you are part-way through rather than as three equal pictures.

   The slots are FIXED and the pictures move through them. Sliding a track
   would mean the centre frame changes size as it travels, and the whole point
   of this arrangement is that the middle is always the big one. */
.rgal{position:relative;margin-top:var(--block-gap)}
.rgalStage{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.95fr) minmax(0,1fr);
  gap:clamp(12px,1.4vw,22px);
  align-items:center;
}
.rgalSlot{
  margin:0;overflow:hidden;background:var(--sand);
  position:relative;
}
/* TWO LAYERS PER SLOT, so a step can be a SLIDE rather than a swap.
   Each slot holds two stacked pictures: the one you are looking at, and the
   one coming in. A step moves the outgoing frame off one edge while the
   incoming frame arrives from the other, both inside the slot's own
   overflow — so the pictures visibly travel in the direction you asked for,
   which is what makes it read as a slider.

   The frames themselves still do not move or resize. That was the original
   reason for cross-fading, and it still holds: the centre is always the
   large frame, so a track of equal items sliding through it would change the
   big picture's size as it went. Animating INSIDE fixed slots keeps the
   arrangement and gets the travel. */
.rgalSlot img{
  position:absolute;inset:0;
  width:100%;height:100%;object-fit:cover;display:block;
  will-change:transform,opacity;
  transition:transform 760ms var(--ease-expo),opacity 620ms var(--ease-expo);
}
/* the frame that is being positioned before it animates — one frame only */
.rgalSlot img.is-placing{transition:none}
.rgalSlot.is-main{aspect-ratio:1.6/1}
/* shorter AND squarer, so the centre reads as the larger picture on both axes */
.rgalSlot.is-side{aspect-ratio:1.08/1}
/* The dimming moved from the picture to the SLOT. It used to sit on the img,
   where it fought the slide for the same opacity property — the incoming
   side frame would animate to 1 and then be dragged back to .55, so it
   flashed bright on every step. On the slot it is a separate layer that the
   transition cannot touch. */
.rgalSlot.is-side{opacity:.55;transition:opacity 520ms var(--ease-expo)}
.rgal:hover .rgalSlot.is-side{opacity:.72}
/* the side frames are live controls — clicking one brings it to the centre */
.rgalSlot.is-side{cursor:pointer}

@media (prefers-reduced-motion:reduce){
  .rgalSlot img{transition:none}
}

/* Arrows sit over the join between the centre frame and each side one, and
   that is exactly why they are SOLID rather than frosted.

   They were a copy of .voicesArrow: a 7%-black tint over backdrop-filter,
   with an `inset 0 1px 0 rgba(255,255,255,.65)` highlight along the top. On
   the guest book that works, because it sits on flat ivory and has one thing
   to be translucent against. Here it straddles a photograph and the page —
   so the blur sampled a dark picture on one side and cream on the other, and
   averaged them into a grey metallic wash. The inset highlight then read as
   a bevel on top of it, which is how a matte, hairline-ruled page ended up
   with a chrome button on it.

   Solid ivory has no such dependency: it looks identical over the picture,
   over the join and over the page. The shadow is what lifts it off the
   photograph now, and it is the only thing here that varies with what is
   behind it. */
.rgalArrow{
  position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:50px;height:50px;border-radius:50%;display:grid;place-items:center;
  cursor:pointer;
  background:var(--ivory);
  border:1px solid var(--line);
  box-shadow:0 4px 18px rgba(15,59,39,.16);
  color:var(--heading);
  transition:background 400ms var(--ease-expo),border-color 400ms var(--ease-expo),
             color 400ms var(--ease-expo);
}
.rgalArrow svg{
  width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
}
/* it fills rather than tinting, the way every other control on the page does */
.rgalArrow:hover,.rgalArrow:focus-visible{
  background:var(--forest);border-color:var(--forest);color:var(--white);
}
.rgalArrow.is-prev{left:calc(25% - 25px)}
.rgalArrow.is-next{right:calc(25% - 25px)}

.rgalCtrl{
  margin-top:clamp(18px,2.2vh,28px);
  display:flex;align-items:center;justify-content:center;gap:18px;
}
.rgalCount{
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.14em;color:var(--text-muted);
}
.rgalCount b{color:var(--heading);font-weight:600}
.rgalCount i{font-style:normal;margin:0 5px;opacity:.5}
.rgalDots{display:flex;align-items:center;gap:8px}
.rgalDots button{
  width:22px;height:2px;background:var(--line);cursor:pointer;border:0;padding:0;
  transition:background 400ms var(--ease-expo),width 400ms var(--ease-expo);
}
.rgalDots button.is-on{background:var(--heading);width:34px}

@media (max-width:1023px){
}
@media (max-width:767px){
}

/* one picture on an offer page, held to the page's measure rather than run
   edge to edge — the offer is a paragraph and a list, and a full-bleed
   photograph would outrank both */
.offerHero{
  margin-top:var(--block-gap);
  overflow:hidden;background:var(--sand);aspect-ratio:2.4/1;
}
.offerHero img{width:100%;height:100%;object-fit:cover;display:block}
@media (max-width:767px){
  .offerHero{aspect-ratio:1.6/1}
}

/* ---- 27c-ii. the event enquiry ----
   One column, centred, capped at a readable measure. A form is read as a
   sequence — you fill it top to bottom — so putting the fields side by side
   would only buy height back at the cost of the order being obvious.

   The controls are the booking bar's, restated for this block rather than
   reused from it: .bookField lives inside the overlay's dark card and paints
   itself against that ground, and lifting those rules onto the ivory page
   would have meant overriding half of them anyway. Same measurements, so the
   two read as one system: 52px tall, a hairline that turns forest on focus,
   square corners like every other edge on the site. */
.evForm{
  margin:calc(var(--block-gap) * 1.6) auto 0;
  max-width:640px;text-align:center;
}
.evFormTitle{
  margin:0 0 var(--block-gap);
  font-family:var(--display);font-weight:400;
  font-size:var(--h2);line-height:1.15;letter-spacing:.02em;color:var(--heading);
}
.evFields{display:grid;gap:clamp(16px,2vh,22px);text-align:left}
.evField label{
  display:block;margin-bottom:8px;
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--text-muted);
}
/* the required star is the only orange on the page and needs no explaining */
.evField label span{color:var(--orange);margin-left:2px}
.evField input,.evField select,.evField textarea{
  width:100%;border:1px solid var(--line);background:var(--white);border-radius:0;
  padding:0 14px;font-family:var(--ui);font-size:var(--body-sm);color:var(--text);
  transition:border-color 300ms var(--ease-expo);
}
.evField input,.evField select{height:52px}
/* a textarea has no line-height to centre against, so it pays its padding */
.evField textarea{padding:14px;line-height:1.6;resize:vertical;min-height:132px}
.evField input:focus,.evField select:focus,.evField textarea:focus{
  outline:none;border-color:var(--forest);
}
/* the browser's own "please fill this in" styling only after a submit is
   attempted — :invalid alone reddens an empty field the moment it renders */
.evFields.is-checked .evField :invalid{border-color:#B0553A}
.evSend{margin-top:clamp(8px,1.2vh,14px);text-align:center}
.evNote{
  margin:clamp(14px,1.8vh,22px) auto 0;max-width:52ch;
  font-size:var(--caption);line-height:1.65;color:var(--text-muted);
}
.evNote a{color:var(--heading);text-decoration:underline;text-underline-offset:3px}

/* ---- 27d. amenity tiles — six across, on white inside a hairline ---- */
.amenHead{margin-top:var(--block-gap);text-align:center}
.amenTitle{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h2);line-height:1.15;letter-spacing:.02em;color:var(--heading);
}
.amenLead{
  margin:clamp(12px,1.6vh,20px) auto 0;max-width:58ch;
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}
.amenGrid{
  display:grid;
  grid-template-columns:repeat(6,minmax(0,1fr));
  gap:clamp(14px,1.5vw,26px);
  margin-top:var(--block-gap);
}
.amenTile{
  background:var(--wash-solid);
  padding:clamp(18px,1.6vw,24px) 10px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(10px,1.4vh,16px);
  text-align:center;
  min-height:clamp(120px,10vw,150px);
}
.amenTile svg{
  width:26px;height:26px;flex:none;
  fill:none;stroke:var(--sage);stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
}
.amenTile span{
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.12em;text-transform:uppercase;line-height:1.45;
  color:var(--heading);
}

/* ---- 27e. the foot of a room page: back to the list, on to the next ---- */
.roomNav{
  margin-top:var(--block-gap);
  padding-top:clamp(26px,3vh,40px);
  border-top:1px solid var(--line);
  display:flex;justify-content:space-between;align-items:center;gap:20px;
  flex-wrap:wrap;
}
.roomNav a{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--heading);
  transition:color 400ms var(--ease-expo);
}
.roomNav a:hover,.roomNav a:focus-visible{color:var(--orange)}
.roomNav svg{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
}

/* ---- 27f. responsive ---- */
@media (max-width:1279px){
  .amenGrid{grid-template-columns:repeat(4,minmax(0,1fr))}
}
@media (max-width:1023px){
  /* the alternation is a two-column device — stacked, the picture always leads
     and the flip has nothing left to flip. Both templates have to be undone
     here, not just the default one: .roomRow:nth-child(even) outranks a bare
     .roomRow, and a media query adds no specificity of its own, so the even
     rows would have stayed two-up on a phone. */
  .roomRow,
  .roomRow:nth-child(even){grid-template-columns:minmax(0,1fr)}
  .roomRow{gap:clamp(22px,3vh,32px)}
  .roomRow:nth-child(even) .roomRowPic{order:0}
  /* stacked, the frame gets the full width — it can afford to be wider again */
  .roomRowPic{aspect-ratio:1.42/1}

  /* Stacked, the panel stops being an overlapping plane and becomes a plain
     block behind the copy. The picture is directly above it rather than beside
     it, so there is nothing to tuck under and nothing to stand proud of: both
     go to zero and the panel is exactly the padding box. */
  .roomRow,
  .roomRow:nth-child(even){
    --panel-in:0px;
    --oversail:0px;
    --pad:clamp(22px,5vw,36px);
  }
  /* the picture is a row of its own now, so it stops opting out of the stretch
     and simply takes its ratio */
  .roomRowPic{align-self:auto}
  /* both parities are named, or the even rule's higher specificity would keep
     its swapped tuck on a phone where there is nothing to swap */
  .roomRowBody::before,
  .roomRow:nth-child(even) .roomRowBody::before{left:0;right:0}
  /* the gap can come back down: with no overhang there is nothing to clear */
  .roomRow + .roomRow{margin-top:calc(var(--block-gap) * 1.7)}
  .facGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pgShot{aspect-ratio:1.8/1}
  .pgMap{aspect-ratio:1.6/1}
  /* the flanking frames get too narrow to read as pictures — the centre one
     takes the whole row and the arrows move to its own edges */
  .rgalStage{grid-template-columns:minmax(0,1fr)}
  .rgalSlot.is-side{display:none}
  .rgalSlot.is-main{aspect-ratio:1.5/1}
  .rgalArrow.is-prev{left:14px}
  .rgalArrow.is-next{right:14px}
  /* the rules between the specs only work while they are on one line */
  .specStrip{gap:clamp(16px,2.6vh,26px) 0}
  .specStrip div{flex:0 0 50%;border-left:0;padding:0 12px}
}
@media (max-width:767px){
  /* one per line on a phone. The #pgEvents override has to be undone by name:
     an id outranks a class however narrow the window gets, and a media query
     adds no specificity of its own — so the three-abreast rule would have
     stayed three abreast on a 390px screen. */
  .roomRowSpecs,
  #pgEvents .roomRowSpecs{grid-template-columns:minmax(0,1fr)}

  /* 10px between a row's picture and its text. Side by side that gap is a
     column gutter and has to be wide; stacked, it is the space between a
     photograph and the name of the thing in it, and 22-32px there reads as two
     unrelated blocks rather than one entry. The gap BETWEEN rows is untouched,
     so a row still separates clearly from the next one. */
  .roomRow{gap:10px}
  .rgalSlot.is-main{aspect-ratio:1.32/1}
  .rgalArrow{width:42px;height:42px}
  .amenGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .roomNav{flex-direction:column;align-items:flex-start;gap:16px}
  .facGrid{grid-template-columns:minmax(0,1fr);gap:20px}
  .pgShot{aspect-ratio:1.45/1}
  /* taller, not wider: a map squeezed into a 390px strip shows one street */
  .pgMap{aspect-ratio:1/1}
}

/* ---- 26e. responsive ----
   The hierarchy holds at every width; only the column counts and the mosaic's
   row height move. Explicit grid placement has to be UNDONE before the
   columns change, or the items keep their desktop coordinates. */
@media (max-width:1023px){
  /* stacked, 50px of dead space between three filled boxes is a lot of scroll
     for no gain — the fill already separates them once they are one per row */
  .mvv{grid-template-columns:minmax(0,1fr);gap:20px}
  .mvvCard{min-height:0}
  .mosaic{grid-auto-rows:clamp(105px,14vw,150px)}
}
@media (max-width:767px){
  .pgLead{max-width:none}

  /* Nothing to override any more: --band-y is 30 at this width, so the base
     rules already give 30 above the content and 30 below it, 60 in total. The
     hardcoded pair that used to sit here said the same thing twice and would
     have drifted the moment the token moved. The header height stays inside
     the top figure — the bar is fixed and opaque, so those pixels never show. */

  /* One weight for every button label. .oButton is 600, but the two <button>
     elements — the booking bar's submit and the enquiry forms' — inherit the
     UA's own font-weight through `font:inherit`, so a page could show a 600
     link-button beside a 400 form-button and they read as different controls.
     Stated once here rather than left to the element type. */
  .oButton,.oButton .oLabel,button.oButton{font-weight:400}

  /* EVERY button is filled on a phone, not just the primary. The hollow
     variant is a hover affordance — it is transparent with a 1px rule and
     fills with green under the pointer. A phone has no pointer, so that state
     never arrives: what a touch user sees is a permanently empty outline,
     while Book now beside it is solid. Same green, so the two read as one
     control family rather than as a button and a bordered label.

     background-COLOR, not the shorthand: the shorthand carries
     background-image and would wipe out the falling-leaf layer that
     .oButton::before paints. */
  .oButton{
    background-color:var(--deep);
    border-color:var(--deep);
    color:var(--white);
  }
  /* ON A DARK BAND THE FILL INVERTS. --deep on the rooms film or the
     experiences band is a dark green button on dark green footage — the fill
     that makes a button obvious on ivory makes it vanish here. These take the
     white ground and the dark label instead, which is the same pairing the
     bar's Book now uses, read the other way round. */
  .on-dark .oButton,.section-dark .oButton,.section-forest .oButton,
  #rooms .oButton,#hFacilitiesHead .oButton{
    background-color:var(--white);
    border-color:var(--white);
    color:var(--deep);
  }

  /* two columns on a phone, and the tall frames become ordinary ones — a
     double-height frame at this width is taller than the screen */
  .mosaic{
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-auto-rows:clamp(96px,25vw,140px);
  }
  .mosaic > *{grid-column:auto !important;grid-row:auto !important}
  .mosaic > :nth-child(1){grid-column:1 / -1 !important;grid-row:span 2 !important}
}


/* ==========================================================================
   28. GALLERY — the filtered grid and the lightbox
   ========================================================================== */

/* ---- 28a. the filters ----
   Same construction as .pgTabs: a shared rule along the bottom with the
   active mark sitting ON it rather than under it. Buttons rather than links,
   because they change what is on screen instead of going anywhere. */
.galFilters{
  display:flex;justify-content:center;flex-wrap:wrap;
  gap:clamp(16px,2.4vw,38px);
  margin-top:var(--head-gap);
  border-bottom:1px solid var(--line);
}
.galFilter{
  padding:0 2px clamp(10px,1.4vh,16px);cursor:pointer;
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-muted);background:none;border:0;
  border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color 340ms var(--ease-expo),border-color 340ms var(--ease-expo);
}
.galFilter:hover{color:var(--heading)}
.galFilter.is-active{color:var(--heading);border-bottom-color:var(--forest)}
.galFilter:focus-visible{outline:2px solid var(--orange);outline-offset:4px}

.galEmpty{
  margin:var(--head-gap) 0 0;text-align:center;
  font-size:var(--body);color:var(--text-muted);
}

/* ---- 28b. the grid ----
   Rows of a fixed height with frames spanning two or three of them, rather
   than CSS columns. Columns would order the pictures top-to-bottom down one
   column and then jump back up to the top of the next — which is fine to
   look at and wrong to step through, because the lightbox's "next" would
   send you back up the page. A grid keeps DOM order and reading order the
   same thing.

   `dense` matters once a filter is on: without it a three-row frame that no
   longer fits leaves a hole where it used to be. */
/* EVERY TILE THE SAME SHAPE, and that is a decision about the bottom edge.
   This was a mosaic: frames spanning three or four rows of a short auto-row,
   placed with grid-auto-flow:dense. Mixed spans cannot end level. With 22
   frames of two heights across three columns the spans totalled 73 units,
   which does not divide by three, so one column always ran short and the
   grid finished on a ragged step with a hole beside it.

   Nothing in pure grid fixes that while the heights vary — and it gets worse
   under a filter, where the surviving frames are a different mix every time.
   One ratio for every tile means the rows are level by construction, at any
   count and in any filtered set. The variety comes from the photographs
   themselves, which is where it should have been coming from. */
.galGrid{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(10px,1.1vw,18px);
  margin-top:var(--head-gap);
}
.galItem{
  aspect-ratio:4/3;
  position:relative;overflow:hidden;padding:0;border:0;
  background:var(--sand);cursor:pointer;
  /* the frame is a button, so it needs the block behaviour back */
  display:block;width:100%;
}
.galItem img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 1500ms var(--ease-expo),opacity 500ms var(--ease-expo);
}
.galItem:hover img,.galItem:focus-visible img{transform:scale(1.06)}
.galItem:focus-visible{outline:2px solid var(--orange);outline-offset:3px}
/* the affordance: a plus, drawn in the corner, that arrives on hover. No
   caption plate over the picture — the caption is the lightbox's job */
.galItem::after{
  content:"";position:absolute;right:14px;bottom:14px;
  width:30px;height:30px;
  background:
    linear-gradient(var(--ivory),var(--ivory)) center/12px 1px no-repeat,
    linear-gradient(var(--ivory),var(--ivory)) center/1px 12px no-repeat,
    rgba(15,59,39,.62);
  opacity:0;transform:translateY(6px);
  transition:opacity 420ms var(--ease-expo),transform 420ms var(--ease-expo);
}
.galItem:hover::after,.galItem:focus-visible::after{opacity:1;transform:none}

/* filtered out. Kept in the DOM and simply not laid out — the JS reads the
   visible set for the lightbox, so a hidden frame is never steppable. */
.galItem.is-out{display:none}

/* ---- 28c. the lightbox ----
   Fixed to the viewport, so it must live OUTSIDE [data-scroll-container]:
   Locomotive transforms that element, and a fixed child of a transformed
   ancestor is fixed to the ancestor instead of to the screen. */
.lbox{
  position:fixed;inset:0;z-index:200;
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;
  gap:clamp(6px,1.6vw,26px);
  padding:clamp(52px,7vh,88px) clamp(12px,3vw,54px) clamp(20px,4vh,44px);
  opacity:0;pointer-events:none;
  transition:opacity 460ms var(--ease-expo);
}
.lbox.is-open{opacity:1;pointer-events:auto}
.lboxBg{position:absolute;inset:0;background:rgba(9,22,15,.94);z-index:0}

.lboxFigure{
  position:relative;z-index:1;margin:0;min-width:0;
  display:flex;flex-direction:column;align-items:center;
  gap:clamp(12px,2vh,22px);
}
.lboxStage{
  width:100%;display:flex;align-items:center;justify-content:center;
  min-height:0;
}
.lboxImg{
  max-width:100%;max-height:calc(100vh - clamp(150px,20vh,230px));
  width:auto;height:auto;object-fit:contain;display:block;
  opacity:0;transition:opacity 380ms var(--ease-expo);
}
.lboxImg.is-in{opacity:1}

.lboxBar{
  display:flex;align-items:baseline;justify-content:center;
  flex-wrap:wrap;gap:8px clamp(16px,2.4vw,32px);
  width:100%;
}
.lboxCaption{
  font-size:var(--body-sm);line-height:1.6;
  color:rgba(245,241,232,.78);text-align:center;max-width:64ch;
}
.lboxCount{
  font-family:var(--display);font-size:15px;line-height:1;
  letter-spacing:.08em;color:rgba(245,241,232,.5);
  font-variant-numeric:tabular-nums;flex:0 0 auto;
}
.lboxCount b{font-weight:400;color:var(--ivory);font-size:20px}
.lboxCount i{font-style:normal;margin:0 5px;opacity:.45}
.lboxCount em{font-style:normal}

/* the arrows are the rail's glass discs, restated for a dark ground */
.lboxArrow,.lboxClose{
  position:relative;z-index:1;
  width:clamp(42px,3.4vw,52px);height:clamp(42px,3.4vw,52px);
  border-radius:50%;padding:0;cursor:pointer;flex:0 0 auto;
  display:grid;place-items:center;color:var(--ivory);
  background:rgba(245,241,232,.14);
  -webkit-backdrop-filter:blur(14px) saturate(1.3);
  backdrop-filter:blur(14px) saturate(1.3);
  border:1px solid rgba(245,241,232,.4);
  transition:background 420ms var(--ease-expo),border-color 420ms var(--ease-expo),
             opacity 300ms var(--ease-expo);
}
.lboxArrow svg,.lboxClose svg{
  width:17px;height:17px;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
  transition:transform 500ms var(--ease-expo);
}
.lboxArrow:hover,.lboxClose:hover,
.lboxArrow:focus-visible,.lboxClose:focus-visible{
  background:var(--forest);border-color:var(--forest);
}
.lboxArrow.is-next:hover svg{transform:translateX(3px)}
.lboxArrow.is-prev:hover svg{transform:translateX(-3px)}
.lboxClose{position:absolute;top:clamp(14px,2.4vh,26px);right:clamp(12px,3vw,40px)}

@media (max-width:1023px){
  /* nothing to do here any more — the tiles size themselves off their own
     ratio, so the grid reflows without a row height to retune */
}
@media (max-width:767px){
  .galFilters{
    justify-content:flex-start;flex-wrap:nowrap;
    overflow-x:auto;gap:22px;
    /* the strip scrolls sideways rather than wrapping to three ragged rows */
    -webkit-overflow-scrolling:touch;scrollbar-width:none;
  }
  .galFilters::-webkit-scrollbar{display:none}
  .galFilter{white-space:nowrap;flex:0 0 auto}

  .galGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* the plus is a pointer affordance and there is no pointer here */
  .galItem::after{display:none}

  /* the arrows leave the sides and sit under the picture with the counter —
     at this width a 44px disc either side eats a third of the image */
  .lbox{
    grid-template-columns:minmax(0,1fr);
    padding:clamp(56px,9vh,76px) 14px 20px;
  }
  .lboxArrow{position:absolute;bottom:18px;z-index:2}
  .lboxArrow.is-prev{left:20px}
  .lboxArrow.is-next{right:20px}
  .lboxBar{padding:0 66px}
  .lboxImg{max-height:calc(100vh - 210px)}
}


/* ==========================================================================
   29. LOCATION
   ========================================================================== */

.locMapLink{margin-top:clamp(18px,2.4vh,30px)}

/* A live embed, not a picture of a map. It is given a ratio rather than a
   height so it cannot letterbox, and a sand ground so the frame is already
   the right colour while Google's tiles are still arriving. */
.locMap{
  margin-top:var(--head-gap);
  aspect-ratio:2.4/1;width:100%;
  background:var(--sand);overflow:hidden;
}
.locMap iframe{width:100%;height:100%;border:0;display:block}

/* the two inner heads. Centred like the page head above them, and set one
   rank below .pgTitle so they read as parts of this page rather than as the
   openings of three separate ones. */
.locHead{margin-top:calc(var(--block-gap) * 1.6);text-align:center}
.locTitle{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h2);line-height:1.16;letter-spacing:.02em;
  color:var(--heading);
}
.locLead{
  margin:clamp(12px,1.6vh,20px) auto 0;max-width:60ch;
  font-size:var(--body);line-height:var(--body-lh);color:var(--text);
}

/* ---- 29a. getting here ----
   Three routes. --wash-solid boxes, the same fill the mission/vision blocks
   and the amenity tiles use, so this is the page reusing a shape the site
   already owns rather than inventing a fourth kind of panel. */
.locWays{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,1.8vw,30px);
  margin-top:var(--head-gap);
}
.locWay{
  background:var(--wash-solid);
  padding:clamp(24px,2.4vw,36px) clamp(20px,2vw,30px);
  display:flex;flex-direction:column;align-items:flex-start;
}
.locWayIcon{
  width:34px;height:34px;display:grid;place-items:center;flex:none;
  margin-bottom:clamp(14px,1.8vh,20px);
}
.locWayIcon svg{
  width:30px;height:30px;fill:none;stroke:var(--sage);
  stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;
}
.locWayName{
  margin:0;
  font-family:var(--display);font-weight:600;font-size:var(--body-lg);
  letter-spacing:.1em;text-transform:uppercase;line-height:1.3;
  color:var(--heading);
}
.locWayNote{
  margin:clamp(10px,1.4vh,16px) 0 0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
/* the figure closes the box, pinned to its foot so three boxes of unequal
   copy still line their numbers up */
.locWayFig{
  margin-top:auto;padding-top:clamp(16px,2vh,22px);
  font-family:var(--display);font-weight:600;font-size:var(--h4);
  letter-spacing:.04em;color:var(--forest);
}
/* a figure the property has not supplied yet. It is deliberately NOT styled
   to look like the real ones: same slot, muted, italic, so nobody reads it
   as a distance and nobody ships the page thinking it is finished. */
.locWayFig.is-pending{
  font-family:var(--ui);font-style:italic;font-weight:400;
  font-size:var(--body-sm);letter-spacing:0;color:var(--text-muted);
}

/* ---- 29b. the address facts ---- */
.locFacts{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(20px,2.6vw,44px);
  margin-top:calc(var(--block-gap) * 1.2);
  padding-top:clamp(26px,3.2vh,42px);
  border-top:1px solid var(--line);
}
.locFact h3{
  margin:0 0 clamp(10px,1.4vh,15px);
  font-family:var(--ui);font-size:var(--caption);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--text-muted);
}
.locFact p,.locFact address{
  margin:0;font-style:normal;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
.locFact a{
  color:var(--text);
  transition:color 360ms var(--ease-expo);
}
.locFact a:hover,.locFact a:focus-visible{color:var(--orange)}

/* ---- 29c. the attractions rail ----
   The rail component is the homepage's, and the homepage's sits on dark
   footage: ivory type, a dark wash behind the caption, pale dashes and glass
   arrows tinted light. On this page's ivory ground every one of those would
   be invisible. This block restates the palette for a light ground and
   changes nothing about the geometry or the JS. */
.locRail{margin-top:var(--head-gap);max-height:none}
/* the homepage rail lives in a fixed-height stage and stretches to fill it;
   here nothing sets a height, so the cards have to size to their content */
.locRail .roomsViewport{height:auto;flex:0 0 auto}
.locRail .roomsTrack{height:auto;align-items:stretch}
.locRail .roomCard{
  background:var(--card);
  -webkit-backdrop-filter:none;backdrop-filter:none;
  border:1px solid var(--line);
  transition:border-color 500ms var(--ease-expo);
}
.locRail .roomCard:hover{background:var(--card);border-color:var(--sage)}
/* a stated ratio instead of flex:1 — with no stage height to divide up, the
   pictures would otherwise collapse to their 120px minimum */
.locRail .roomCardPic{
  flex:0 0 auto;aspect-ratio:1.5/1;min-height:0;
  background:var(--wash-solid);
}
.locRail .roomCardBody{
  display:flex;flex-direction:column;align-items:flex-start;flex:1 1 auto;
}
.locRail .roomCardName{font-size:var(--h4);color:var(--heading)}
.locRail .roomCardNote{
  color:var(--text);-webkit-line-clamp:3;max-width:none;
}
/* the distance, closing the card the way .locWayFig closes a route box */
.attrDist{
  margin-top:auto;padding-top:clamp(12px,1.6vh,18px);
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--forest);
}
/* an attraction the property has no photograph of. The site's own answer to
   a missing picture — the wash block it uses everywhere else — rather than a
   stock photograph of somewhere that is not the place named. */
.locRail .roomCardPic.is-placeholder{
  background:var(--wash-solid) url("../assets/images/wash-block.png") center / 82% auto no-repeat;
  opacity:.9;
}
.locRail .roomsCount{color:var(--text-muted)}
.locRail .roomsCount b{color:var(--heading)}
.locRail .roomsDot{background:rgba(64,60,49,.24)}
.locRail .roomsDot:hover{background:rgba(64,60,49,.5)}
.locRail .roomsDot.is-active{background:var(--forest)}
.locRail .roomsArrow{
  color:var(--earth);
  background:rgba(39,39,39,.06);
  -webkit-backdrop-filter:blur(12px) saturate(1.1);
  backdrop-filter:blur(12px) saturate(1.1);
  border:1px solid var(--line);
  box-shadow:none;
}
.locRail .roomsArrow:hover:not(:disabled),
.locRail .roomsArrow:focus-visible:not(:disabled){
  background:var(--forest);border-color:var(--forest);color:var(--ivory);
}

@media (max-width:1023px){
  .locMap{aspect-ratio:1.7/1}
  .locWays{grid-template-columns:minmax(0,1fr)}
  .locFacts{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* the boxes are stacked now, so the figure no longer has to hang at the
     foot to line up with anything */
  .locWayFig{margin-top:clamp(14px,2vh,20px)}
}
@media (max-width:767px){
  .locMap{aspect-ratio:1.15/1}
  .locFacts{grid-template-columns:minmax(0,1fr);gap:26px}
  .locLead{max-width:none}
}
/* an experience with no photograph yet. The site's own wash block, not a
   stock picture of something that is not the thing named. */
.facCardPic.is-placeholder{
  background:var(--wash-solid) url("../assets/images/wash-block.png") center / 74% auto no-repeat;
  opacity:.9;
}

@media (max-width:1023px){
}
@media (max-width:767px){
}


/* ==========================================================================
   31. LEGAL — terms, and any other page that is a document rather than a view
   A column of running text, so the only things that matter are the measure,
   the space between clauses, and being able to find one answer without
   reading the other eight sections. No cards, no boxes, no columns: this is
   the one page on the site nobody browses.
   ========================================================================== */

/* 72ch, not the .pgLead's 64. A lead is three lines under a title and wants
   to be narrow; this runs for two screens, and a short measure over that
   length turns into a ladder of thirty near-identical lines. */
.legalDoc{
  max-width:72ch;margin:var(--head-gap) auto 0;
  text-align:left;
}

/* Space does the separating, and it is deliberately uneven: a large gap
   ABOVE the heading and a small one below it, so each heading belongs to the
   clauses under it rather than floating between two blocks. A rule between
   sections would have made this a table of terms, which reads as
   small print. */
.legalBlock + .legalBlock{margin-top:clamp(38px,5vh,64px)}
.legalHead{
  margin:0 0 clamp(14px,1.9vh,22px);
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.2;letter-spacing:.01em;
  color:var(--heading);
}

.legalList{margin:0;padding:0;list-style:none}
.legalList li{
  position:relative;
  padding-left:26px;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
.legalList li + li{margin-top:clamp(10px,1.4vh,15px)}
/* a square, because the site has no round corners anywhere else. Sized and
   positioned off the line box rather than the baseline, so it sits on the
   optical centre of the first line at any font size. */
.legalList li::before{
  content:"";position:absolute;left:2px;top:calc(.9em - 2px);
  width:5px;height:5px;background:var(--sage);
}
/* the clause's own subject — "No-show.", "American Express is not accepted."
   Weight rather than colour: this page is read for one line in ten, and the
   bold is what lets someone scan for theirs. */
.legalList b{font-weight:600;color:var(--heading)}

/* consecutive paragraphs need air between them — .legalNote was written
   for one standalone line under a list and carries margin:0 */
.legalNote + .legalNote,
.legalNote + .legalList,
.legalList + .legalNote{margin-top:clamp(12px,1.7vh,18px)}
.legalNote{
  margin:0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
.legalNote a,.legalList a{
  color:var(--text);
  border-bottom:1px solid var(--line);
  transition:color 360ms var(--ease-expo),border-color 360ms var(--ease-expo);
}
.legalNote a:hover,.legalList a:hover,
.legalNote a:focus-visible,.legalList a:focus-visible{
  color:var(--orange);border-bottom-color:var(--orange);
}

/* content the property still owes. It is set apart from the terms around it
   on purpose — indented behind a rule, italic, muted — so that it can never
   be mistaken for a clause that is in force. */
.legalPending{
  margin:0;padding:clamp(14px,1.8vh,20px) 0 clamp(14px,1.8vh,20px) clamp(18px,2vw,26px);
  border-left:2px solid var(--line);
  font-family:var(--ui);font-style:italic;
  font-size:var(--body-sm);line-height:1.65;color:var(--text-muted);
}

@media (max-width:767px){
  .legalDoc{max-width:none}
  .legalList li{padding-left:22px}
}


/* ==========================================================================
   32. CONTACT — the details block above the form
   A photograph and a card, side by side. The picture is slightly the wider
   of the two: the card is a fixed amount of text that stops wherever it
   stops, and giving it the larger half would have left a column of white
   under the last phone number.
   ========================================================================== */
.ctIntro{
  display:grid;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:clamp(20px,2.6vw,48px);
  align-items:stretch;
  margin-top:var(--head-gap);
}
/* stretch, not center: the two halves are meant to draw one rectangle
   across the page, and a card shorter than its picture reads as a card that
   has come loose from it */
/* the picture fills the row but never sets it: in flow, its own 3:2 height
   made the row ~950px tall and left dead space under the card */
.ctPic{position:relative;overflow:hidden;background:var(--sand);min-height:clamp(300px,38vh,470px)}
.ctPic img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}

.ctCard{
  background:var(--card);
  border:1px solid var(--line);
  padding:clamp(26px,3vw,46px);
  display:flex;flex-direction:column;
}
.ctName{
  margin:0 0 clamp(20px,2.6vh,30px);
  padding-bottom:clamp(16px,2.2vh,24px);
  border-bottom:1px solid var(--line);
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.18;letter-spacing:.02em;
  color:var(--heading);
}

.ctList{margin:0}
.ctRow + .ctRow{margin-top:clamp(18px,2.4vh,28px)}
/* the same label the location page's fact columns use — uppercase Montserrat
   at caption size, muted. A contact detail is a labelled value, which is a
   different thing from the room specs and is set differently on purpose. */
.ctRow dt{
  margin:0 0 clamp(6px,.9vh,10px);
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--text-muted);
}
.ctRow dd{
  margin:0;
  font-size:var(--body-sm);line-height:1.65;color:var(--text);
}
.ctRow dd a{
  color:var(--text);
  border-bottom:1px solid transparent;
  transition:color 360ms var(--ease-expo),border-color 360ms var(--ease-expo);
}
.ctRow dd a:hover,.ctRow dd a:focus-visible{
  color:var(--orange);border-bottom-color:var(--orange);
}
/* the inline Map link, set apart from the address it follows by a dot rather
   than by a line break — it is a control, not another line of the address */
.ctMap{
  margin-left:14px;position:relative;
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;
  border-bottom:1px solid var(--line) !important;
}
.ctMap::before{
  content:"·";position:absolute;left:-11px;
  color:var(--text-muted);border:0;
}

@media (max-width:1023px){
  /* stacked, the picture leads and takes a stated ratio — with no card
     beside it any more there is nothing for min-height to match */
  .ctIntro{grid-template-columns:minmax(0,1fr)}
  .ctPic{min-height:0;aspect-ratio:1.9/1}
}
@media (max-width:767px){
  .ctPic{aspect-ratio:1.5/1}
}


/* ==========================================================================
   33. SITEMAP
   An index read band by band. Each group is one full-width row: its number,
   name and a line of what is in it on the left, its pages on the right in up
   to three columns. A band is only as tall as its own list, so a long group
   (Stay) no longer leaves the short ones beside it standing in empty space.

   Two kinds of link and only two: a section page in the display face, and
   its sub-pages under it in the UI face. The one rule on the page is the
   hairline between bands; links carry none, and their arrow only appears
   on hover. Also used by 404.html.
   ========================================================================== */
.smapIndex{margin-top:var(--head-gap)}
.smapRow{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,2.7fr);
  gap:clamp(20px,3vw,56px);
  padding:clamp(30px,4.4vh,52px) 0;
}
.smapRow:first-child{padding-top:0}
.smapRow + .smapRow{border-top:1px solid var(--line)}
.smapNum{
  display:block;margin-bottom:clamp(10px,1.4vh,16px);
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.2em;color:var(--botanical);
}
.smapTitle{
  margin:0;
  font-family:var(--display);font-weight:400;
  font-size:var(--h3);line-height:1.15;letter-spacing:.03em;
  color:var(--heading);
}
.smapNote{
  margin:clamp(8px,1.1vh,12px) 0 0;max-width:28ch;
  font-size:var(--body-sm);line-height:1.6;color:var(--text-muted);
}

.smapSets,.smapList{margin:0;padding:0;list-style:none}
.smapSets{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(18px,2.4vh,26px) clamp(20px,2.6vw,44px);
  align-items:start;
  /* the band stretches this to the label's height; the rows must not follow */
  align-content:start;
}
.smapTop,.smapList a{
  display:flex;width:fit-content;align-items:center;gap:10px;
  transition:color 380ms var(--ease-expo);
}
.smapTop{
  font-family:var(--display);font-weight:400;
  font-size:var(--h4);line-height:1.25;letter-spacing:.03em;
  color:var(--heading);
}
.smapList{margin-top:clamp(10px,1.4vh,14px)}
.smapList li + li{margin-top:8px}
.smapList a{font-size:var(--body-sm);line-height:1.45;color:var(--text)}
/* the arrow: a 1.5px line icon like the header's, masked so it takes colour;
   hidden at rest so the page carries no marks beyond the words */
.smapTop::after,.smapList a::after{
  content:"";flex:none;width:15px;height:15px;
  background-color:currentColor;opacity:0;transform:translateX(-4px);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center/contain no-repeat;
  transition:transform 380ms var(--ease-expo),opacity 380ms var(--ease-expo),background-color 380ms var(--ease-expo);
}
.smapTop:hover,.smapTop:focus-visible,
.smapList a:hover,.smapList a:focus-visible{color:var(--forest)}
.smapTop:hover::after,.smapTop:focus-visible::after,
.smapList a:hover::after,.smapList a:focus-visible::after{
  opacity:1;background-color:var(--orange);transform:none;
}

/* the page you are already on: named, not offered, so a label in place of the arrow */
.smapIndex a[aria-current]{color:var(--text-muted)}
.smapIndex a[aria-current]::after{
  content:"This page";width:auto;height:auto;opacity:1;transform:none;
  -webkit-mask:none;mask:none;background:none;
  font-family:var(--ui);font-size:var(--caption-xs);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;
}

@media (max-width:1023px){
  .smapRow{grid-template-columns:minmax(0,1fr);gap:clamp(16px,2.4vh,24px)}
  .smapNote{max-width:52ch}
}
@media (max-width:767px){
  .smapSets{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:520px){
  .smapSets{grid-template-columns:minmax(0,1fr)}
}

/* ==========================================================================
   CLIENT ROUND 2 (2026-10-29)
   ========================================================================== */
/* FAQs, Blogs and Terms live in the footer's quick links from 768px up, so
   the drawer drops them there. Below 768px the footer hides its quick links,
   so the drawer keeps them — the two breakpoints meet exactly. Twelve links
   regroup as 3 x 4 on wide screens and 2 x 6 on tablets. */
@media (min-width:768px){
  .navList li.navFooterDup{display:none}
  .navList{grid-template-rows:repeat(6,auto)}
}
@media (min-width:1280px){
  .navList{grid-template-rows:repeat(4,auto)}
}

/* the hero film's pause/play control — a sibling of the transformed image
   layer, like the booking bar, so it keeps its own pointer events */
.heroVidToggle{
  position:absolute;z-index:12;
  top:calc(var(--header-h) + 18px);right:var(--gutter);
  width:44px;height:44px;border-radius:50%;padding:0;cursor:pointer;
  display:grid;place-items:center;
  background:rgba(10,10,10,.44);color:var(--ivory);
  border:1px solid rgba(245,241,232,.32);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  transition:background 460ms var(--ease-expo),border-color 460ms var(--ease-expo);
}
.heroVidToggle:hover,.heroVidToggle:focus-visible{background:rgba(10,10,10,.66);border-color:var(--ivory)}
.heroVidToggle svg{width:16px;height:16px;fill:currentColor}
.heroVidToggle .icoPlay{display:none}
.heroVidToggle.is-paused .icoPlay{display:block}
.heroVidToggle.is-paused .icoPause{display:none}
/* the film's seek bar and running time, the same glass pill to the toggle's left */
.heroVidBar{
  position:absolute;z-index:12;
  top:calc(var(--header-h) + 18px);right:calc(var(--gutter) + 54px);
  height:44px;padding:0 16px;border-radius:22px;
  display:flex;align-items:center;gap:12px;
  background:rgba(10,10,10,.44);color:var(--ivory);
  border:1px solid rgba(245,241,232,.32);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  font-family:var(--ui);font-size:12px;letter-spacing:.06em;font-variant-numeric:tabular-nums;
}
.heroVidSeek{
  -webkit-appearance:none;appearance:none;margin:0;padding:0;
  width:clamp(96px,14vw,200px);height:24px;background:transparent;cursor:pointer;
}
.heroVidSeek::-webkit-slider-runnable-track{
  height:2px;border-radius:2px;
  background:linear-gradient(to right,var(--ivory) var(--p,0%),rgba(245,241,232,.3) var(--p,0%));
}
.heroVidSeek::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;margin-top:-5px;border-radius:50%;background:var(--ivory);border:0}
.heroVidSeek::-moz-range-track{height:2px;border-radius:2px;background:rgba(245,241,232,.3)}
.heroVidSeek::-moz-range-progress{height:2px;border-radius:2px;background:var(--ivory)}
.heroVidSeek::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:var(--ivory);border:0}
.heroVidSeek:focus-visible{outline:1px solid var(--ivory);outline-offset:4px}
@media (max-width:479px){
  .heroVidBar{padding:0 14px}
  .heroVidTime{display:none}
}

/* the gallery band's way in: the lockup lets the pointer through to the
   frames, so the button takes it back */
.cloudActions{margin-top:clamp(16px,1.8vw,26px);pointer-events:auto}

/* the enquiry forms' date field: the browser's grey calendar glyph swapped
   for the site's own line icon in forest green (the pop-up calendar itself
   is drawn by the browser and cannot be themed without a picker library) */
.evField input[type="date"]::-webkit-calendar-picker-indicator{
  width:20px;height:20px;cursor:pointer;opacity:1;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231E5536' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='1.5'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4'/%3E%3C/svg%3E") center/20px 20px no-repeat;
  color:transparent;
}
.evField input[type="number"]{-moz-appearance:textfield}

/* ---- the date pickers (flatpickr), drawn in the site's own hand ----
   the visible field is flatpickr's text twin of the date input; it carries
   the same calendar line icon the native fields had */
.heroBookField input.datePick{
  padding-right:22px;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F5F1E8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='1.5'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4'/%3E%3C/svg%3E") right center/16px 16px no-repeat;
}
.evField input.datePick{
  cursor:pointer;padding-right:44px;
  background:var(--white) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231E5536' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='1.5'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4'/%3E%3C/svg%3E") right 14px center/20px 20px no-repeat;
}
.flatpickr-calendar{
  font-family:var(--ui);background:var(--ivory);border-radius:0;
  border:1px solid var(--line);box-shadow:0 18px 48px rgba(15,59,39,.16);
}
.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after,
.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{display:none}
/* opening upward from the booking bar, it clears the field label */
.flatpickr-calendar.arrowBottom{margin-top:-22px}
.flatpickr-months{padding:6px 0 2px}
.flatpickr-months .flatpickr-month{color:var(--earth);fill:var(--earth);height:40px}
.flatpickr-current-month{font-size:15px;padding-top:9px}
.flatpickr-current-month span.cur-month,
.flatpickr-current-month input.cur-year{font-family:var(--display);font-weight:500;letter-spacing:.06em;color:var(--earth)}
.flatpickr-current-month span.cur-month:hover,
.flatpickr-current-month .numInputWrapper:hover{background:transparent}
.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{top:6px;padding:10px 12px;color:var(--forest)}
.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
}
.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:none;stroke:var(--orange)}
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,.flatpickr-months .flatpickr-next-month.flatpickr-disabled{opacity:.3}
span.flatpickr-weekday{
  font-family:var(--ui);font-size:10px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--text-muted);
}
.flatpickr-day{border-radius:0;color:var(--text);font-size:13px}
.flatpickr-day:hover,.flatpickr-day:focus{background:var(--wash-solid);border-color:var(--wash-solid)}
.flatpickr-day.today{border-color:var(--sage)}
.flatpickr-day.today:hover,.flatpickr-day.today:focus{background:var(--wash-solid);border-color:var(--sage);color:var(--text)}
.flatpickr-day.selected,.flatpickr-day.selected:hover,.flatpickr-day.selected:focus{
  background:var(--forest);border-color:var(--forest);color:var(--ivory);
}
.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover,
.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay{color:rgba(39,39,39,.28)}
.evField input[type="number"]::-webkit-inner-spin-button,
.evField input[type="number"]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}

/* phone cards are narrower than the note's 34ch, so the clamps that suit a
   desktop rail cut real copy short there; give the notes the lines they need */
@media (max-width:767px){
  .roomCardNote{-webkit-line-clamp:4}
  .locRail .roomCardNote{-webkit-line-clamp:5}
}

/* ---- round 4e: phone tap targets and the facilities list ----
   hit areas are invisible pseudo-elements, so nothing moves on screen */
.menuController::after{content:"";position:absolute;inset:-17px -13px}
.rgalDots button{position:relative}
.rgalDots button::after{content:"";position:absolute;left:-4px;right:-4px;top:-14px;bottom:-14px}
.ctMap::after{content:"";position:absolute;inset:-14px -8px}
@media (max-width:767px){
  /* a picture and a name per card: two to a row halves the scroll */
  #pgFacilities .facGrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
  #pgFacilities .facCardName{font-size:15px;line-height:1.3}
}

/* ---- round 5 ----
   the hero film's controls ride the foot of the hero, just above the booking
   pane's right end, not under the header's Book now. No glass pill: a slim line,
   the running time, and the round button. The pane is 101px tall at every width. */
.heroVidToggle,.heroVidBar{top:auto;bottom:calc(clamp(20px,4vh,40px) + 115px)}
.heroVidToggle{right:max(var(--gutter),(100% - 1060px) / 2)}
.heroVidBar{
  right:calc(max(var(--gutter),(100% - 1060px) / 2) + 52px);
  padding:0;background:none;border:0;border-radius:0;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  text-shadow:0 1px 6px rgba(0,0,0,.55);
}
.heroVidSeek{width:clamp(96px,11vw,160px);filter:drop-shadow(0 1px 3px rgba(0,0,0,.45))}
@media (max-width:767px){
  /* no booking pane on a phone: the controls sit in the corner of the fold */
  .heroVidToggle,.heroVidBar{bottom:calc(22px + env(safe-area-inset-bottom,0px))}
  .heroVidToggle{right:var(--gutter)}
  .heroVidBar{right:calc(var(--gutter) + 52px);padding:0}
}

/* one style for a link inside running text, sitewide: forest with a light
   underline, orange on hover. Standalone text links (Know more, All rooms,
   the sitemap, the footer) keep no underline and share the same orange hover. */
:is(p,dd,.legalList li) > a:not([class]){
  color:var(--forest);border-bottom:0;
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;
  text-decoration-color:rgba(30,85,54,.4);
  transition:color 360ms var(--ease-expo),text-decoration-color 360ms var(--ease-expo);
}
:is(p,dd,.legalList li) > a:not([class]):hover,
:is(p,dd,.legalList li) > a:not([class]):focus-visible{color:var(--orange);text-decoration-color:var(--orange)}
.swooshLink{transition:color 400ms var(--ease-expo)}
.swooshLink:hover,.swooshLink:focus-visible{color:var(--orange)}

/* FAQ groups */
.faqGroup{display:flex;max-width:min(880px,100%);margin:clamp(40px,5vh,64px) auto 0}
.faqGroup + .faqList{margin-top:clamp(16px,2vh,22px)}

/* round 5: the homepage and location rail dots are 2px tall; an invisible hit area, nothing moves */
.roomsDot{position:relative}
.roomsDot::after{content:"";position:absolute;left:-4px;right:-4px;top:-14px;bottom:-14px}


/* ==========================================================================
   ROUND 7 (client, 2026-10-30) - mobile reading and scroll length
   Centred lines of six or seven words leave every line starting somewhere
   new; on a phone the reading blocks start at the gutter instead. Copy laid
   over a photograph (the cloud band) and the testimonial quote stay centred.
   ========================================================================== */
@media (max-width:767px){
  .pgTitleWrap,.amenHead,.locHead,.evForm,.svcHead,.csrHead{text-align:left}
  .pgLead,.pgTagline,.amenLead,.locLead,.csrHead .secLead{margin-left:0;margin-right:0}
  .csrHead{margin-left:0}
  .svcHead{align-items:flex-start}
  .pgCta{justify-content:flex-start}
  /* the room's four facts as a left-set two-column list */
  .specStrip{justify-content:flex-start}
  .specStrip div{text-align:left;padding:0 12px 0 0}
  /* Amenities: seventeen 120px tiles ran to about 1,100px. As icon-and-label
     rows two across, the list is about half that and still scans as a table. */
  .amenGrid{gap:8px}
  .amenTile{
    flex-direction:row;justify-content:flex-start;align-items:center;
    text-align:left;gap:10px;min-height:52px;padding:10px 12px;
  }
  .amenTile svg{width:20px;height:20px}
  .amenTile span{letter-spacing:.08em}
}


/* ---- round 8 (client, 2026-10-31) ----
   the film's controls span the booking pane, its left and right edges exactly:
   the round button on the left, the seek line filling the row, the time on the right */
.heroVidToggle{left:max(var(--gutter),(100% - 1060px) / 2);right:auto}
.heroVidBar{
  left:calc(max(var(--gutter),(100% - 1060px) / 2) + 60px);
  right:max(var(--gutter),(100% - 1060px) / 2);
  gap:16px;font-size:13px;
}
.heroVidSeek{flex:1;width:auto;min-width:0}
.heroVidSeek::-webkit-slider-runnable-track{height:3px}
.heroVidSeek::-webkit-slider-thumb{width:14px;height:14px;margin-top:-5.5px}
.heroVidSeek::-moz-range-track,.heroVidSeek::-moz-range-progress{height:3px}
.heroVidSeek::-moz-range-thumb{width:14px;height:14px}
@media (max-width:767px){
  .heroVidToggle{left:var(--gutter);right:auto}
  .heroVidBar{left:calc(var(--gutter) + 60px);right:var(--gutter)}
  .heroVidTime{display:inline}
}
