/* ═══════════════════════════════════════════════════════════════
   PÄÄSKYLÄNRINNE 6 — design system
   Taken from the room rather than imposed on it:
     ink    the dark merbau floor
     bone   the sloped white ceilings under the roof
     amber  the pine stair, its LED strip, the leather pulls
     clay   the exposed brick wall
     slate  the Baltic in February, used sparingly and cold
   The photography is already warm and red-heavy, so amber stays the
   accent: a red accent would compete with the brick instead of
   sitting on it. Type: Cardinal / Neue Haas Grotesk / FH Alpha
   (licensed — see brand-fonts.css) over free equivalents.
   ═══════════════════════════════════════════════════════════════ */

:root{
  --ink:      #17150f;
  --ink-2:    #211e15;
  --bone:     #f0eae0;
  --bone-2:   #e4dccd;
  --amber:    #c68a3f;
  --amber-2:  #e2ab55;
  /* was a tropical green; pulled towards cold Baltic water */
  --petrol:   #2f5560;
  --petrol-d: #1f3a42;
  --clay:     #9c5b41;
  --t-ink-60: rgba(23,21,15,.6);
  --t-bone-2: rgba(240,234,224,.16);

  /* Brand faces first, closest free equivalents behind them.
     See css/brand-fonts.css to activate the licensed files. */
  --font-display: 'Cardinal', 'Instrument Serif', Georgia, serif;
  --font-body: 'Plus Jakarta Sans', 'Neue Haas Grotesk', system-ui, sans-serif;
  /* label/data face: no longer a monospace, which read as typewriter */
  --font-mono: 'FH Alpha', 'Plus Jakarta Sans', system-ui, sans-serif;

  --wrap: 1200px;
  --pad: clamp(1.25rem, 4vw, 3rem);
  --sec: clamp(5rem, 10vw, 9rem);
  --r: 4px;
}

*,*::before,*::after{ box-sizing:border-box; margin:0; padding:0; }
[hidden]{ display:none !important; }
html{ scroll-behavior:smooth; }
body{
  font-family:var(--font-body);
  font-weight:400;
  color:var(--ink);
  background:var(--bone);
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
}
img{ max-width:100%; display:block; }
a{ color:inherit; }
button{ font:inherit; cursor:pointer; }
:focus-visible{ outline:2px solid var(--amber); outline-offset:3px; }

.mono{ font-family:var(--font-mono); font-weight:500; font-size:.78rem; letter-spacing:.08em;
  font-variant-numeric:tabular-nums; }
.wrap{ max-width:var(--wrap); margin-inline:auto; padding-inline:var(--pad); }

h1,h2,h3{ font-family:var(--font-display); font-weight:400; line-height:1.08; }
h2{ font-size:clamp(2rem, 4.6vw, 3.4rem); letter-spacing:-.01em; }
h3{ font-size:clamp(1.3rem, 2.4vw, 1.7rem); }
h4{ font-weight:600; font-size:1.02rem; }

.eyebrow{
  color:var(--clay);
  text-transform:lowercase;
  margin-bottom:1rem;
  font-size:.82rem;
}
.eyebrow span{ color:inherit; opacity:.55; }
.sec-head{ max-width:44rem; margin-bottom:clamp(2.5rem,6vw,4.5rem); }
/* the village headline needs the extra measure to stay on one line */
.sec-head--wide{ max-width:60rem; }
.sec-head__sub{ margin-top:1.1rem; font-size:1.05rem; opacity:.82; max-width:38rem; }
.sec-head--light .eyebrow{ color:var(--amber-2); }

/* ── buttons ─────────────────────────────────────────────── */
.btn{
  display:inline-flex; align-items:center; gap:.5rem;
  font-family:var(--font-mono); font-size:.8rem; letter-spacing:.06em;
  padding:.85rem 1.6rem; border-radius:var(--r);
  text-decoration:none; border:1px solid transparent;
  transition:background .25s, color .25s, border-color .25s, transform .2s;
}
.btn--solid{ background:var(--amber); color:var(--ink); border-color:var(--amber); }
.btn--solid:hover{ background:var(--amber-2); border-color:var(--amber-2); transform:translateY(-2px); }
.btn--ghost{ border-color:var(--t-bone-2); color:var(--bone); background:rgba(23,21,15,.25); backdrop-filter:blur(6px); }
.btn--ghost:hover{ border-color:var(--bone); }
.btn--line{ border-color:rgba(23,21,15,.28); color:var(--ink); background:transparent; padding:.55rem 1.1rem; }
.btn--line:hover{ background:var(--ink); color:var(--bone); border-color:var(--ink); }
.btn--lg{ padding:1.05rem 2.1rem; font-size:.88rem; }

/* ── candi bentar gate ───────────────────────────────────── */
.gate{ position:fixed; inset:0; z-index:100; display:flex; pointer-events:none; }
/* --gate-fade is animated from 100% (hard seam, halves read as one gate) to
   80% as they part, dissolving the inner 20% so no clean edge floats away. */
.gate__half{
  position:relative; width:50%; height:100%; overflow:hidden;
  --gate-fade:100%;
}
.gate__half img{ width:100%; height:100%; object-fit:cover; }
/* Anchor each photo's split edge to the inner edge of its panel, so the two
   halves read as one candi bentar before they part. */
