/**
 * Brovad Sands Lodge — motion & scroll-effects system. Pairs with assets/js/motion.js.
 *
 * 1. Content is visible with JS off: every pre-animation hidden state is scoped under
 *    `html.js`, added by the wp_head one-liner in bsl_motion_boot().
 * 2. prefers-reduced-motion:reduce DISABLES the system. Two independent kill switches at
 *    the bottom: the media query, and `html.mo-off`. Either alone is sufficient.
 * 3. Only transform/opacity animate. Sole exception: the clip-path wipe the .reveal-mask
 *    contract names — clip-path is paint-only and never triggers layout.
 *
 * Layout defaults use :where() (zero specificity) so pages.css overrides them without
 * !important. content-visibility is used nowhere (it changes scroll height mid-scroll).
 * will-change is applied only while an element is being driven, and dropped when it stops.
 */

:root{--mo-ease:cubic-bezier(.22,.61,.36,1);--mo-soft:cubic-bezier(.16,.84,.44,1);--mo-rise:16px;--mo-dur:720ms}

/* --- 0. Page transition: entry fade only. No outbound link interception — that delays
   navigation on slow connections and breaks the back button. `backwards` (not `both`)
   leaves no filled transform, so no stray containing block forms around the booking bar. */
html.js .site-main{animation:mo-enter 280ms var(--mo-ease) backwards}
@keyframes mo-enter{from{opacity:0;transform:translate3d(0,10px,0)}to{opacity:1;transform:none}}

/* --- 1. .reveal — fade + 16px rise. data-delay="120" → --mo-delay --------- */
html.js .reveal{opacity:0;transform:translate3d(0,var(--mo-rise),0);transition:opacity var(--mo-dur) var(--mo-ease),transform var(--mo-dur) var(--mo-ease);transition-delay:var(--mo-delay,0ms)}
html.js .reveal.is-in{opacity:1;transform:none}

/* --- 2. .reveal-stagger — children 90ms apart; JS writes --mo-delay ------- */
html.js .reveal-stagger>*{opacity:0;transform:translate3d(0,var(--mo-rise),0);transition:opacity var(--mo-dur) var(--mo-ease),transform var(--mo-dur) var(--mo-ease);transition-delay:var(--mo-delay,0ms)}
html.js .reveal-stagger.is-in>*{opacity:1;transform:none}

/* --- 3. .reveal-mask — clip-path wipe upward, headings only. Negative top/bottom insets
   stop descenders being shaved at rest. */
html.js .reveal-mask{opacity:0;clip-path:inset(-.2em 0 100% 0);transform:translate3d(0,10px,0);transition:clip-path 820ms var(--mo-soft),transform 820ms var(--mo-soft),opacity 420ms linear;transition-delay:var(--mo-delay,0ms)}
html.js .reveal-mask.is-in{opacity:1;clip-path:inset(-.2em 0 -.2em 0);transform:none}

/* --- 4. .parallax — data-speed default 0.18, driven by the shared rAF loop, which writes
   --mo-py clamped so the element cannot leave its container. OFF below 768px: jittery on
   touch and costly on battery; motion.js skips the maths there too. */
@media (min-width:768px){
  html.js .parallax.mo-p{transform:translate3d(0,var(--mo-py,0px),0)}
  html.js .parallax.mo-p.mo-live{will-change:transform}
}

/* --- 5. .zoom-in — 1.0 → 1.06 while in view. Put .zoom-in on the WRAPPER, not the image:
   the wrapper clips, so a scaled image can never spill and widen the page. A .zoom-in with
   no image child scales itself (motion.js marks it .mo-self). */
:where(.zoom-in:not(img):not(picture)){overflow:hidden}
html.js .zoom-in>img,html.js .zoom-in>picture>img,html.js .zoom-in.mo-self,html.js img.zoom-in{transform:scale(1);transform-origin:center;transition:transform 2600ms var(--mo-soft)}
html.js .zoom-in.is-in>img,html.js .zoom-in.is-in>picture>img,html.js .zoom-in.mo-self.is-in,html.js img.zoom-in.is-in{transform:scale(1.06)}