.gate__half--left img{
  object-position:right center;
  -webkit-mask-image:linear-gradient(to right, #000 var(--gate-fade), transparent 100%);
          mask-image:linear-gradient(to right, #000 var(--gate-fade), transparent 100%);
}
.gate__half--right img{
  object-position:left center;
  -webkit-mask-image:linear-gradient(to left, #000 var(--gate-fade), transparent 100%);
          mask-image:linear-gradient(to left, #000 var(--gate-fade), transparent 100%);
}
.gate__word{
  position:absolute; inset:0; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:1rem; color:var(--bone);
  text-shadow:0 2px 24px rgba(12,11,8,.85);
}
/* The lit stonework meets in the middle — a soft scrim keeps the mark legible */
.gate__word::before{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(ellipse 46% 26% at 50% 50%, rgba(12,11,8,.82), rgba(12,11,8,0) 72%);
}
.gate__om, .gate__name{ position:relative; }
.gate__om{ font-family:var(--font-mono); font-size:.8rem; letter-spacing:.35em; text-transform:uppercase; color:var(--amber-2); }
/* a line of speech, not a wordmark: the wide logotype tracking is dropped so
   it reads as a sentence, and it holds one line down to the smallest screens */
.gate__name{
  font-family:var(--font-display); font-size:clamp(1.5rem,4.6vw,3.5rem);
  letter-spacing:.05em; line-height:1.1;
  max-width:92vw; text-align:center; text-wrap:balance;
}

/* ── nav ─────────────────────────────────────────────────── */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:60;
  display:flex; align-items:center; gap:2rem;
  padding:1.1rem var(--pad);
  color:var(--bone);
  transition:background .35s, box-shadow .35s, padding .35s;
}
.nav.is-scrolled{ background:rgba(23,21,15,.9); backdrop-filter:blur(10px); padding-block:.7rem;
  box-shadow:0 1px 0 rgba(241,234,217,.08); }
.nav__brand{ font-family:var(--font-display); letter-spacing:.24em; font-size:1.05rem; text-decoration:none; white-space:nowrap; }
.nav__links{ display:flex; gap:1.6rem; margin-left:auto; }
.nav__links a{
  font-family:var(--font-mono); font-size:.74rem; letter-spacing:.08em;
  text-decoration:none; opacity:.8; text-transform:uppercase;
  transition:opacity .2s;
}
.nav__links a:hover{ opacity:1; }
.nav__cta{
  font-family:var(--font-mono); font-size:.74rem; letter-spacing:.06em; text-transform:uppercase;
  text-decoration:none; border:1px solid var(--amber); color:var(--amber-2);
  padding:.55rem 1rem; border-radius:var(--r); white-space:nowrap;
  transition:background .25s, color .25s;
}
.nav__cta:hover{ background:var(--amber); color:var(--ink); }
.nav__cta-tag{ opacity:.7; }
.nav__portal{
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.06em; text-transform:uppercase;
  text-decoration:none; color:var(--bone); border:1px solid rgba(241,234,217,.32);
  padding:.55rem 1rem; border-radius:99px; white-space:nowrap; opacity:.85;
  transition:background .25s, border-color .25s, opacity .25s;
}
.nav__portal:hover{ background:rgba(241,234,217,.12); border-color:var(--bone); opacity:1; }
.nav__burger{ display:none; background:none; border:0; width:40px; height:36px;
  flex-direction:column; gap:9px; align-items:center; justify-content:center; }
.nav__burger span{ display:block; width:24px; height:1.5px; background:var(--bone); transition:transform .3s, opacity .3s; }
.nav__burger.is-open span:first-child{ transform:translateY(5.25px) rotate(45deg); }
.nav__burger.is-open span:last-child{ transform:translateY(-5.25px) rotate(-45deg); }

/* Nine links at clamp(1.8rem,7vw,2.6rem) come to 588px. That fits a 667px
   phone and does not fit a 568px one, a landscape one, or an upright one with
   a keyboard or a fat browser chrome. It used to be centred with no overflow
   rule, which failed twice over: the menu could not scroll, and `justify-
   content:center` pushes the surplus out of BOTH ends, so the first link went
   above the top of the screen where no amount of scrolling would ever have
   reached it. At 375x500 that hid "The loft" and "Guest portal" completely.

   The centring is done with auto margins on the nav instead. Auto margins take
   up the slack when there is slack and collapse to nothing when there is not,
   so the layout is identical on a roomy phone and simply becomes scrollable on
   a cramped one. The nav's bottom auto margin is also what keeps the footer
   line at the bottom of the screen, which is why the footer no longer needs to
   be positioned absolutely — and must not be, since an absolutely positioned
   child of a scroll container sits over the content as it scrolls past. */
.mobile-menu{
  position:fixed; inset:0; z-index:55; background:var(--ink); color:var(--bone);
  display:flex; flex-direction:column;
  /* top padding clears the fixed header, which sits above this at z-index 60 */
  padding:calc(var(--pad) + 3.25rem) var(--pad) var(--pad);
  transform:translateY(-100%); visibility:hidden;
  transition:transform .45s cubic-bezier(.7,0,.2,1), visibility 0s .45s;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  /* without this, flicking past the end of the menu scrolls the page behind it,
     so closing the menu returns you somewhere you never chose to be */
  overscroll-behavior:contain;
}
.mobile-menu.is-open{ transform:translateY(0); visibility:visible; transition-delay:0s; }
/* The header sits above the menu at z-index 60 so the close button stays put.
   Now that the menu scrolls under it, it needs something behind it: without
   this the links slide straight through the wordmark on their way past. The
   header is earlier in the document than the menu, so this cannot be a sibling
   selector and is hung off the body instead. */
body:has(.mobile-menu.is-open) .nav{ background:var(--ink); }
.mobile-menu nav{ display:flex; flex-direction:column; gap:.4rem; margin-block:auto; }
.mobile-menu a{
  font-family:var(--font-display); font-size:clamp(1.8rem,7vw,2.6rem);
  text-decoration:none; padding-block:.35rem; border-bottom:1px solid rgba(241,234,217,.08);
}
/* In normal flow, not absolute: the nav's auto bottom margin already holds it
   against the bottom of the screen, and an absolute child would ride over the
   links once the menu starts scrolling. */
.mobile-menu__foot{ margin-top:1.5rem; opacity:.6; }

/* ── hero ────────────────────────────────────────────────── */
/* The video never moves; the hero text and every section below it scroll
   up over the top, so the page reveals itself like a rising curtain. */
.herofix{ position:fixed; inset:0; z-index:0; overflow:hidden; pointer-events:none; }
#smooth-wrapper{ position:relative; z-index:1; }
.hero{ position:relative; height:100svh; min-height:600px;
  display:flex; align-items:flex-end; color:var(--bone); background:transparent; }
.hero__video{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.hero__shade{ position:absolute; inset:0;
  background:linear-gradient(to bottom, rgba(23,21,15,.42) 0%, rgba(23,21,15,.12) 40%, rgba(23,21,15,.78) 100%); }
.hero__content{ position:relative; z-index:2; width:100%; max-width:var(--wrap);
  margin-inline:auto; padding:0 var(--pad) 7.5rem; }
.hero__eyebrow{ letter-spacing:.28em; text-transform:uppercase; color:var(--amber-2);
  margin-bottom:1.2rem; text-shadow:0 1px 14px rgba(23,21,15,.65); }
.hero__title{ line-height:0; margin:0; }
/* the hand-drawn mark, lifted off the footage by a dark shadow plus a soft
   warm bloom so the thin strokes hold on any frame */
.hero__markwrap{ position:relative; display:inline-block; line-height:0; }
.hero__mark{
  width:min(100%, 1040px); height:auto; display:block;
  filter:drop-shadow(0 3px 16px rgba(12,11,8,.72))
         drop-shadow(0 0 48px rgba(241,234,217,.3));
}
/* the sister-villa mark: the script logotype ends at ~96.5% with a low tail, so
   One nestles into the pocket beneath it, in amber like the nav pill */
.hero__one{
  position:absolute; right:-1%; bottom:1%;
  width:clamp(4.5rem, 13vw, 9rem); height:auto;
  filter:drop-shadow(0 2px 14px rgba(12,11,8,.7)) drop-shadow(0 0 26px rgba(226,171,85,.3));
  pointer-events:none;
}
/* the line sits against the buttons rather than across the hero from them */
.hero__row{
  display:flex; align-items:flex-end; justify-content:flex-end;
  gap:2rem; flex-wrap:wrap; margin-top:1.6rem;
}
.hero__line{
  font-size:clamp(1.05rem, 2vw, 1.3rem); font-weight:300; max-width:26rem;
  text-align:right;
}
.hero__actions{ display:flex; gap:.9rem; flex-wrap:nowrap; align-items:stretch; }
.hero__rail{
  position:absolute; z-index:2; right:calc(var(--pad) / 2); top:50%;
  transform:translateY(-50%); writing-mode:vertical-rl;
  letter-spacing:.3em; opacity:.55; color:var(--bone);
}
.hero__foot{
  position:absolute; z-index:2; left:0; right:0; bottom:0;
  display:flex; justify-content:space-between; align-items:center; gap:1rem;
  padding:1.2rem var(--pad);
  border-top:1px solid rgba(241,234,217,.16);
  max-width:none;
}
.hero__foot .mono{ opacity:.75; }

/* ── ambient soundtrack toggle ────────────────────────────
   See-through disc above the hero footer. The bars run only while
   audio is actually playing, so the animation is a readout of state
   rather than decoration. */
/* Sits in the hero button row; the disc itself is fully see-through, so only
   the ring and the bars read against the footage. */
.hum{
  flex:none; width:52px; height:52px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:transparent;
  border:1px solid rgba(241,234,217,.55);
  cursor:pointer; transition:border-color .28s, transform .28s;
}
.hum:hover{ border-color:rgba(241,234,217,.95); transform:scale(1.06); }

/* the same control, docked by the book pill once the hero has scrolled away,
   so the soundtrack can always be switched off */
.hum--float{
  position:fixed; right:1.3rem; bottom:1.3rem; z-index:69;
  width:46px; height:46px;
  border-color:rgba(241,234,217,.5);
  background:rgba(23,21,15,.55);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  box-shadow:0 14px 34px -12px rgba(23,21,15,.6);
  opacity:0; transform:translateY(16px); pointer-events:none;
  transition:opacity .4s, transform .4s, border-color .28s;
}
.hum--float.is-shown{ opacity:1; transform:translateY(0); pointer-events:auto; }
.hum--float.is-shown:hover{ transform:translateY(0) scale(1.06); }
/* the pill slides left to make room whenever the disc is docked */
.bookpill{ transition:opacity .4s, transform .4s, background .25s, right .4s; }
body:has(.hum--float.is-shown) .bookpill{ right:4.6rem; }
.hum:focus-visible{ outline:2px solid var(--amber-2); outline-offset:4px; }

/* Two glyphs, two states: bars while sound plays, a crossed speaker when it
   doesn't. Flattened bars alone read as four dots and say nothing about audio. */
.hum__mute{
  width:23px; height:23px; fill:var(--bone); stroke:var(--bone);
  stroke-width:1.9; stroke-linejoin:round; stroke-linecap:round; opacity:.85;
}
.hum__mute line{ fill:none; }
.hum.is-on .hum__mute{ display:none; }
.hum:not(.is-on) .hum__bars{ display:none; }
.hum:hover .hum__mute{ opacity:1; }

.hum__bars{ display:flex; align-items:center; gap:3px; height:22px; }
/* Off is the resting state: a flat line. The animation is only attached
   while sound plays, because a merely *paused* animation still wins over
   this height in the cascade and would freeze the bars at random heights. */
.hum__bars i{
  display:block; width:3px; border-radius:2px; background:var(--bone);
  height:4px; opacity:.75; transition:opacity .25s;
}
.hum.is-on .hum__bars i{
  opacity:1;
  animation:hum-wave 1.1s ease-in-out infinite alternate;
}
/* offsets must out-specify the .is-on shorthand above, which resets delay to 0 */
.hum.is-on .hum__bars i:nth-child(1){ animation-delay:-.90s; }
.hum.is-on .hum__bars i:nth-child(2){ animation-delay:-.50s; }
.hum.is-on .hum__bars i:nth-child(3){ animation-delay:-.20s; }
.hum.is-on .hum__bars i:nth-child(4){ animation-delay:-.65s; }
.hum.is-on .hum__bars i:nth-child(5){ animation-delay:-.35s; }
@keyframes hum-wave{ from{ height:4px; } to{ height:21px; } }

/* No motion: keep the control fully usable, just stop the bars moving.
   They hold mid-height so the disc still reads as "sound is on". */
html.no-motion .hum.is-on .hum__bars i,
html.no-motion .hum__bars i{ animation:none; }
html.no-motion .hum.is-on .hum__bars i{ height:13px; }
@media (prefers-reduced-motion: reduce){
  .hum.is-on .hum__bars i, .hum__bars i{ animation:none; }
  .hum.is-on .hum__bars i{ height:13px; }
  .hum{ transition:none; }
  .hum:hover{ transform:none; }
}
.hero__scroll{ width:1px; height:34px; background:rgba(241,234,217,.3); position:relative; overflow:hidden; }
.hero__scroll i{ position:absolute; left:0; top:-40%; width:100%; height:40%;
  background:var(--amber-2); animation:scrollcue 2.2s ease-in-out infinite; }
@keyframes scrollcue{ 0%{ top:-40%; } 60%,100%{ top:110%; } }

/* ── parallax backdrops ──────────────────────────────────────
   One viewport-sized image per zone, held in view while the
   content scrolls over it and drifting --drift px slower.
   The wrapper clips; only .backdrop__img moves.            */
.backdrop{
  position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
}
.backdrop__img{
  position:absolute; left:0; right:0;
  top:calc(var(--drift) * -1);
  height:calc(100svh + var(--drift) * 2);
  background-position:center center;
  background-size:cover;
  background-repeat:no-repeat;
  will-change:transform;
}

/* No motion: nothing drives the drift, so let the image cover its whole zone */
html.no-motion .backdrop__img{ top:0; height:100%; }
@media (prefers-reduced-motion: reduce){ .backdrop__img{ top:0; height:100%; } }

/* Paper ground (intro → gallery).
   The original loaded a scanned Balinese line-art sheet. This is generated
   instead: two turbulence layers, one coarse and one fine, which gives the
   same sense of a warm laid paper without a 400 kB texture to download or a
   missing-asset risk. Tint comes from --bone underneath, so retuning the
   palette retunes the paper with it. */
.paperzone{ position:relative; background:var(--bone); overflow:clip; --drift:120px; }
.paper .backdrop__img{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Cfilter id='q'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.012' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='600' height='600' filter='url(%23q)' opacity='0.5'/%3E%3Crect width='600' height='600' filter='url(%23p)' opacity='0.22'/%3E%3C/svg%3E");
  background-size:600px 600px;
  background-repeat:repeat;
  opacity:.22;
  mix-blend-mode:multiply;
}
.paperzone > section{ position:relative; z-index:1; }

/* ── intro ───────────────────────────────────────────────── */
.intro{ padding-block:var(--sec); background:transparent; }
.intro__text{
  font-size:clamp(1.5rem, 3.2vw, 2.4rem); line-height:1.35; max-width:56rem;
  letter-spacing:-.005em; text-align:center; margin-inline:auto;
}
.intro__text em{ font-style:italic; color:var(--petrol); }
.intro__rule{
  display:block; width:min(100%, 46rem); height:auto;
  margin:clamp(2.5rem,6vw,4rem) auto clamp(1.5rem,4vw,2.5rem);
  opacity:.75;
}
/* the sister-villa line, between the divider and the numbers */
.intro__villa{
  max-width:52rem; margin:0 auto clamp(2.2rem,5vw,3.4rem);
  text-align:center; font-size:clamp(1rem,1.5vw,1.15rem);
  line-height:1.7; opacity:.8;
}
.intro__stats{
  list-style:none; display:grid; grid-template-columns:repeat(4, 1fr);
  gap:1.5rem; padding-top:.5rem;
}
.intro .eyebrow{ text-align:center; }
.intro__stats li{ display:flex; flex-direction:column; gap:.3rem;
  align-items:center; text-align:center; }
.intro__stats strong{ font-family:var(--font-display); font-weight:400; font-size:clamp(1.9rem,4vw,2.8rem); }
.intro__stats strong i{ font-style:normal; font-size:.55em; }
.intro__stats .mono{ opacity:.6; }

/* ── villa features ──────────────────────────────────────── */
.villa{ padding-block:var(--sec) 0; background:transparent; }
.feature{
  position:relative;
  display:grid; grid-template-columns:7fr 5fr; gap:clamp(1.5rem,4vw,4rem);
  align-items:center; margin-bottom:clamp(4rem,9vw,8rem);
}
/* Flipped blocks move the image to the right column, so the column widths
   swap too — otherwise the right-hand images render narrower. */
.feature--flip{ grid-template-columns:5fr 7fr; }
.feature--flip .feature__img{ order:2; }
/* Hand-drawn Balinese numerals, drifting behind each feature block */
.feature__numeral{
  position:absolute; z-index:0; top:-1.5rem; right:-3.5rem;
  height:clamp(12rem, 28.5vw, 25.5rem); width:auto;
  opacity:.2; pointer-events:none; user-select:none;
}
.feature--flip .feature__numeral{ right:auto; left:-3.5rem; }
.feature__img, .feature__body{ position:relative; z-index:1; }
.feature:nth-child(even) .feature__body{ margin-top:clamp(0px, 4vw, 3.5rem); }
.feature:nth-child(odd) .feature__img{ margin-top:clamp(0px, 3vw, 2.5rem); }
.feature__img{ overflow:hidden; border-radius:var(--r); }
/* overscan has to exceed the parallax travel (±12%) or the drift would pull
   the image edge into the frame */
.feature__img img{ width:100%; height:100%; object-fit:cover; aspect-ratio:4/3;
  transform:scale(1.28); will-change:transform; }
.feature__idx{ color:var(--clay); margin-bottom:.7rem; letter-spacing:.14em; text-transform:uppercase; }
.feature__body h3{ margin-bottom:.8rem; }
.feature__body p{ opacity:.85; max-width:30rem; }
.feature__tags{ list-style:none; display:flex; flex-wrap:wrap; gap:.5rem; margin-top:1.2rem; }
.feature__tags li{
  border:1px solid rgba(23,21,15,.18); padding:.28rem .7rem; border-radius:99px;
  font-size:.7rem; opacity:.75;
}

/* ── gallery ─────────────────────────────────────────────── */
.gallery{ overflow:hidden; background:var(--ink);
  /* the section boundary lands on a fractional pixel, so paint 2px past it
     and pull the next section back up: covers the flickering hairline */
  padding-block:clamp(3rem,6vw,5rem) calc(clamp(3rem,6vw,5rem) + 2px);
  margin-bottom:-2px; }
.gallery__track{ display:flex; gap:clamp(1rem,2vw,1.8rem); width:max-content; padding-inline:var(--pad); }
.gallery__track figure{ width:clamp(240px, 30vw, 420px); flex-shrink:0; }
.gallery__track img{ aspect-ratio:3/4; object-fit:cover; width:100%; border-radius:var(--r); }
.gallery__track figcaption{ color:var(--bone); opacity:.55; margin-top:.6rem; }

/* ── film: scroll-driven reveal ──────────────────────────── */
.film{ background:var(--ink); height:460vh; position:relative; }
.film__pin{
  height:100svh; position:relative; overflow:hidden;
  display:flex; align-items:center; justify-content:center;
}
/* The frame is viewport-sized and starts scaled down, so it grows into a
   full-bleed screen without ever animating width/height. */
.film__frame{
  position:absolute; inset:0; overflow:hidden;
  transform:scale(.3); will-change:transform;
}
.film__video{ width:100%; height:100%; object-fit:cover; display:block; }
.film__veil{
  position:absolute; inset:0; background:rgba(23,21,15,.35);
  transition:background .4s;
}
.film__words{
  position:relative; z-index:2; display:flex; align-items:center;
  gap:.28em; background:none; border:0; padding:0; color:var(--bone);
  font-family:var(--font-body); font-weight:300;
  font-size:clamp(3rem, 9vw, 8rem); line-height:1; letter-spacing:-.02em;
}
.film__word{ display:block; will-change:transform; text-shadow:0 2px 30px rgba(23,21,15,.45); }
.film__pin:hover .film__veil{ background:rgba(23,21,15,.18); }
.film__note{
  position:absolute; z-index:2; bottom:2.2rem; left:50%; transform:translateX(-50%);
  color:var(--bone); opacity:.65; white-space:nowrap; text-align:center;
}

html.no-motion .film{ height:auto; }
html.no-motion .film__frame{ transform:none; }
html.no-motion .film__pin{ height:auto; overflow:visible; }
html.no-motion .ftrack{ position:static; width:100%; flex-direction:column; transform:none !important; }
html.no-motion .ftrack__panel{ width:100%; height:auto; }
html.no-motion .ftrack__panel--film{ height:100svh; }
html.no-motion .ftrack__panel--svc{ padding-block:var(--sec); }
@media (prefers-reduced-motion: reduce){
  .film{ height:100svh; }
  .film__frame{ transform:none; }
}

/* An iframe swallows pointermove, so the custom cursor would freeze over the
   film — hand control back to the native cursor while the modal is open. */
html.film-open .cursor{ display:none; }
html.film-open.has-cursor body,
html.film-open.has-cursor a,
html.film-open.has-cursor button{ cursor:auto; }

/* the pinned view holds two panels side by side; scroll slides it left */
.ftrack{
  position:absolute; inset:0; display:flex; width:200%;
  will-change:transform;
}
.ftrack__panel{
  position:relative; width:50%; height:100%; flex:none;
  display:flex; align-items:center; justify-content:center;
}
/* The bone panel overshoots its box on the left and bottom: the slide eases
   asymptotically into place under a scrubbed trigger, so without the overlap
   a 1-3px slice of the dark film panel flickers at the edge on arrival.
   .film__pin clips the overhang, so nothing moves. */
.ftrack__panel--svc{
  background:var(--bone); color:var(--ink);
  height:calc(100% + 2px); box-shadow:-5px 0 0 0 var(--bone);
  /* clips the wordmark's bleed here rather than relying on .film__pin, which
     drops to overflow:visible on phones and in reduced motion */
  overflow:hidden;
}
/* The wordmark is near-white artwork (rgb 210,211,212), which all but
   disappears on bone. Inverted it lands at rgb 45,44,43 and reads as a
   watermark; the opacity then does the washing back, not the colour. */
.svcintro__mark{
  position:absolute; top:50%; left:50%; translate:-50% -50%;
  width:min(118%, 1900px); max-width:none; height:auto;
  filter:invert(1); opacity:.07;   /* 30% more translucent */
  pointer-events:none; user-select:none; z-index:0;
}
.svcintro{ position:relative; z-index:1; text-align:center; padding:var(--pad); max-width:44rem; }
.svcintro .eyebrow{ color:var(--clay); }
.svcintro__title{
  font-size:clamp(2.6rem, 7vw, 6rem); line-height:1.02; letter-spacing:-.015em;
  margin-bottom:1.2rem;
}
.svcintro__sub{ opacity:.72; font-size:1.02rem; max-width:26rem; margin-inline:auto; }
.svcintro__rule{
  display:block; width:min(300px, 62%); height:auto;
  margin:clamp(1.6rem,3vw,2.4rem) auto 0; opacity:.55;
}
/* the caveat sits under the statement, deliberately quieter */
.services__fineprint{
  display:block; margin-top:.9rem;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em;
  text-transform:lowercase; opacity:.55;
}
.services__lead{
  max-width:42rem; margin-inline:auto; text-align:center;
  font-family:var(--font-display); font-size:clamp(1.25rem,2.4vw,1.75rem);
  line-height:1.35; opacity:.85;
  margin-bottom:clamp(2.5rem,6vw,4rem);
}

/* ── film modal: Balinese frame ──────────────────────────── */
/* Near-full-viewport: the crests are the only chrome, --chrome reserves
   exactly their height plus gaps so the screen takes everything else. */
.vmodal{
  position:fixed; inset:0; z-index:95; display:flex; align-items:center; justify-content:center;
  padding:.75rem;
  --crest:clamp(24px, 4.4vh, 48px);
  --chrome:calc(var(--crest) * 2 + 2.4rem);
}
.vmodal[hidden]{ display:none; }
.vmodal__scrim{ position:absolute; inset:0; background:rgba(12,11,8,.94); backdrop-filter:blur(6px); }
.vmodal__frame{
  position:relative; width:100%; height:100%;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:.5rem;
}
.vmodal__crest{ height:var(--crest); width:auto; max-width:56%; flex:none; opacity:.85; pointer-events:none; }
.vmodal__crest--bottom{ transform:rotate(180deg); }
.vmodal__screen{
  position:relative; aspect-ratio:16/9;
  width:min(100%, calc((100svh - var(--chrome)) * 16 / 9));
  max-height:calc(100svh - var(--chrome));
  background:#000; overflow:hidden;
  border:1px solid rgba(226,171,85,.55);
  box-shadow:0 0 0 6px rgba(23,21,15,.9), 0 0 0 7px rgba(226,171,85,.28),
             0 40px 90px -30px rgba(0,0,0,.9);
}
.vmodal__screen iframe{ position:absolute; inset:0; width:100%; height:100%; border:0; }
.vmodal__x{
  position:absolute; top:-.4rem; right:-.2rem; z-index:3;
  background:none; border:0; color:var(--bone); font-size:2.4rem; line-height:1; opacity:.7;
}
.vmodal__x:hover{ opacity:1; }

/* ── location ────────────────────────────────────────────── */
.location{ background:var(--ink); color:var(--bone); padding-block:var(--sec); }
.location--photo{ position:relative; overflow:clip; --drift:120px; }
/* the village film sits exactly where the still backdrop did */
.backdrop__video{
  position:absolute; left:0; right:0;
  top:calc(var(--drift) * -1);
  height:calc(100svh + var(--drift) * 2);
  width:100%; object-fit:cover; will-change:transform;
  /* the footage is far brighter than the still it replaced, so knock it back
     before the gradient goes over the top, or the drive times wash out */
  filter:brightness(.72) saturate(.95);
}
html.no-motion .backdrop__video{ top:0; height:100%; }
@media (prefers-reduced-motion: reduce){ .backdrop__video{ top:0; height:100%; } }
/* A warm ink scrim rather than the old petrol wash: that gradient tinted the
   whole village green. Ink darkens for legibility and lets the paddies keep
   their own colour. */
.location__bg::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(to bottom,
    rgba(23,21,15,.62) 0%, rgba(23,21,15,.50) 38%, rgba(23,21,15,.93) 100%);
}
.location--photo .wrap{ position:relative; z-index:1; }
.location h2, .location h3, .location h4{ color:var(--bone); }
.location__grid{ display:grid; grid-template-columns:7fr 5fr; gap:clamp(1.5rem,3vw,3rem);
  margin-bottom:clamp(2.5rem,5vw,4rem); }
.location__map{ display:flex; flex-direction:column; gap:.8rem; }
.location__map iframe{ width:100%; height:100%; min-height:380px; border:0;
  border-radius:var(--r); filter:grayscale(.35) contrast(1.02); }
.location__coords{ opacity:.7; }
.location__coords a{ color:var(--amber-2); }
.location__dist-title{ letter-spacing:.2em; text-transform:uppercase; color:var(--amber-2); margin-bottom:1rem; }
.dist-table{ width:100%; border-collapse:collapse; }
.dist-table td{ padding:.55rem 0; border-bottom:1px solid rgba(241,234,217,.12); font-size:.8rem; }
.dist-table td:last-child{ text-align:right; color:var(--amber-2); white-space:nowrap; padding-left:1rem; }
.location__note{ margin-top:1rem; opacity:.55; }
.area-cards{ display:grid; grid-template-columns:repeat(3,1fr); gap:1.2rem; }
.area-card{
  border:1px solid rgba(241,234,217,.14); border-radius:6px; overflow:hidden;
  background:rgba(23,21,15,.3);
  transition:border-color .3s, transform .3s;
}
.area-card:hover{ border-color:rgba(226,171,85,.5); transform:translateY(-4px); }
.area-card__img{ aspect-ratio:16/9; overflow:hidden; }
.area-card__img img{ width:100%; height:100%; object-fit:cover;
  transform:scale(1.02); transition:transform .8s cubic-bezier(.2,.6,.2,1); }
.area-card:hover .area-card__img img{ transform:scale(1.1); }
.area-card__body{ padding:1.4rem 1.5rem 1.6rem; }
.area-card h4{ font-family:var(--font-display); font-weight:400; font-size:1.25rem; margin-bottom:.35rem; }
.area-card__dist{ color:var(--amber-2); margin-bottom:.7rem; }
.area-card__link{
  display:inline-flex; align-items:center; gap:.35rem; margin-bottom:.7rem;
  color:var(--bone); opacity:.72; text-decoration:none;
  border-bottom:1px solid rgba(241,234,217,.35); padding-bottom:.12rem;
  transition:opacity .25s, border-color .25s, color .25s;
}
.area-card__link:hover, .area-card__link:focus-visible{
  opacity:1; color:var(--amber-2); border-color:var(--amber-2);
}

/* ── tooltip bubble ──────────────────────────────────────── */
[data-tip]{ position:relative; }
[data-tip]::after{
  content:attr(data-tip);
  position:absolute; top:calc(100% + 7px); left:0;
  transform:translateY(-4px);
  background:var(--ink); color:var(--bone);
  border:1px solid rgba(226,171,85,.4); border-radius:4px;
  padding:.3rem .55rem; white-space:nowrap;
  font-family:var(--font-body); font-size:.72rem; font-weight:400;
  letter-spacing:0; text-transform:none; line-height:1.3;
  opacity:0; pointer-events:none; z-index:7;
  transition:opacity .18s ease, transform .18s ease;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after{
  opacity:1; transform:translateY(0);
}
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.area-card p:last-child{ font-size:.92rem; opacity:.8; }

/* ── services ────────────────────────────────────────────── */
/* margin-top:-2px closes a sub-pixel seam: the boundary with the ink .film
   section above lands off-pixel at some scroll offsets and the compositor
   blends a 1px dark line across the full width. Overlap beats rounding. */
.services{
  background:var(--bone); margin-top:-2px; padding-block:var(--sec);
  position:relative; overflow:clip; --drift:120px;
  transition:background-color .5s ease;
}
.services > .wrap{ position:relative; z-index:1; }
/* the paper texture fades up into the panel above, so the join between the
   sideways slide and the list reads as one continuous surface */
.services .backdrop__img{
  -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 26vh);
  mask-image:linear-gradient(to bottom, transparent 0, #000 26vh);
}

.svclist{ list-style:none; border-top:1px solid rgba(23,21,15,.18); }
.svcrow{ border-bottom:1px solid rgba(23,21,15,.18); }
.svcrow__hit{
  position:relative; width:100%; display:grid;
  grid-template-columns:3.5rem 1fr auto 2.5rem;
  align-items:center; gap:1.2rem;
  padding:1.35rem .8rem; background:none; border:0; text-align:left;
  color:var(--ink); overflow:hidden;
}
.svcrow__hit::before{
  content:""; position:absolute; inset:0; background:var(--ink);
  transform:scaleY(0); transform-origin:bottom;
  transition:transform .45s cubic-bezier(.65,0,.15,1); z-index:0;
}
.svcrow__hit > *{ position:relative; z-index:1; }
.svcrow__idx{ color:var(--clay); }
.svcrow__main{ display:flex; flex-direction:column; gap:.15rem; min-width:0; }
.svcrow__name{ font-family:var(--font-display); font-size:clamp(1.3rem, 2.6vw, 2rem); line-height:1.1; }
.svcrow__blurb{ font-size:.85rem; opacity:.6; max-width:34rem; transition:opacity .35s; }
.svcrow__price{
  color:var(--clay); white-space:nowrap; display:flex; flex-direction:column;
  align-items:flex-end; gap:.12rem; line-height:1.35;
}
.svcrow__fx{ opacity:.62; font-size:.66rem; }
.svcrow__arrow{ font-size:1.1rem; opacity:.4; transform:translateX(-6px);
  transition:transform .35s, opacity .35s; }
.svcrow__hit:hover::before, .svcrow__hit:focus-visible::before{ transform:scaleY(1); }
.svcrow__hit:hover .svcrow__blurb{ opacity:.75; }
.svcrow__hit:hover .svcrow__arrow{ transform:translateX(0); opacity:1; }

.svc-preview{
  display:none; position:fixed; z-index:58; width:290px; aspect-ratio:4/3;
  border-radius:6px; overflow:hidden; pointer-events:none;
  opacity:0; top:0; left:0;
  box-shadow:0 24px 60px -20px rgba(23,21,15,.55);
}
.svc-preview img{ width:100%; height:100%; object-fit:cover; }
@media (pointer:fine){ .svc-preview{ display:block; } }

/* Hovering a row fills only that row — the section around it stays put. */
.svcrow__hit > *{ transition:color .35s ease; }
.svcrow__hit:hover > *, .svcrow__hit:focus-visible > *{ color:var(--bone); }
.svcrow__hit:hover .svcrow__idx, .svcrow__hit:focus-visible .svcrow__idx,
.svcrow__hit:hover .svcrow__price, .svcrow__hit:focus-visible .svcrow__price{ color:var(--amber-2); }

/* ── amenities ───────────────────────────────────────────── */
/* A panel, not a footnote: the amenity list is the last thing between a
   guest and the booking form, so it gets the same card treatment as the
   guest-portal block it replaced. */
.amen{
  margin-top:clamp(2.5rem,5vw,4rem);
  border:1px solid rgba(23,21,15,.16); border-radius:var(--r);
  background:#f6f1e4;
  padding:clamp(1.6rem,3.6vw,2.8rem);
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(1.5rem,3.5vw,3rem); align-items:center;
}
.amen__head .eyebrow{ color:var(--clay); margin-bottom:.7rem; }
.amen__title{ font-size:clamp(1.5rem,2.8vw,2.1rem); margin-bottom:.4rem; }
.amen__sub{ opacity:.72; font-size:.95rem; max-width:26rem; }
.amen__head .btn{ margin-top:1.4rem; }

.amen__side{ display:flex; flex-direction:column; gap:.9rem; }
.amen__count{
  font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; opacity:.6;
  display:flex; align-items:baseline; gap:.5rem;
}
.amen__count b{
  font-family:var(--font-display); font-size:2.1rem; font-weight:400;
  letter-spacing:0; text-transform:none; color:var(--clay); line-height:1;
}
.amen__count i{ font-style:normal; opacity:.7; }

.amen__peek{ list-style:none; display:flex; flex-wrap:wrap; gap:.45rem; }
.amen__peek li{
  border:1px solid rgba(23,21,15,.2); border-radius:99px;
  padding:.34rem .85rem; font-size:.68rem; opacity:.75;
  background:rgba(241,234,217,.55);
}
/* the overflow chip reads as a count, not another amenity */
.amen__peek .amen__more{
  border-style:dashed; opacity:.55; background:none;
}

/* the practical questions guests actually ask, sitting under the chips rather
   than buried in the 56-item modal */
.amen__facts{
  grid-column:1 / -1; margin-top:clamp(1.5rem,3vw,2.2rem);
  padding-top:clamp(1.3rem,3vw,1.9rem);
  border-top:1px solid rgba(23,21,15,.16);
  display:grid; grid-template-columns:repeat(4,1fr); gap:1.4rem 2rem;
}
.amen__facts dt{
  color:var(--clay); font-size:.68rem; letter-spacing:.16em;
  text-transform:uppercase; margin-bottom:.5rem;
}
.amen__facts dd{ font-size:.88rem; line-height:1.6; opacity:.8; }
.amen__link{
  background:none; border:0; padding:0; font:inherit; color:var(--clay);
  border-bottom:1px solid rgba(156,91,65,.45); cursor:pointer;
}
.amen__link:hover{ color:var(--ink); border-color:var(--ink); }

.amodal{ position:fixed; inset:0; z-index:93; display:flex; align-items:center;
  justify-content:center; padding:clamp(1rem,3vw,2.5rem); }
.amodal[hidden]{ display:none; }
.amodal__scrim{ position:absolute; inset:0; background:rgba(12,11,8,.86); backdrop-filter:blur(5px); }
.amodal__card{
  position:relative; width:min(920px,100%); max-height:88svh;
  background:var(--bone); border-radius:8px; display:flex; flex-direction:column;
  box-shadow:0 40px 90px -34px rgba(0,0,0,.8);
}
.amodal__top{
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:clamp(1.3rem,3vw,1.9rem) clamp(1.3rem,3vw,2.1rem) 1rem;
  border-bottom:1px solid rgba(23,21,15,.14);
}
.amodal__top h3{ font-size:clamp(1.3rem,2.6vw,1.8rem); }
.amodal__x{ background:none; border:0; font-size:2rem; line-height:1; color:var(--ink); opacity:.55; }
.amodal__x:hover{ opacity:1; }
.amodal__body{ overflow:auto; padding:clamp(1.3rem,3vw,2.1rem); }
.amenlist{ columns:2; column-gap:2.4rem; }
.amenlist__group{ break-inside:avoid; margin-bottom:1.5rem; }
.amenlist__cat{
  letter-spacing:.16em; text-transform:uppercase; color:var(--clay);
  font-size:.62rem; margin-bottom:.5rem;
}
.amenlist__group ul{ list-style:none; display:flex; flex-direction:column; gap:.3rem; }
.amenlist__group li{ font-size:.92rem; opacity:.85; }
.amenlist__group--not li{ opacity:.45; text-decoration:line-through; }
.amodal__note{ margin-top:.5rem; opacity:.55; }

/* ── service detail modal ────────────────────────────────── */
.smodal{ position:fixed; inset:0; z-index:92; display:flex; align-items:center;
  justify-content:center; padding:clamp(1rem,3vw,2rem); }
.smodal[hidden]{ display:none; }
.smodal__scrim{ position:absolute; inset:0; background:rgba(12,11,8,.86); backdrop-filter:blur(5px); }
.smodal__card{
  position:relative; width:min(880px,100%); max-height:88svh; overflow:visible;
  background:var(--bone); border-radius:6px;
  display:grid; grid-template-columns:5fr 6fr;
  box-shadow:0 40px 90px -34px rgba(0,0,0,.8);
}
.smodal__img{ overflow:hidden; border-radius:6px 0 0 6px; }
.smodal__img img{ width:100%; height:100%; object-fit:cover; min-height:220px; }
.smodal__body{
  padding:clamp(1.6rem,3vw,2.4rem); display:flex; flex-direction:column; gap:.35rem;
  max-height:88svh; overflow:auto; border-radius:0 6px 6px 0;
}
.smodal__body h3{ font-size:clamp(1.5rem,3vw,2.1rem); margin-bottom:.5rem; }
.smodal__detail{ opacity:.85; font-size:.97rem; }
.smodal__price{ margin-top:1rem; display:flex; gap:.9rem; align-items:baseline;
  flex-wrap:wrap; color:var(--clay); font-size:.85rem; }
.smodal__price span + span{ opacity:.62; }
.smodal__note{ margin-top:.9rem; font-size:.85rem; opacity:.62;
  border-top:1px solid rgba(23,21,15,.16); padding-top:.9rem; }
.smodal__actions{ display:flex; gap:.7rem; flex-wrap:wrap; margin-top:1.1rem; }
.smodal__nav{
  position:absolute; top:50%; transform:translateY(-50%); z-index:3;
  width:44px; height:44px; border-radius:50%;
  background:rgba(23,21,15,.82); color:var(--bone);
  border:1px solid rgba(226,171,85,.4); font-size:1rem;
  transition:background .22s, border-color .22s;
}
.smodal__nav:hover{ background:var(--ink); border-color:var(--amber-2); }
.smodal__nav--prev{ left:-56px; }
.smodal__nav--next{ right:-56px; }
@media (max-width: 1060px){
  .smodal__nav{ top:auto; bottom:.9rem; transform:none; }
  .smodal__nav--prev{ left:auto; right:4.3rem; }
  .smodal__nav--next{ right:.9rem; }
  .smodal__count{ bottom:1.6rem; right:1.4rem; }
}
.smodal__count{ position:absolute; bottom:.85rem; right:1.4rem; z-index:3;
  color:var(--clay); opacity:.7; font-size:.7rem; }
.smodal__x{ position:absolute; top:.5rem; right:.7rem; z-index:2;
  background:none; border:0; font-size:2rem; line-height:1; color:var(--ink); opacity:.55; }
.smodal__x:hover{ opacity:1; }

/* ── culture ─────────────────────────────────────────────────
   Same Balinese line-art paper as the light zones, inverted:
   bone lines on ink instead of ink lines on bone. The section
   colour sits under it so the invert stays warm, not blue.  */
.culture{
  background:var(--ink); color:var(--bone);
  padding-block:var(--sec); position:relative; overflow:clip; --drift:120px;
}
.culture > .wrap{ position:relative; z-index:1; }
/* The dark section takes the same generated paper, screened rather than
   multiplied, so the grain reads as light on ink. The old invert filter is
   gone with the scanned texture it was correcting. */
.paper--inv .backdrop__img{
  mix-blend-mode:screen;
  opacity:.14;
}
.culture__lower{
  display:grid; grid-template-columns:1.05fr .95fr;
  gap:clamp(2rem,5vw,4rem); margin-top:clamp(3rem,7vw,5rem);
}

/* ── culture: expanding triptych ─────────────────────────── */
.trip{ display:flex; gap:.7rem; min-height:460px; }
.trip__panel{
  position:relative; flex:1; border-radius:6px; overflow:hidden;
  background:var(--img) center/cover; cursor:pointer;
  display:flex; flex-direction:column; justify-content:flex-end;
  padding:1.8rem; color:var(--bone);
  transition:flex .75s cubic-bezier(.65,0,.15,1);
}
.trip__panel.is-open{ flex:2.6; }
.trip__veil{ position:absolute; inset:0;
  background:linear-gradient(to top, rgba(23,21,15,.88) 0%, rgba(23,21,15,.35) 55%, rgba(23,21,15,.25) 100%); }
.trip__head, .trip__body{ position:relative; z-index:1; }
.trip__head h3{ font-size:clamp(1.4rem,2.4vw,1.9rem); color:var(--bone); }
.trip__head .mono{ color:var(--amber-2); margin-top:.2rem; }
.trip__body{
  max-width:30rem; font-size:.93rem; opacity:0; max-height:0; overflow:hidden;
  transition:opacity .5s .25s, max-height .6s .1s, margin .5s .1s;
}
.trip__panel.is-open .trip__body{ opacity:.92; max-height:16rem; margin-top:.9rem; }

/* ── culture notes accordion ─────────────────────────────── */
.culture__notes{ display:flex; flex-direction:column; gap:.6rem; }
.cnote{ border:1px solid rgba(241,234,217,.16); border-radius:var(--r); background:rgba(241,234,217,.03); }
.cnote summary{
  cursor:pointer; padding:1rem 1.3rem; list-style:none;
  font-family:var(--font-display); font-size:1.15rem; color:var(--bone);
  display:flex; justify-content:space-between; align-items:center;
}
.cnote summary::after{ content:"+"; font-family:var(--font-mono); color:var(--amber-2); }
.cnote[open] summary::after{ content:"−"; }
.cnote p{ padding:0 1.3rem 1.1rem; font-size:.93rem; opacity:.8; max-width:36rem; }

/* ── seasons: cards spread from a single stack ───────────── */
.seasons{ background:var(--bone-2); height:340vh; position:relative; }
.seasons__pin{ height:100svh; position:relative; overflow:hidden;
  display:flex; flex-direction:column; align-items:center; --drift:120px; }
.seasons__pin > .backdrop{ z-index:0; }
.seasons__head, .seasons__stage, .yearbar, .seasons__hint{ position:relative; z-index:1; }
.seasons__head{
  position:relative; z-index:8; text-align:center; padding:clamp(2.2rem,5.5vh,3.6rem) var(--pad) 0;
  max-width:44rem; pointer-events:none;
}
.seasons__head h2{ font-size:clamp(1.7rem,3.4vw,2.6rem); }
.seasons__sub{ margin-top:.7rem; opacity:.75; font-size:.95rem; }
/* start below the header zone so the fan's centre clears the heading */
.seasons__stage{ position:absolute; top:clamp(8rem,21svh,12rem); left:0; right:0; bottom:0; z-index:1; }
.seasons__hint{
  position:absolute; bottom:1.6rem; left:50%; transform:translateX(-50%);
  opacity:.45; letter-spacing:.2em; text-transform:uppercase; z-index:8;
}

.scard{
  position:absolute; left:50%; top:50%; margin:0; padding:0; border:0;
  width:clamp(130px, 14vw, 200px); aspect-ratio:3/4;
  transform:translate(-50%,-50%);
  border-radius:6px; overflow:hidden; background:var(--ink);
  box-shadow:0 26px 60px -28px rgba(23,21,15,.75);
  will-change:transform;
}
.scard img{ width:100%; height:100%; object-fit:cover; }
.scard__veil{
  position:absolute; inset:0;
  background:linear-gradient(to bottom, rgba(12,11,8,.92) 0%, rgba(12,11,8,.35) 52%, rgba(12,11,8,.12) 100%);
}
.scard__label{
  position:absolute; left:0; right:0; top:0; padding:.9rem 1rem 1.2rem;
  display:flex; flex-direction:column; gap:.2rem; text-align:left; color:var(--bone);
}
.scard__when{ color:var(--amber-2); letter-spacing:.14em; text-transform:uppercase; font-size:.62rem; }
.scard__name{ font-family:var(--font-display); font-size:clamp(1rem,1.5vw,1.35rem); line-height:1.15; }
.scard:not(.scard--core){
  z-index:1;
  transition:box-shadow .35s ease, filter .35s ease;
}
/* pull the hovered card clear of the pile */
.scard:not(.scard--core):hover,
.scard:not(.scard--core):focus-visible{
  z-index:9;
  box-shadow:0 34px 80px -22px rgba(23,21,15,.95);
  filter:brightness(1.08);
}
/* the scrub owns .scard's transform, so the lift lives on the image */
.scard img{ transition:transform .45s cubic-bezier(.2,.6,.2,1); }
.scard:not(.scard--core):hover img,
.scard:not(.scard--core):focus-visible img{ transform:scale(1.08); }
.scard:not(.scard--core):hover .scard__veil,
.scard:not(.scard--core):focus-visible .scard__veil{
  background:linear-gradient(to bottom, rgba(12,11,8,.86) 0%, rgba(12,11,8,.2) 55%, rgba(12,11,8,.05) 100%);
}

/* the pair in the middle never moves — green above, dry below */
.scard--core{
  z-index:6; height:min(66%, 560px); width:auto; aspect-ratio:3/4.2;
  display:flex; flex-direction:column; background:none; box-shadow:none;
  border-radius:0; overflow:visible; pointer-events:none;
}
.scard__half{
  position:relative; flex:1; min-height:0; border:0; padding:0; margin:0;
  overflow:hidden; background:var(--ink); pointer-events:auto;
  box-shadow:0 26px 60px -28px rgba(23,21,15,.8);
  transition:flex .5s cubic-bezier(.65,0,.15,1);
}
.scard__half--green{ border-radius:6px 6px 0 0; }
.scard__half--dry{ border-radius:0 0 6px 6px; }
.scard__half + .scard__half{ margin-top:2px; }
.scard__half:hover{ flex:1.25; }
.scard__half img{ width:100%; height:100%; object-fit:cover; }

/* ── seasons: the year at a glance ──────────────────────── */
.yearbar{
  position:absolute; top:clamp(.8rem, 2.2vh, 1.7rem); left:50%;
  transform:translateX(-50%);
  width:min(900px, 90vw); z-index:7; margin:0;
  opacity:0;                      /* fades in as the fan opens */
  pointer-events:none;
}
.yearbar__title{
  text-align:center; color:var(--clay); opacity:.7;
  letter-spacing:.2em; text-transform:uppercase; font-size:.58rem; margin-bottom:.45rem;
}
.yearbar__grid{ display:grid; grid-template-columns:repeat(12, 1fr); gap:3px; }
.yearbar__col{ display:flex; flex-direction:column; align-items:center; gap:.22rem; }
.yearbar__m{ font-size:.62rem; opacity:.55; letter-spacing:.06em; }
.yearbar__band{
  display:block; width:100%; height:6px; border-radius:2px;
  background:var(--petrol); opacity:.85;
}
.yearbar__col--dry .yearbar__band{ background:var(--amber); }
.yearbar__t{ font-size:.6rem; opacity:.5; }
/* the month you are actually in */
.yearbar__col.is-now .yearbar__m{ opacity:1; color:var(--clay); }
.yearbar__col.is-now .yearbar__band{ opacity:1; height:11px; margin-block:-2.5px; }
.yearbar__col.is-now .yearbar__t{ opacity:.95; }
.yearbar__key{
  display:flex; align-items:center; justify-content:center; gap:.4rem;
  flex-wrap:wrap; margin-top:.5rem; font-size:.58rem; opacity:.6;
}
.yearbar__dot{ width:9px; height:9px; border-radius:2px; display:inline-block; }
.yearbar__dot--green{ background:var(--petrol); }
.yearbar__dot--dry{ background:var(--amber); margin-left:.8rem; }
.yearbar__avg{ margin-left:.9rem; opacity:.8; }

/* Without motion nothing pins or fans the cards, so the section would be
   2160px of empty scroll with a pile in the middle. Lay it out flat instead,
   exactly as the phone breakpoint does. */
html.no-motion .stayzone{ height:auto; }
html.no-motion .stay--photo{ height:auto; }
html.no-motion .reviews{ margin-bottom:0; }
html.no-motion .yearbar{ opacity:1; }
@media (prefers-reduced-motion: reduce){ .yearbar{ opacity:1; } }

html.no-motion .seasons{ height:auto; padding-block:var(--sec); }
html.no-motion .seasons__pin{ height:auto; overflow:visible; }
html.no-motion .seasons__head{ padding-top:0; }
html.no-motion .seasons__prompt{ display:none; }   /* nothing to scroll open */
html.no-motion .yearbar{
  position:static; transform:none; width:min(900px, 90vw); margin:1.8rem auto 0;
}
html.no-motion .seasons__stage{
  position:static; display:grid; grid-template-columns:repeat(3, 1fr);
  gap:1rem; padding:2.2rem var(--pad) 0; max-width:var(--wrap); margin-inline:auto;
}
html.no-motion .scard{
  position:static; width:auto; transform:none !important; aspect-ratio:3/4;
}
html.no-motion .scard--core{
  grid-column:1 / -1; width:auto; aspect-ratio:16/7;
  flex-direction:row; gap:2px;
}
html.no-motion .scard--core .scard__half{ border-radius:6px; }
html.no-motion .scard__half + .scard__half{ margin-top:0; }
html.no-motion .seasons__hint{
  position:static; transform:none; text-align:center; margin-top:1.6rem;
}

/* ── reviews: testimonials arriving from both sides ──────── */
/* same light zone as guest services, paper texture and all */
.reviews{
  background:var(--bone); color:var(--ink); padding-block:var(--sec);
  position:relative; overflow:clip; --drift:120px;
}
.reviews > .wrap{ position:relative; z-index:1; }

/* Penjor: the curved Galungan bamboo the culture notes describe, flanking
   the guest voices. Sits above the paper backdrop and below the cards, and
   rides the zone slower than the paper so the two layers pull apart.    */
.reviews__deco{ position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none; }
.reviews__pole{
  /* nearly as tall as the backdrop image (100svh + 240px), planted with its
     base below the fold so only the arch and shaft ever read as an edge */
  position:absolute; top:16svh; height:118svh; width:auto;
  opacity:.5; will-change:transform;
}
.reviews__pole--l{ left:min(-3vw, -20px); }
.reviews__pole--r{ right:min(-3vw, -20px); }

/* No motion: nothing drives the offset, so hold them mid-zone */
html.no-motion .reviews__pole{ top:50%; transform:translateY(-50%); }
@media (prefers-reduced-motion: reduce){
  .reviews__pole{ top:50%; transform:translateY(-50%); }
}

.reviews__head{ text-align:center; max-width:44rem; margin:0 auto clamp(3rem,7vw,5.5rem); }
.reviews__head .eyebrow{ color:var(--clay); }
.reviews__head h2{ margin-bottom:1.4rem; }
.reviews__score{
  display:flex; align-items:center; justify-content:center; gap:1rem;
  margin-bottom:1.1rem;
}
.reviews__num{
  font-family:var(--font-display); font-size:clamp(3rem,7vw,4.6rem);
  line-height:1; color:var(--ink);
}
.reviews__of{ display:flex; flex-direction:column; align-items:flex-start; gap:.25rem; }
.reviews__stars{ color:var(--amber); letter-spacing:.14em; font-size:.9rem; }
.reviews__of .mono{ opacity:.6; }

/* the stream: cards step down the page, alternating sides */
.tstream{ display:flex; flex-direction:column; gap:clamp(1.6rem,4vw,3rem); }
.tcard{
  width:min(560px, 88%);
  background:#f6f1e4; color:var(--ink);
  border:1px solid rgba(23,21,15,.14); border-radius:8px;
  padding:clamp(1.5rem,3vw,2.1rem);
  box-shadow:0 22px 50px -34px rgba(23,21,15,.5);
  will-change:transform;
}
.tcard--l{ align-self:flex-start; }
.tcard--r{ align-self:flex-end; }
.tcard__head{ display:flex; align-items:center; gap:.85rem; margin-bottom:1rem; }
.tcard__ava{
  flex:none; width:44px; height:44px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:radial-gradient(circle at 30% 30%, var(--petrol), var(--petrol-d));
  color:var(--bone); font-family:var(--font-display); font-size:1.15rem;
  border:1px solid var(--amber);
}
.tcard__who{ display:flex; flex-direction:column; gap:.1rem; margin-right:auto; min-width:0; }
.tcard__name{ font-weight:600; font-size:1rem; }
.tcard__meta{ opacity:.55; font-size:.68rem; }
.tcard__stars{ color:var(--amber); letter-spacing:.1em; font-size:.72rem; flex:none; }
.tcard__quote{
  font-family:var(--font-display); font-size:clamp(1.1rem,1.9vw,1.4rem);
  line-height:1.45; letter-spacing:.004em;
}
.tcard__quote::before{ content:"“"; }
.tcard__quote::after{ content:"”"; }

/* the numbers behind the words */
.tsummary{
  margin-top:clamp(3rem,7vw,5rem); padding-top:clamp(2rem,4vw,3rem);
  border-top:1px solid rgba(23,21,15,.16); text-align:center;
}
.tsummary__title{
  letter-spacing:.2em; text-transform:uppercase; color:var(--clay);
  margin-bottom:1.6rem; font-size:.68rem;
}
.tsummary__grid{
  list-style:none; display:grid; grid-template-columns:repeat(3, minmax(0,1fr));
  gap:.9rem 2.2rem; max-width:52rem; margin:0 auto 1.8rem;
}
.tscore{ display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:.7rem; }
.tscore__label{ font-size:.85rem; opacity:.8; white-space:nowrap; }
.tscore__bar{
  height:3px; border-radius:2px; background:rgba(23,21,15,.15); overflow:hidden;
}
.tscore__bar i{ display:block; height:100%; background:var(--amber); }
.tscore__val{ font-size:.72rem; color:var(--clay); }
.tsummary__note{ opacity:.72; font-size:.95rem; margin-bottom:1.5rem; }
.tsummary strong{ font-weight:600; color:var(--ink); }

/* ── stay: twilight backdrop ─────────────────────────────── */
/* Revealed from underneath: the zone starts one viewport early so the
   testimonials sit over it, and the sticky screen holds for exactly that
   viewport before releasing. The zone bounds the stickiness, otherwise the
   booking screen would stay pinned over the team, contact and footer. */
/* NOTE: position:sticky cannot be used here. ScrollSmoother puts
   overflow:hidden on its wrapper, which kills sticky for every descendant.
   The hold is done with a ScrollTrigger pin in main.js instead. */
.stayzone{ position:relative; z-index:0; height:200svh; }
.stay--photo{
  height:100svh; display:flex; align-items:center; overflow:hidden;
}
.reviews{ position:relative; z-index:1; margin-bottom:-100svh; }
.stay{ background:var(--ink); color:var(--bone); padding-block:var(--sec); }
.stay h2{ color:var(--bone); font-size:clamp(2.4rem,5.4vw,4rem); margin-bottom:1.2rem; }
.stay .eyebrow{ color:var(--amber-2); }
.stay__grid{ display:grid; grid-template-columns:6fr 5fr; gap:clamp(2rem,5vw,5rem); align-items:start; }
.stay__pitch > p{ opacity:.85; max-width:34rem; }
.stay__perks{ list-style:none; margin-block:1.6rem; display:flex; flex-direction:column; gap:.6rem; }
.stay__perks li{ padding-left:1.4rem; position:relative; opacity:.85; }
.stay__perks li::before{ content:"—"; position:absolute; left:0; color:var(--amber-2); }
.stay__price{ display:flex; align-items:baseline; gap:.8rem; flex-wrap:wrap; margin-bottom:1.8rem; }
.stay__price .mono{ opacity:.55; }
.stay__price s{ opacity:.5; font-size:1.2rem; }
.stay__price strong{ font-family:var(--font-display); font-weight:400; font-size:2.6rem; color:var(--amber-2); }
.stay__price strong i{ font-style:normal; font-size:.4em; }
.stay__actions{ display:flex; gap:.9rem; flex-wrap:wrap; }

.cal{ border:1px solid rgba(241,234,217,.16); border-radius:var(--r); padding:1.4rem; background:var(--ink-2); }
.cal__head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:1rem; }
.cal__head h3{ font-family:var(--font-mono); font-size:.85rem; letter-spacing:.18em; text-transform:uppercase; color:var(--bone); }
.cal__nav{ background:none; border:1px solid rgba(241,234,217,.2); color:var(--bone);
  width:34px; height:34px; border-radius:var(--r); transition:border-color .2s; }
.cal__nav:hover{ border-color:var(--amber-2); }
.cal__nav[disabled]{ opacity:.3; }
.cal__grid{ display:grid; grid-template-columns:repeat(7,1fr); gap:4px; font-size:.72rem; }
.cal__grid .cal__dow{ text-align:center; opacity:.45; padding-block:.35rem; }
.cal__day{
  aspect-ratio:1; display:flex; align-items:center; justify-content:center;
  border-radius:3px; background:rgba(241,234,217,.06); position:relative;
}
.cal__day--busy{ background:rgba(156,91,65,.35); color:rgba(241,234,217,.45); text-decoration:line-through; }
.cal__day--free:hover{ background:rgba(226,171,85,.25); }
.cal__day--pad{ background:transparent; }
.cal__legend{ display:flex; gap:1.4rem; margin-top:1rem; opacity:.7; }
.cal__dot{ display:inline-block; width:9px; height:9px; border-radius:2px; margin-right:.4rem; }
.cal__dot--free{ background:rgba(241,234,217,.25); }
.cal__dot--busy{ background:rgba(156,91,65,.7); }
.cal__note{ margin-top:.8rem; opacity:.5; }

.stay__bg{
  position:absolute; inset:0;
  background:url('../assets/brick-gallery-1600.webp') center/cover;
}
.stay__bg::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(100deg, rgba(23,21,15,.93) 30%, rgba(23,21,15,.78) 100%); }
.stay--photo .wrap{ position:relative; z-index:1; }
.stay--photo .cal{ background:rgba(33,30,21,.72); backdrop-filter:blur(10px); }

/* ── team: sticky photo + cards ──────────────────────────── */
.team{
  background:var(--bone); padding-block:var(--sec);
  position:relative; z-index:1; overflow:clip; --drift:120px;
}
.team > .wrap{ position:relative; z-index:1; }
.team-grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:1.2rem; }
.member{
  border:1px solid rgba(23,21,15,.14); border-radius:var(--r);
  background:#f6f1e4; overflow:hidden; display:flex; flex-direction:column;
}
/* Portrait band. Placeholder art for now, sized to the final crop so real
   photography drops in without touching the layout. */
.member__photo{
  /* height:auto is load-bearing: the width/height attributes map to CSS
     presentation hints, so without it height=1000 wins over aspect-ratio */
  display:block; width:100%; height:auto; aspect-ratio:4/5; object-fit:cover;
  background:var(--petrol-d);
}
.member__body{ padding:1.5rem 1.4rem 1.7rem; }
.member h3{ font-size:1.25rem; }
.member__role{ color:var(--clay); margin:.25rem 0 .7rem; }
.member p:last-child{ font-size:.88rem; opacity:.8; }
.sec-head--center{ margin-inline:auto; text-align:center; }

/* A canang sari drifting down the left margin of the team section, the daily
   offering the housekeeping team actually lays out. Sits behind the cards and
   turns slightly as it falls, so it reads as placed rather than pasted. */
/* --gut is the margin between the viewport edge and the card column; each
   offering is centred inside its own gutter, so nothing is ever clipped. */
.team{ --gut:calc((100vw - min(1200px, 100vw)) / 2 + var(--pad)); }
.team__offering{
  position:absolute; z-index:0; pointer-events:none; user-select:none;
  height:auto; opacity:.9;
  filter:drop-shadow(0 22px 34px rgba(23,21,15,.22));
  will-change:transform;
}
.team__offering--l{
  --ow:clamp(112px, 9.5vw, 200px);
  width:var(--ow); left:calc((var(--gut) - var(--ow)) / 2); top:13%;
}
/* 60% of the left one and set lower down the section */
.team__offering--r{
  --ow:clamp(67px, 5.7vw, 120px);
  width:var(--ow); right:calc((var(--gut) - var(--ow)) / 2); top:31%;
}
@media (max-width: 1180px){ .team__offering{ opacity:.5; } }
/* behind the cards it would only muddy them at phone widths */
@media (max-width: 900px){ .team__offering{ display:none; } }

/* ── contact ─────────────────────────────────────────────── */
.contact{ background:var(--bone-2); padding-block:var(--sec); position:relative; z-index:1; }
.contact__grid{ display:grid; grid-template-columns:5fr 7fr; gap:clamp(2rem,5vw,5rem); align-items:start; }
/* The enquiry form is hidden while email cannot send. Without this the details
   stay pinned in the 5fr column with seven twelfths of the row empty beside
   them. Keyed off the [hidden] attribute rather than a modifier class so that
   un-hiding the form restores the two-column layout on its own, with nothing
   else to remember. Browsers without :has() get the old wide gap, which is
   untidy rather than broken. */
.contact__grid:has(> .contact__forms[hidden]){ grid-template-columns:1fr; }
.contact__info h2{ margin-bottom:1rem; }
.contact__info > p{ opacity:.85; max-width:26rem; }
.contact__list{ list-style:none; margin-top:2rem; display:flex; flex-direction:column; gap:1.1rem; }
.contact__list li{ display:grid; grid-template-columns:110px 1fr; gap:1rem; font-size:.82rem; }
.contact__list span{ opacity:.5; text-transform:uppercase; letter-spacing:.1em; }
.contact__list a{ color:var(--petrol); }
.contact__formhead{
  font-size:1.25rem; margin-bottom:1.4rem;
  padding-bottom:.9rem; border-bottom:1px solid rgba(23,21,15,.14);
}

/* ── forms ───────────────────────────────────────────────── */
.form{ display:flex; flex-direction:column; gap:1.1rem; }
.form[hidden]{ display:none; }
.form__row{ display:grid; grid-template-columns:1fr 1fr; gap:1.1rem; }
.form label{
  display:flex; flex-direction:column; gap:.4rem;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em;
  text-transform:uppercase; opacity:.9;
}
.form input, .form select, .form textarea{
  font-family:var(--font-body); font-size:.95rem; color:var(--ink);
  padding:.8rem .9rem; border:1px solid rgba(23,21,15,.22); border-radius:var(--r);
  background:var(--bone); transition:border-color .2s;
}
.form input:focus, .form select:focus, .form textarea:focus{ outline:none; border-color:var(--petrol); }
.form textarea{ resize:vertical; }
.form .btn{ align-self:flex-start; }
.form__done{ color:var(--petrol); }

/* ── footer ──────────────────────────────────────────────── */
.footer{
  background:var(--ink); color:var(--bone);
  padding-block:clamp(3rem,6vw,4.5rem); position:relative; z-index:1;
}
.footer__mark{ overflow:hidden; text-align:center;
  margin-bottom:clamp(2rem,5vw,3.5rem); padding-inline:var(--pad); }
.footer__markwrap{ position:relative; display:inline-block; line-height:0; }
.footer__mark img:not(.footer__one){
  width:min(100%, 640px); height:auto; margin-inline:auto;
  opacity:.62; transition:opacity .7s;
  filter:drop-shadow(0 0 30px rgba(241,234,217,.16));
}
/* One tucked under the tail, kept as quiet as the faded wordmark it sits on */
.footer__one{
  position:absolute; right:-.5%; bottom:1%;
  width:clamp(2.4rem, 7vw, 4.4rem); height:auto;
  opacity:.82; pointer-events:none;
}
.footer__mark:hover .footer__one{ opacity:1; }
.footer__mark:hover img{ opacity:1; }
.footer__grid{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:2rem; align-items:start; }
.footer__brand{ font-family:var(--font-display); letter-spacing:.24em; font-size:1.3rem; margin-bottom:.8rem; }
.footer .mono{ opacity:.6; line-height:2; }
.footer__nav{ display:grid; grid-template-columns:1fr 1fr; gap:.2rem 2rem; }
.footer__nav a{ text-decoration:none; opacity:.7; }
.footer__nav a:hover{ opacity:1; }
.footer__meta a{ color:var(--amber-2); }
.footer__credit{ font-size:.62rem; opacity:.45; }

/* ── floating book pill ──────────────────────────────────── */
.bookpill{
  position:fixed; right:1.3rem; bottom:1.3rem; z-index:68;
  display:flex; align-items:center; gap:.55rem;
  font-family:var(--font-mono); font-size:.74rem; letter-spacing:.08em; text-transform:uppercase;
  background:var(--amber); color:var(--ink); text-decoration:none;
  padding:.85rem 1.25rem; border-radius:99px;
  box-shadow:0 14px 34px -12px rgba(23,21,15,.6);
  opacity:0; transform:translateY(16px); pointer-events:none;
  transition:opacity .4s, transform .4s, background .25s;
}
.bookpill.is-on{ opacity:1; transform:translateY(0); pointer-events:auto; }
.bookpill:hover{ background:var(--amber-2); }
.bookpill em{ font-style:normal; opacity:.65; }
.bookpill__dot{ width:7px; height:7px; border-radius:50%; background:var(--petrol-d);
  animation:pilldot 2s ease-in-out infinite; }
@keyframes pilldot{ 0%,100%{ opacity:.5; } 50%{ opacity:1; } }

/* ── selection ───────────────────────────────────────────── */
::selection{ background:var(--amber); color:var(--ink); }

/* ── film grain ──────────────────────────────────────────── */
.grain{
  position:fixed; inset:-50%; z-index:90; pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity:.05; animation:grain 9s steps(6) infinite;
}
@keyframes grain{
  0%,100%{ transform:translate(0,0); } 20%{ transform:translate(-6%,4%); }
  40%{ transform:translate(4%,-6%); } 60%{ transform:translate(-3%,-4%); } 80%{ transform:translate(5%,5%); }
}

/* ── custom cursor: a swallow ─────────────────────────────
   Three nested elements, each owning exactly one transform, because they are
   driven by different things and would otherwise overwrite each other:

     .cursor        x/y, from GSAP, follows the pointer
     .cursor__bird  rotation, from GSAP, banks with travel direction
     .cursor__mask  scale + tilt, from CSS, reacts to what is under it

   Collapsing any two of these into one element means the hover transition and
   the banking fight over the same property, and whichever wrote last wins. */
@media (pointer:fine){
  .cursor{
    display:block; position:fixed; top:0; left:0; z-index:96; pointer-events:none;
    opacity:0; transition:opacity .3s;
  }
  .cursor.is-live{ opacity:1; }

  /* The beak sits at 6.25% / 22.7% of the artwork. Anchoring that point to the
     pointer, and pivoting on it, means the bird turns about its beak rather
     than swinging its whole body around a centre it does not have. */
  .cursor__bird{
    display:block;
    transform:translate(-6.25%, -22.7%);
    transform-origin:6.25% 22.7%;
    will-change:transform;
  }
  .cursor__mask{
    display:block; height:44px; width:auto;
    transform-origin:6.25% 22.7%;
    transition:transform .3s cubic-bezier(.34,1.56,.64,1);
  }
  /* Ink body with a bone halo, so the same bird reads on the warm paper
     sections and on the near-black ones without swapping assets. */
  .cursor__mask path{ fill:var(--ink); }
  .cursor__mask{
    filter:
      drop-shadow(0 0 2px rgba(240,234,224,1))
      drop-shadow(0 0 5px rgba(240,234,224,.65))
      drop-shadow(0 3px 8px rgba(23,21,15,.45));
  }
  .cursor.is-hover .cursor__mask{ transform:scale(1.2) rotate(-8deg); }
  .cursor.is-label .cursor__mask{ transform:scale(1.1) rotate(7deg); }
  .cursor__label{
    position:absolute; top:26px; left:28px;
    width:74px; height:74px; border-radius:50%;
    display:flex; align-items:center; justify-content:center; text-align:center;
    font-size:.56rem; letter-spacing:.12em; text-transform:uppercase; line-height:1.25;
    padding:.4rem; color:var(--bone);
    background:rgba(23,21,15,.34);
    backdrop-filter:blur(9px) saturate(1.3);
    -webkit-backdrop-filter:blur(9px) saturate(1.3);
    border:1px solid rgba(241,234,217,.34);
    opacity:0; transform:scale(.82); transition:opacity .22s, transform .28s cubic-bezier(.34,1.56,.64,1);
  }
  .cursor.is-label .cursor__label{ opacity:1; transform:scale(1); }
  /* Universal, not an allow-list. The previous rule named a, button and the
     gallery track, so every component that set its own cursor — the culture
     panels, the accordion summaries, form labels and fields — punched a hole
     in the illusion and showed a native hand or beam. */
  html.has-cursor, html.has-cursor *{ cursor:none; }
}
html.film-open .cursor{ display:none; }
/* the film plays in a native video frame, so the real cursor comes back —
   higher specificity than the universal rule above, deliberately */
html.film-open.has-cursor,
html.film-open.has-cursor *{ cursor:auto; }

/* ── scroll progress + nav hide ──────────────────────────── */
.progress{
  position:fixed; top:0; left:0; right:0; height:2px; z-index:66;
  background:var(--amber); transform-origin:0 50%; transform:scaleX(0);
  pointer-events:none;
}
.nav{ transition:background .35s, box-shadow .35s, padding .35s, transform .45s cubic-bezier(.4,0,.2,1); }
.nav.is-hidden{ transform:translateY(-110%); }

/* ── SplitText line masks ────────────────────────────────── */
.st-mask{ overflow:hidden; }
.st-line{ will-change:transform; }

/* ── gallery drag affordances ────────────────────────────── */
.gallery{ position:relative; }
.gallery__track{ cursor:grab; touch-action:pan-y; }
.gallery__track.is-dragging{ cursor:grabbing; }
.gallery__track figure{ position:relative; }
.gallery__track img{ pointer-events:none; }
.gallery__progress{
  position:absolute; left:var(--pad); right:var(--pad); bottom:1.6rem;
  height:1px; background:rgba(241,234,217,.15);
}
.gallery__progress i{
  display:block; height:100%; background:var(--amber-2);
  transform-origin:0 50%; transform:scaleX(.08);
}
.gallery__hint{
  position:absolute; top:1.6rem; right:var(--pad);
  color:var(--bone); opacity:.5; letter-spacing:.22em; text-transform:uppercase;
}

/* ── lightbox ────────────────────────────────────────────── */
.lightbox{
  position:fixed; inset:0; z-index:85; background:rgba(23,21,15,.96);
  display:flex; align-items:center; justify-content:center; gap:1rem;
  padding:clamp(1rem,4vw,3rem);
}
.lightbox__stage{ max-width:min(1100px, 88vw); text-align:center; }
.lightbox__stage img{
  max-height:78vh; max-width:100%; width:auto; margin-inline:auto;
  border-radius:var(--r); object-fit:contain;
}
.lightbox__stage figcaption{
  display:flex; justify-content:space-between; gap:1rem;
  color:var(--bone); opacity:.65; margin-top:1rem;
}
.lightbox__x{
  position:absolute; top:1.2rem; right:1.4rem; z-index:2;
  background:none; border:0; color:var(--bone); font-size:2.2rem; line-height:1; opacity:.7;
}
.lightbox__x:hover{ opacity:1; }
.lightbox__nav{
  flex-shrink:0; width:54px; height:54px; border-radius:50%;
  background:rgba(241,234,217,.06); border:1px solid rgba(241,234,217,.25);
  color:var(--bone); font-size:1.1rem; transition:background .25s, border-color .25s;
}
.lightbox__nav:hover{ background:rgba(226,171,85,.2); border-color:var(--amber-2); }

/* ── culture: the language table ─────────────────────────── */
.phrase__title{ letter-spacing:.2em; text-transform:uppercase; color:var(--amber-2); margin-bottom:1.2rem; }
.phrase__table{ width:100%; border-collapse:collapse; }
.phrase__table td{ padding:.6rem 0; border-bottom:1px solid rgba(241,234,217,.1); font-size:.95rem; }
.phrase__table td:first-child{ font-family:var(--font-display); font-size:1.05rem; padding-right:1.5rem; white-space:nowrap; }
.phrase__table i{ font-style:normal; opacity:.5; font-size:.7rem; }

/* ═══ responsive ═════════════════════════════════════════ */
@media (max-width: 1024px){
  .team-grid{ grid-template-columns:repeat(2,1fr); }
  .area-cards{ grid-template-columns:repeat(2,1fr); }
  /* cards now span most of the width, so pull the penjor further out */
  .reviews__pole{ height:70svh; opacity:.38; }
  .reviews__pole--l{ left:-8vw; }
  .reviews__pole--r{ right:-8vw; }
}
/* No room beside the cards on a phone: the poles would only sit behind them */
@media (max-width: 760px){ .reviews__deco{ display:none; } }
@media (max-width: 900px){
  /* No pinned film timeline below 900, so the section must not reserve 460vh
     for an animation that never runs — that reserve rendered as screens of
     bare ink. And .film__frame's scale(.3) is only ever undone by that same
     timeline, so it is reset here and re-animated by initFilmGrow(). */
  .film{ height:auto; }
  .film__pin{ height:auto; overflow:visible; }
  .film__frame{ transform:none; }
  /* matches the JS threshold: above 900 the pinned timeline slides these
     panels sideways, below it there is no animation, so they must stack */
  .ftrack{ position:static; width:100%; flex-direction:column; transform:none !important; }
  .ftrack__panel{ width:100%; height:auto; }
  .ftrack__panel--film{ height:100svh; }
  .ftrack__panel--svc{ padding-block:var(--sec); }
  /* no reveal-from-underneath on phones: keep booking in normal flow */
  .stayzone{ height:auto; }
  .stay--photo{ height:auto; }
  .reviews{ margin-bottom:0; }
  /* No room to fan on a phone — lay the year out as a legible grid instead. */
  .trip{ flex-direction:column; min-height:0; }
  .trip__panel{ min-height:150px; flex:none; }
  .trip__panel.is-open{ flex:none; }
  .feature, .feature--flip{ grid-template-columns:1fr; gap:1.4rem; }
  .feature--flip .feature__img{ order:0; }
  .location__grid{ grid-template-columns:1fr; }
  .location__map iframe{ min-height:300px; }
  .culture__lower{ grid-template-columns:1fr; }
  .stay__grid{ grid-template-columns:1fr; }
  .contact__grid{ grid-template-columns:1fr; }
  .footer__grid{ grid-template-columns:1fr; gap:2.5rem; }
}
@media (max-width: 900px){
  .hero__rail{ display:none; }
  /* no page margin to bleed into on small screens — sit at the block edge */
  .feature__numeral,
  .feature--flip .feature__numeral{
    height:clamp(9rem, 33vw, 13.5rem); top:auto; bottom:-.5rem;
    right:0; left:auto; opacity:.24;
  }
}
@media (max-width: 640px){
  .amen__facts{ grid-template-columns:1fr; gap:1.1rem; }
  /* one row at any width: the buttons share the space and wrap their own text
     rather than the toggle dropping to a second line */
  .hero__actions{ gap:.45rem; }
  .hero__actions .btn{ flex:1 1 0; min-width:0; padding:.6rem .55rem;
    font-size:.74rem; text-align:center; line-height:1.2; }
  .hero__actions .hum{ flex:0 0 auto; }
  /* two-up on phones: the card keeps desktop type otherwise, and a 20px name
     with 1.5rem padding leaves a ~90px measure that breaks every third word */
  .team-grid{ gap:.75rem; }
  .member__body{ padding:1rem .9rem 1.2rem; }
  .member h3{ font-size:1.05rem; }
  .member__role{ font-size:.72rem; margin:.2rem 0 .5rem; }
  .member p:last-child{ font-size:.8rem; line-height:1.45; }
  .intro__stats{ grid-template-columns:repeat(2,1fr); }
  .amenlist{ columns:1; }
  .tcard{ width:100%; }
  .tsummary__grid{ grid-template-columns:1fr; gap:.7rem; }
  .reviews__score{ flex-direction:column; gap:.5rem; }
  .reviews__of{ align-items:center; }
  .smodal__card{ grid-template-columns:1fr; }
  .smodal__nav{ width:38px; height:38px; }
  .smodal__img img{ max-height:32svh; }
  .film__note{ white-space:normal; width:82%; }
  .vmodal__crest{ width:70%; }
  .svcrow__hit{ grid-template-columns:2.2rem 1fr; padding:1.1rem .4rem; gap:.4rem 1rem; }
  .svcrow__arrow{ display:none; }
  .svcrow__price{ grid-column:2; justify-self:start; font-size:.7rem; }
  .quotes__mark{ font-size:6rem; }
  .bookpill{ right:.9rem; bottom:.9rem; padding:.75rem 1.05rem; }
  .area-cards{ grid-template-columns:1fr; }
  .form__row{ grid-template-columns:1fr; }
  /* two columns get cramped well before the panel runs out of room */
  .amen{ grid-template-columns:1fr; align-items:start; }
  .amen__facts{ grid-template-columns:1fr 1fr; }
  /* the row wraps to stacked blocks here, where ranged-right body text reads
     badly and there is no button beside it to align to */
  .hero__row{ justify-content:flex-start; }
  .hero__line{ text-align:left; }
  .hero__foot{ justify-content:center; }
  .hero__foot .mono:last-child{ display:none; }
  .hum{ width:46px; height:46px; }
  .hum__bars{ height:18px; gap:2.5px; }
  .hum__bars i{ width:2.5px; }
  .hum__mute{ width:20px; height:20px; }
  .hum--float{ right:.9rem; bottom:.9rem; }
  body:has(.hum--float.is-shown) .bookpill{ right:4.1rem; }
}

/* ═══ reduced motion ═════════════════════════════════════ */
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  *, *::before, *::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; }
  .gate{ display:none; }
  .feature__img img{ transform:none; }
}


/* The seasons fan is laid out with absolute percentage offsets tuned for a
   desktop canvas. Below ~1100px the cards land on top of the heading, so the
   grid fallback takes over there instead — this threshold must stay in step
   with the one guarding the fan in main.js. */
/* Full nav needs ~1220px for the wordmark, links, portal pill and CTA; below
   that the hamburger takes over rather than letting the CTA run off the edge. */
@media (max-width: 1220px){
  .nav__links, .nav__cta, .nav__portal{ display:none; }
  .nav__burger{ display:flex; margin-left:auto; }
}

/* Below ~1100px or on a short viewport the fan has no room, so the section
   falls back to a clean two-column grid. Must match the JS gate. */
@media (max-width: 1099px), (max-height: 699px){
  .seasons{ height:auto; padding-block:var(--sec); }
  .seasons__pin{ height:auto; overflow:visible; }
  .seasons__head{ padding-top:0; }
  .seasons__sub{ opacity:.75 !important; transform:none !important; }
  .seasons__prompt{ display:none; }
  .seasons__stage{
    position:static; display:grid; grid-template-columns:1fr 1fr;
    gap:.7rem; padding:2rem var(--pad) 0; max-width:var(--wrap); margin-inline:auto;
  }
  .scard{
    position:static; width:auto; transform:none !important;
    aspect-ratio:3/4; opacity:1 !important;
  }
  .scard--core{
    grid-column:1 / -1; width:auto; aspect-ratio:16/10;
    flex-direction:row; gap:2px;
  }
  .scard--core .scard__half{ border-radius:6px; }
  .scard__half + .scard__half{ margin-top:0; }
  .scard__half:hover{ flex:1; }
  .seasons__hint{ position:static; transform:none; text-align:center; margin-top:1.4rem; }
  .yearbar{ position:static; transform:none; opacity:1 !important; width:auto;
    margin:1.6rem var(--pad) 0; }
  .yearbar__t{ display:none; }
  .yearbar__avg{ margin-left:0; width:100%; text-align:center; margin-top:.3rem; }
  .seasons .backdrop__img{ top:0; height:100%; }
}