/* --- 6. .pin-section / .pin-track — sticky showcase, DESKTOP ONLY. position:sticky plus a
   transform on the steps, never JS-driven fixed positioning: sticky is GPU-cheap and
   degrades to a plain vertical stack the instant the media query stops matching. motion.js
   sets --mo-steps (child count) and drives --mo-pin (fractional step index) from the shared
   rAF loop. .pin-track should be a direct child. Below 1024px there are no rules at all. */
@media (min-width:1024px){
  html.js .pin-section.mo-pin{position:relative;min-height:calc(100vh + (var(--mo-steps,1) - 1) * 80vh)}
  html.js .pin-section.mo-pin .pin-track{position:sticky;top:0;height:100vh;overflow:hidden;display:flex;align-items:stretch}
  html.js .pin-section.mo-pin .pin-track>*{flex:0 0 100%;width:100%;min-width:100%;transform:translate3d(calc(var(--mo-pin,0) * -100%),0,0)}
}

/* --- 7. .hscroll — native overflow-x:auto; the browser does drag, momentum and snapping
   better than any JS rebuild. Scrollbar hidden, affordance not: a mask-image edge fade plus
   real <button> prev/next controls (desktop) and arrow keys, both injected by motion.js. */
:where(.hscroll){display:flex;align-items:stretch;gap:18px}
:where(.hscroll-item){flex:0 0 auto}
.hscroll{--mo-f1:28px;--mo-f2:48px;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none;-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 var(--mo-f1),#000 calc(100% - var(--mo-f2)),transparent 100%);mask-image:linear-gradient(90deg,transparent 0,#000 var(--mo-f1),#000 calc(100% - var(--mo-f2)),transparent 100%)}
.hscroll::-webkit-scrollbar{width:0;height:0;display:none}
.hscroll.mo-start{--mo-f1:0px}
.hscroll.mo-end{--mo-f2:0px}
.hscroll-item{scroll-snap-align:start;scroll-snap-stop:always}
.hscroll:focus-visible{outline:2px solid var(--cobalt-deep,#00607D);outline-offset:4px}
.hscroll-ui,.hscroll-ui[hidden]{display:none}
@media (min-width:1024px){
  .hscroll-ui{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}
  .hscroll-ui[hidden]{display:none}
  .hscroll-ui button{width:44px;height:44px;border-radius:50%;border:1px solid var(--line,#90E0DA);background:#fff;color:var(--ink,#07303C);font:300 17px/1 var(--body,sans-serif);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .25s ease,color .25s ease,border-color .25s ease,opacity .25s ease}
  .hscroll-ui button:hover:not([disabled]){background:var(--aqua,#40C8CC);color:var(--ink,#07303C);border-color:var(--aqua,#40C8CC)}
  .hscroll-ui button[disabled]{opacity:.32;cursor:default}
  .hscroll-ui button:focus-visible{outline:2px solid var(--cobalt-deep,#00607D);outline-offset:2px}
}

/* --- 8. .kb-hero / .kb-slide — Ken Burns still sequence. THERE IS NO HERO VIDEO: the three
   .mp4 files on disk are 720x960 vertical phone clips of the wrong subject. Each slide
   drifts scale 1.0 → 1.08 while translating a couple of percent over ~9s, with a 1.6s
   crossfade; alternate slides drift the other way so it never reads as a loop. The first
   slide carries .on server-side, so with JS off the hero is one sharp still, not a blank box.
   TO SWAP IN REAL FOOTAGE LATER: drop <video class="kb-video" muted loop playsinline
   poster="…"> in as the first child of .kb-hero. The rule below already stacks it over the
   stills at full bleed; the stills become the poster / no-JS fallback. Nothing else changes. */
.kb-hero{position:relative;overflow:hidden;isolation:isolate}
.kb-slide{position:absolute;inset:0;background-size:cover;background-position:center;background-repeat:no-repeat;opacity:0;z-index:0}
.kb-slide.on{opacity:1;z-index:1}
.kb-hero>video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:2}
html.js .kb-slide{transition:opacity 1600ms linear}
html.js .kb-slide.on{animation:mo-kb 10600ms var(--mo-soft) both}
html.js .kb-slide.on:nth-child(2n){animation-name:mo-kb-b}
@keyframes mo-kb{from{transform:scale(1.001) translate3d(0,0,0)}to{transform:scale(1.08) translate3d(-2%,-1.5%,0)}}
@keyframes mo-kb-b{from{transform:scale(1.001) translate3d(0,0,0)}to{transform:scale(1.08) translate3d(2%,-2%,0)}}
/* Scroll-linked drift on top of the keyframes, via the independent `translate` property so
   it composes with the animation's `transform` instead of fighting it. */
@media (min-width:768px){html.js .kb-hero.mo-live .kb-slide{translate:0 var(--mo-py,0px)}}

/* --- 9. KILL SWITCH. style.css ships *{animation-duration:.01ms!important} in its own
   reduced-motion block; .01ms is not 0s and * (0,0,0) loses to any class selector, so these
   restate 0s at higher specificity — and this file is enqueued after style.css. .kb-slide
   keeps its opacity rules (they stack the hero: layout, not motion) and loses only motion. */
html.mo-off .site-main,html.mo-off .reveal,html.mo-off .reveal-stagger,html.mo-off .reveal-stagger>*,
html.mo-off .reveal-mask,html.mo-off .parallax,html.mo-off .zoom-in,html.mo-off .zoom-in>img,
html.mo-off .zoom-in>picture>img,html.mo-off .kb-slide,html.mo-off .pin-track>*,html.mo-off .hscroll{
  animation:none!important;animation-duration:0s!important;animation-delay:0s!important;
  transition:none!important;transition-duration:0s!important;transition-delay:0s!important;
  transform:none!important;translate:none!important;will-change:auto!important}
html.mo-off .site-main,html.mo-off .reveal,html.mo-off .reveal-stagger,html.mo-off .reveal-stagger>*,
html.mo-off .reveal-mask,html.mo-off .parallax,html.mo-off .zoom-in{opacity:1!important;clip-path:none!important}
html.mo-off .pin-section,html.mo-off .pin-section .pin-track{position:static!important;height:auto!important;min-height:0!important;display:block!important;overflow:visible!important}
html.mo-off,html.mo-off .hscroll{scroll-behavior:auto!important}

@media (prefers-reduced-motion:reduce){
  html .site-main,html .reveal,html .reveal-stagger,html .reveal-stagger>*,html .reveal-mask,
  html .parallax,html .zoom-in,html .zoom-in>img,html .zoom-in>picture>img,html .kb-slide,
  html .pin-track>*,html .hscroll{
    animation:none!important;animation-duration:0s!important;animation-delay:0s!important;
    transition:none!important;transition-duration:0s!important;transition-delay:0s!important;
    transform:none!important;translate:none!important;will-change:auto!important}
  html .site-main,html .reveal,html .reveal-stagger,html .reveal-stagger>*,html .reveal-mask,
  html .parallax,html .zoom-in{opacity:1!important;clip-path:none!important}
  html .pin-section,html .pin-section .pin-track{position:static!important;height:auto!important;min-height:0!important;display:block!important;overflow:visible!important}
  html,html .hscroll{scroll-behavior:auto!important}
}

/* Scroll buttons on touch too.
   They were desktop-only (min-width:1024px) on the assumption that a swipe is
   the mobile affordance. But the section still prints "Scroll sideways", so a
   phone user is told to scroll and given nothing to press. The buttons are
   already 44x44 and keyboard-reachable, so showing them below 1024px costs
   nothing and makes the instruction true. They stay hidden via [hidden] when
   the track does not actually overflow. */
@media (max-width:1023px){
  .hscroll-ui{display:flex;justify-content:center;gap:14px;margin-top:16px}
  .hscroll-ui[hidden]{display:none}
  .hscroll-ui button{
    width:44px;height:44px;border-radius:50%;
    border:1px solid var(--line,#90E0DA);
    background:#fff;color:var(--ink,#07303C);
    font-size:20px;line-height:1;cursor:pointer;
    display:flex;align-items:center;justify-content:center;
  }
  .hscroll-ui button[disabled]{opacity:.32;cursor:default}
}