/* ── modals on touch devices ──────────────────────────────────
   Three fixes, all only observable on a phone:

   1. Scrolling. The card stacked image-over-body and let the body carry its own
      max-height, so image + body could exceed the card while the card itself was
      overflow:visible — content ran off the bottom with nothing to scroll. The
      card is the scroll container here and the body just flows.
   2. Height. svh is the *small* viewport (toolbar showing), so sizing to it and
      padding by the safe-area inset keeps the card clear of Safari's chrome
      whether the toolbar is expanded or collapsed.
   3. The close control. It was a 19x32 ink glyph on no background, sitting over
      the photo at the top of the stacked card: invisible, and half the minimum
      tap size. It gets a legible chip and a 44px target. */
@media (max-width: 640px), (pointer: coarse) and (max-width: 900px){
  .smodal, .amodal{
    padding:.75rem;
    padding-bottom:calc(.75rem + env(safe-area-inset-bottom));
    align-items:flex-start;
  }
  .smodal__card, .amodal__card{
    max-height:calc(100svh - 1.5rem - env(safe-area-inset-bottom));
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;
    margin-top:.75rem;
  }
  /* the body no longer competes with the card for the scroll */
  .smodal__body{ max-height:none; overflow:visible; }
  .amodal__body{ overflow:visible; }
  .amodal__top{ position:sticky; top:0; z-index:2; background:var(--bone); }

  /* room for the prev/next chips pinned to the card's bottom edge */
  .smodal__body{ padding-bottom:4.2rem; }

  /* fixed, not absolute: the card is now the scroll container, so anything
     anchored to it scrolls away — on a short screen the close control vanished
     as soon as you reached the middle of a long service description. */
  .smodal__x, .vmodal__x{
    position:fixed; top:1.15rem; right:1.15rem; z-index:5;
  }
  .smodal__nav{
    position:fixed; bottom:calc(1.1rem + env(safe-area-inset-bottom)); top:auto;
    transform:none; z-index:5;
  }
  .smodal__nav--prev{ left:auto; right:4.6rem; }
  .smodal__nav--next{ right:1.15rem; }
  /* clear of the 38px prev chip, which ends at ~7rem from the right edge */
  .smodal__count{ position:fixed; bottom:calc(1.9rem + env(safe-area-inset-bottom)); right:7.8rem; z-index:5; }

  .smodal__x, .amodal__x, .vmodal__x{
    z-index:5;
    width:44px; height:44px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    font-size:1.5rem; line-height:1; opacity:1;
    color:var(--bone); background:rgba(23,21,15,.62);
    backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    border:1px solid rgba(241,234,217,.28);
  }
  .amodal__x{ position:sticky; float:right; margin:-.4rem -.4rem 0 0; }
}

/* ── touch ergonomics ─────────────────────────────────────────
   Applies wherever the primary input is a finger, at any width.
   44px is the Apple HIG / WCAG target minimum; 16px inputs stop iOS
   zooming the page the moment a field takes focus. */
@media (pointer: coarse){
  .form input, .form select, .form textarea{ font-size:16px; min-height:44px; }
  .form textarea{ min-height:110px; }
  .btn{ min-height:44px; display:inline-flex; align-items:center; justify-content:center; }
  .nav__burger{ width:44px; height:44px; }
  .nav__brand{ display:inline-flex; align-items:center; min-height:44px; }
  .cal__nav{ width:44px; height:44px; }
  /* min-height is deterministic where guessed padding kept landing at 37-43px */
  .area-card__link,
  .contact__list a,
  .footer__nav a,
  .footer__meta a,
  .location__coords a{
    display:inline-flex; align-items:center; min-height:44px;
  }
  /* the fixed pair need clearance from the gesture bar */
  .bookpill{ bottom:calc(.9rem + env(safe-area-inset-bottom)); }
  .bookpill, .hum--float{ bottom:max(.9rem, calc(.9rem + env(safe-area-inset-bottom))); }
  .hum--float{ bottom:calc(.9rem + env(safe-area-inset-bottom)); }
}

/* ═══════════════════════════════════════════════════════════════
   PÄÄSKYLÄNRINNE ADDITIONS
   Appended rather than spliced. The one CSS lesson from the previous
   build was that cutting a stylesheet by index range silently
   destroyed 40% of it, so nothing above this line is rewritten:
   new classes are declared here and existing ones are only
   overridden where a rule has to change.
   ═══════════════════════════════════════════════════════════════ */

/* ── stills where the original had video ─────────────────────
   There is no film of this flat, so the hero, the pinned panel and the
   location backdrop each carry a photograph. Same box, same object-fit,
   so every layout rule above still applies. */
.hero__still{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.film__still{ width:100%; height:100%; object-fit:cover; display:block; }
.backdrop__still{
  position:absolute; left:0; width:100%;
  top:calc(var(--drift, 120px) * -0.5);
  height:calc(100% + var(--drift, 120px));
  object-fit:cover; will-change:transform;
}
html.no-motion .backdrop__still{ top:0; height:100%; }
@media (prefers-reduced-motion: reduce){ .backdrop__still{ top:0; height:100%; } }

/* ── hero wordmark, set in type ───────────────────────────────
   The original hero was a drawn logotype PNG. There is no logotype for this
   property and inventing one would be a brand decision, not a build decision,
   so the name is set in the display face with the same shadow-and-bloom
   treatment that lifted the artwork off its background. */
.hero__wordmark{
  display:block; font-family:var(--font-display);
  font-size:clamp(2.6rem, 9.5vw, 7.4rem); line-height:.92;
  letter-spacing:-.02em; color:var(--bone);
  text-shadow:0 3px 16px rgba(12,11,8,.72), 0 0 48px rgba(240,234,224,.28);
}
.hero__wordmark-b{ font-style:italic; color:var(--amber-2); }
.hero__six{
  position:absolute; right:-.55em; bottom:.02em;
  font-family:var(--font-display); font-style:italic;
  font-size:clamp(1.6rem, 5vw, 3.6rem); line-height:1; color:var(--amber-2);
  text-shadow:0 2px 14px rgba(12,11,8,.7), 0 0 26px rgba(226,171,85,.3);
  pointer-events:none;
}
.hero__title{ line-height:normal; }
.hero__markwrap{ line-height:normal; padding-right:.6em; }

/* film panel: the words are a caption now, not a play button */
.film__words{ background:none; border:0; padding:0; cursor:default; }

/* ── yearbar: daylight, not rainfall ─────────────────────────
   The Bali chart had two flat states, wet and dry. Helsinki's axis is
   continuous, so --fill carries hours of daylight as a percentage and the
   bar grows with it. Colour still splits the year in two halves. */
.yearbar__col--light .yearbar__band{ background:var(--amber); }
.yearbar__col--dark .yearbar__band{ background:var(--petrol); }
.yearbar__col .yearbar__band{
  height:calc(4px + (var(--fill, 50%) / 100%) * 26px);
  align-self:end;
}
.yearbar__col{ justify-content:flex-end; }
.yearbar__col.is-now .yearbar__band{ height:calc(6px + (var(--fill, 50%) / 100%) * 30px); margin-block:0; }
.yearbar__dot--dark{ background:var(--petrol); }
.yearbar__dot--light{ background:var(--amber); margin-left:.8rem; }

/* ── host section: two wide cards, no portraits ──────────────
   One host and no photograph of him, so the four-portrait grid collapses to
   two text cards. member__photo simply never appears. */
.team-grid--solo{ grid-template-columns:repeat(2,1fr); max-width:56rem; margin-inline:auto; }
.member--wide .member__body{ padding:1.8rem 1.7rem 2rem; }
@media (max-width: 720px){ .team-grid--solo{ grid-template-columns:1fr; } }

/* ── what's on: a horizontal rail ────────────────────────────
   Pinned and scrubbed sideways by initReveal in main.js on wide screens;
   an ordinary horizontal scroller on a phone, where pinning fights the
   browser's own gesture handling. */
.rail{ padding-block:var(--sec) calc(var(--sec) * .6); background:var(--bone); overflow:clip; }
.rail__track{
  display:flex; gap:1.1rem; padding:2.5rem var(--pad) 1rem;
  will-change:transform;
}
/* Native horizontal scroll is the baseline and the whole behaviour on touch.
   .rail--drag is added by JS only when Draggable takes over, and it has to
   switch the native scrolling off: leaving both on means the browser's
   momentum and Draggable's inertia both move the track, in different
   directions, at different rates. */
.rail:not(.rail--drag) .rail__track{
  overflow-x:auto; scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch; scrollbar-width:none;
}
.rail:not(.rail--drag) .rail__track::-webkit-scrollbar{ display:none; }
.rail--drag .rail__track{ overflow:visible; will-change:transform; }
.rail--drag{ overflow:clip; }
.ecard{
  flex:0 0 clamp(15rem, 26vw, 20rem); scroll-snap-align:start;
  border:1px solid rgba(23,21,15,.14); border-radius:var(--r);
  background:#f6f1e4; overflow:hidden;
  display:flex; flex-direction:column;
}
.ecard__img{ margin:0; aspect-ratio:4/3; overflow:hidden; background:var(--petrol-d); }
.ecard__img img{
  width:100%; height:100%; object-fit:cover; display:block;
  transition:transform .6s cubic-bezier(.2,.8,.3,1);
  /* the drag must not start a native image drag */
  user-select:none; -webkit-user-drag:none;
}
.ecard:hover .ecard__img img{ transform:scale(1.045); }
.ecard__body{
  padding:1.2rem 1.25rem 1.4rem;
  display:flex; flex-direction:column; gap:.5rem; flex:1;
}
.ecard__when{
  font-size:.6rem; letter-spacing:.18em; text-transform:uppercase;
  color:var(--clay);
}
.ecard__title{ font-family:var(--font-display); font-size:1.35rem; line-height:1.15; }
.ecard__venue{ font-size:.72rem; opacity:.6; }
.ecard__blurb{ font-size:.88rem; line-height:1.55; opacity:.82; }
.ecard__link{
  margin-top:auto; padding-top:.6rem; font-size:.72rem;
  color:var(--clay); text-decoration:none; border-bottom:1px solid currentColor;
  align-self:flex-start;
}
.ecard__link:hover{ color:var(--ink); }
/* a season is a window, not an occasion: mark it differently */
.ecard--season{ background:transparent; border-style:dashed; }
.ecard--season .ecard__when{ color:var(--petrol); }

/* ── reviews: honest empty state ─────────────────────────────
   The section is hidden until real reviews are pasted into main.js, so this
   only styles the populated case; nothing renders an apology. */
.reviews__num[data-review-score]{ font-variant-numeric:tabular-nums; }

/* ── hero legibility ──────────────────────────────────────────
   The Bali hero was a dark twilight video, so a mostly-vertical scrim was
   enough. This photograph is bright exactly where the text sits: a white
   sloped ceiling and a lit roof window fill the upper left. The eyebrow, set
   in amber, disappeared into the window entirely. A second scrim runs left to
   right under the text column, which leaves the brick and the sofa on the
   right at full strength. */
.hero__shade{
  background:
    linear-gradient(95deg, rgba(23,21,15,.72) 0%, rgba(23,21,15,.45) 38%, rgba(23,21,15,0) 66%),
    linear-gradient(to bottom, rgba(23,21,15,.5) 0%, rgba(23,21,15,.12) 34%, rgba(23,21,15,.8) 100%);
}
.hero__eyebrow{ color:#f0c489; text-shadow:0 1px 3px rgba(12,11,8,.9), 0 2px 20px rgba(12,11,8,.8); }
.hero__line{ text-shadow:0 1px 12px rgba(12,11,8,.75); }
.hero__six{ right:-.42em; }
@media (max-width: 700px){
  .hero__shade{
    background:linear-gradient(to bottom, rgba(23,21,15,.62) 0%, rgba(23,21,15,.3) 34%, rgba(23,21,15,.86) 100%);
  }
}

/* ── intro: the gap the divider used to make ──────────────────
   The original separated the manifesto from the paragraph below it with a
   drawn divider image, and .intro__villa therefore had no top margin of its
   own. That artwork is gone with the rest of the Balinese ornament, so the two
   blocks ran straight into each other. A hairline does the same job without an
   asset: short, centred, and the amber picks up the accent rather than drawing
   a line across the page. */
.intro__villa{
  margin-top:clamp(2.6rem, 6vw, 4.2rem);
  position:relative;
}
.intro__villa::before{
  content:""; position:absolute; left:50%; translate:-50% 0;
  top:calc(clamp(2.6rem, 6vw, 4.2rem) * -0.5);
  width:clamp(3.5rem, 8vw, 5.5rem); height:1px;
  background:linear-gradient(90deg,
    rgba(198,138,63,0) 0%, rgba(198,138,63,.85) 50%, rgba(198,138,63,0) 100%);
}

/* The gate is gone, so its rules are dead weight. Left in place rather than
   cut out of the middle of the stylesheet, because deleting CSS by position is
   how 40% of a stylesheet went missing on the previous project. Nothing in the
   markup carries these classes any more. */

/* ── the getting-out panel's caption ──────────────────────────
   The original caption was seven words ("Best with sound…") and could get away
   with white-space:nowrap, which is why it had it. This one names a route and
   three timings, and on a single unwrappable line it ran off both edges of the
   viewport. Wrap it and hold it to a readable measure; the mobile breakpoint
   already did this, it just never applied above 720px. */
.film__note{
  white-space:normal;
  max-width:min(52rem, 82vw);
  line-height:1.6;
  text-wrap:pretty;
}

/* ── touch targets ───────────────────────────────────────────
   The "more ↗" link on an event card measured 46x30 at 375px, with an 11.5px
   label. It is the only way out of a card and into the listing itself, and it
   is the sort of target that gets tapped twice because the first one missed.
   Coarse pointers only, so nothing about the desktop card changes. */
@media (pointer: coarse){
  .ecard__link{ min-height:44px; display:inline-flex; align-items:center; font-size:.78rem; }
}
