/* Progressive enhancement: without JS everything remains visible. Add data-reveal
   to an effect wrapper. main.js adds .is-visible when the wrapper enters the viewport
   and removes it again when the wrapper has fully left, so every effect replays
   (forwards on the way in, backwards on the way out). After 1.6 s in view the
   wrapper also receives .is-settled, which starts the gentle ambient loops below. */
.motion-ready .photo[data-reveal]:not(.is-visible) { background:transparent; }
.motion-ready [data-reveal] {
  transition:opacity var(--motion-medium) var(--ease-soft),transform var(--motion-medium) var(--ease-out-cinematic),clip-path var(--motion-cinematic) var(--ease-out-cinematic),filter var(--motion-medium) var(--ease-soft);
  transition-delay:var(--delay,0ms)
}

.motion-ready [data-reveal].fx-reveal:not(.is-visible) {
  opacity:0;
  transform:translateY(25px)
}

.motion-ready [data-reveal].fx-soft-focus:not(.is-visible) {
  opacity:0;
  filter:blur(16px);
  transform:scale(1.025)
}

.motion-ready [data-reveal].fx-whip-horizontal:not(.is-visible) {
  opacity:0;
  transform:translateX(-12%);
  filter:blur(10px)
}

.motion-ready [data-reveal].fx-whip-vertical:not(.is-visible) {
  opacity:0;
  transform:translateY(15%);
  filter:blur(10px)
}

.motion-ready [data-reveal].fx-radial-zoom:not(.is-visible) {
  opacity:0;
  transform:scale(1.16);
  filter:blur(14px)
}

.motion-ready [data-reveal].fx-slide-reveal {
  overflow:hidden
}

.motion-ready [data-reveal].fx-slide-reveal img {
  transition:transform var(--motion-cinematic) var(--ease-out-cinematic)
}

.motion-ready [data-reveal].fx-slide-reveal:not(.is-visible) img {
  transform:translateY(20%) scale(1.2)
}

.motion-ready [data-reveal].fx-crossfade:not(.is-visible) {
  opacity:0
}

.motion-ready [data-reveal].fx-slice>.photo {
  transition:transform var(--motion-cinematic) var(--ease-out-cinematic),opacity var(--motion-medium);
  transition-delay:calc(var(--i,0)*110ms)
}

.motion-ready [data-reveal].fx-slice:not(.is-visible)>.photo {
  opacity:0;
  transform:translateY(80px)
}

/* Light stays dormant except during a single reveal or explicit Lab replay. */
.fx-light-leak {
  position:relative;
  overflow:hidden;
  isolation:isolate
}

.fx-light-leak:before {
  content:'';
  pointer-events:none;
  position:absolute;
  inset:-30%;
  z-index:4;
  opacity:0;
  background:radial-gradient(ellipse at 30% 50%,#ffd294b3 0%,#e78c3d55 22%,transparent 55%);
  mix-blend-mode:screen;
  transform:translateX(-80%) rotate(-20deg)
}

.motion-ready .fx-light-leak.is-visible:before {
  animation:light-leak var(--motion-cinematic) var(--ease-soft) 1 both
}

@keyframes light-leak {
  0% {
    opacity:0;
    transform:translateX(-80%) rotate(-20deg)
  }

  35% {
    opacity:.65
  }

  100% {
    opacity:0;
    transform:translateX(85%) rotate(-20deg)
  }

}

/* SVG path drawing is separate from dotted stroke rendering. */
.motion-ready [data-reveal] .route-draw {
  stroke-dasharray:1;
  stroke-dashoffset:1;
  transition:stroke-dashoffset var(--motion-cinematic) var(--ease-soft) .2s
}

.motion-ready [data-reveal].is-visible .route-draw {
  stroke-dashoffset:0
}

.motion-ready [data-reveal] .route-node {
  opacity:0;
  transition:opacity var(--motion-fast) var(--ease-soft);
  transition-delay:calc(550ms + var(--i,0)*150ms)
}

.motion-ready [data-reveal].is-visible .route-node {
  opacity:1
}

.motion-ready [data-reveal] .route-label {
  opacity:0;
  transition:opacity var(--motion-fast) var(--ease-soft);
  transition-delay:calc(650ms + var(--i,0)*150ms)
}

.motion-ready [data-reveal].is-visible .route-label {
  opacity:1
}

/* Opener has its own once-only title timing; no ambient loops. */
.motion-ready .hero-title>.meta {
  animation:opening-label var(--motion-medium) .1s both
}

.motion-ready .hero-title h1 {
  animation:opening-title var(--motion-cinematic) .25s both
}

.motion-ready .hero-subtitle {
  animation:opening-label var(--motion-medium) .7s both
}

@keyframes opening-label {
  from {
    opacity:0;
    translate:0 12px
  }

  to {
    opacity:1;
    translate:0 0
  }

}

@keyframes opening-title {
  from {
    opacity:0;
    clip-path:inset(0 100% 0 0);
    translate:0 20px
  }

  to {
    opacity:1;
    clip-path:inset(-20% -10%);
    translate:0 0
  }

}

/* Manual switch works alongside the OS preference. */
html.motion-off {
  scroll-behavior:auto
}

.motion-off *, .motion-off *:before,.motion-off *:after {
  animation:none!important;
  transition:none!important
}

.motion-off [data-reveal] {
  opacity:1!important;
  transform:none!important;
  filter:none!important;
  clip-path:none!important
}

.motion-off [data-reveal] img,.motion-off [data-reveal]>.photo {
  transform:none!important;
  opacity:1!important
}

.motion-off .route-draw {
  stroke-dashoffset:0!important
}

.motion-off .route-node,.motion-off .route-label {
  opacity:1!important
}

.motion-off .reef-sequence {
  height:auto
}

.motion-off .fx-light-leak:before {
  display:none
}

.motion-off .site-header.is-hidden {
  transform:none
}

.is-resetting,.is-resetting *,.is-resetting:before {
  transition:none!important;
  animation:none!important
}

html:not(.motion-ready) .reef-curtain {
  display:none
}

html:not(.motion-ready) .reef-sequence {
  height:calc(var(--journal-svh, 1svh) * 100)
}

/* Keep observed wrappers geometrically present. Clipping the wrapper to zero
   can prevent IntersectionObserver from ever seeing it; mask its image instead. */
.motion-ready [data-reveal].fx-mask-reveal,
.motion-ready [data-reveal].fx-letterbox,
.motion-ready [data-reveal].fx-slide-reveal { clip-path: none; }
.motion-ready [data-reveal].fx-slide-reveal:not(.is-visible) { opacity: 0; }
.motion-ready [data-reveal].fx-mask-reveal img,
.motion-ready [data-reveal].fx-letterbox img { transition: clip-path var(--motion-cinematic) var(--ease-out-cinematic), opacity var(--motion-medium) var(--ease-soft), transform var(--motion-cinematic) var(--ease-out-cinematic); }
.motion-ready [data-reveal].fx-mask-reveal:not(.is-visible) img { clip-path: inset(12% 50%); opacity:0; transform:scale(1.08); }
.motion-ready [data-reveal].fx-letterbox:not(.is-visible) img { opacity:0; }
.motion-ready [data-reveal].photo-circle.fx-mask-reveal:not(.is-visible) img { clip-path: circle(0% at 50% 50%); }
.motion-ready [data-reveal].photo-circle.fx-mask-reveal.is-visible img { clip-path: circle(75% at 50% 50%); }
.motion-ready [data-reveal].fx-letterbox:not(.is-visible) img { clip-path: inset(48% 0); }
.motion-ready [data-reveal].fx-mask-reveal.is-visible img,
.motion-ready [data-reveal].fx-letterbox.is-visible img { clip-path: inset(0); }
.motion-off [data-reveal] img { clip-path: none!important; }

/* mJourney-inspired graphic choreography: line, title, then fine information. */
.motion-ready .fx-title .motion-line>span {
  transition:transform 800ms var(--ease-out-cinematic),opacity 500ms;
  transition-delay:calc(var(--i,0)*140ms);
}
.motion-ready .fx-title:not(.is-visible) .motion-line>span { transform:translateY(115%); opacity:0; }
.motion-ready .fx-graphic .graphic-panel { transition:opacity 450ms var(--ease-soft),transform 550ms var(--ease-out-cinematic); transition-delay:var(--delay,250ms); }
.motion-ready .fx-graphic:not(.is-visible) .graphic-panel { opacity:0; transform:translateY(9px); }
.motion-ready .fx-graphic:not(.is-visible) .flight-connector { transform:scaleX(0); }
.flight-connector { transform-origin:left; transition:transform 650ms var(--ease-out-cinematic); }
.motion-ready .fx-defocus>img { transition:filter 1500ms var(--ease-soft),transform 1700ms var(--ease-out-cinematic); }
.motion-ready .fx-defocus:not(.is-visible)>img { filter:blur(10px) brightness(.8); transform:scale(1.04); }
.motion-ready .fx-defocus.fx-light-leak.is-visible:before { animation-duration:1900ms; animation-delay:350ms; }
.motion-ready .fx-defocus .photo-caption { transition:opacity 550ms var(--ease-soft); transition-delay:800ms; }
.motion-ready .fx-defocus:not(.is-visible) .photo-caption { opacity:0; }
/* These two-image cuts finish on the unmodified incoming photograph. */
.motion-ready .transition-shot:not(.is-visible) .shot-from { opacity:1; }
.motion-ready .transition-shot.is-visible .shot-from { animation:shot-out 550ms var(--ease-soft) both; }
@keyframes shot-out { from{opacity:1} to{opacity:0} }
.motion-ready .fx-smear.is-visible .shot-echo:first-of-type { animation:smear-left 750ms var(--ease-out-cinematic) both; }
.motion-ready .fx-smear.is-visible .shot-echo:nth-of-type(2) { animation:smear-right 750ms var(--ease-out-cinematic) both; }
@keyframes smear-left { 0%{opacity:0;transform:translateX(-50%) scaleX(2.8);filter:blur(14px)} 25%{opacity:.85} 100%{opacity:0;transform:translateX(0) scaleX(1);filter:blur(0)} }
@keyframes smear-right { 0%{opacity:0;transform:translateX(50%) scaleX(2.8);filter:blur(14px)} 25%{opacity:.5} 100%{opacity:0;transform:translateX(0) scaleX(1);filter:blur(0)} }
.fx-mirror .shot-echo { width:50%; overflow:hidden; }
.fx-mirror .shot-echo img { width:200%; max-width:none; }
.fx-mirror .shot-echo:first-of-type { right:auto; }
.fx-mirror .shot-echo:nth-of-type(2) { left:50%; }
.fx-mirror .shot-echo:nth-of-type(2) img { left:-100%; transform:scaleX(-1); }
.motion-ready .fx-mirror.is-visible .shot-echo:first-of-type { animation:mirror-left 900ms var(--ease-out-cinematic) both; }
.motion-ready .fx-mirror.is-visible .shot-echo:nth-of-type(2) { animation:mirror-right 900ms var(--ease-out-cinematic) both; }
@keyframes mirror-left { 0%{opacity:1;transform:translateX(0)} 50%{opacity:.9} 100%{opacity:0;transform:translateX(-105%)} }
@keyframes mirror-right { 0%{opacity:1;transform:translateX(0)} 50%{opacity:.9} 100%{opacity:0;transform:translateX(105%)} }
.motion-ready .transition-shot .transition-caption { transition:opacity 400ms; transition-delay:650ms; }
.motion-ready .transition-shot:not(.is-visible) .transition-caption { opacity:0; }
.motion-off .motion-line>span,.motion-off .graphic-panel,.motion-off .flight-connector { opacity:1!important; transform:none!important; }
.motion-off .fx-defocus .photo-caption,.motion-off .transition-caption { opacity:1!important; }
.motion-off .shot-from,.motion-off .shot-echo { opacity:0!important; }

.motion-off .fx-defocus>img {filter:none!important;transform:none!important;}


/* Pop-ups (bird spots, fun facts, sticky notes): bouncy scale-in, reversed on exit. */
.motion-ready [data-reveal].fx-pop { transition:opacity 380ms var(--ease-soft),transform 620ms var(--ease-pop); transition-delay:var(--delay,0ms); }
.motion-ready [data-reveal].fx-pop:not(.is-visible) { opacity:0; transform:scale(.55) rotate(-7deg) translateY(30px); }
.motion-ready [data-reveal].fx-pop.tilt-right:not(.is-visible) { transform:scale(.55) rotate(7deg) translateY(30px); }

/* Ambient loops start once a revealed element has settled (.is-settled from main.js). */
.motion-ready .fx-light-leak.is-settled:before { animation:light-leak-loop 11s var(--ease-soft) infinite; }
@keyframes light-leak-loop { 0%,60% { opacity:0; transform:translateX(-80%) rotate(-20deg); } 75% { opacity:.4; } 100% { opacity:0; transform:translateX(85%) rotate(-20deg); } }
.motion-ready [data-reveal].is-settled .route-node { animation:node-pulse 2.6s ease-in-out infinite; animation-delay:calc(var(--i,0)*300ms); transform-box:fill-box; transform-origin:center; }
@keyframes node-pulse { 0%,100% { transform:scale(1); } 50% { transform:scale(1.45); } }
.motion-ready .fx-graphic.is-settled .locator-cross { animation:locator-blink 2.2s steps(2,end) infinite; }
@keyframes locator-blink { 50% { opacity:.25; } }
.motion-ready .fx-graphic.is-settled .flight-connector svg { animation:plane-bob 2.4s ease-in-out infinite; }
@keyframes plane-bob { 0%,100% { translate:0 0; } 50% { translate:3px -3px; } }
.motion-ready .fx-pop.is-settled { animation:pop-idle 5s ease-in-out infinite; }
.motion-ready .fx-pop.tilt-right.is-settled { animation-name:pop-idle-right; }
@keyframes pop-idle { 0%,100% { transform:rotate(0) translateY(0); } 50% { transform:rotate(-1.2deg) translateY(-5px); } }
@keyframes pop-idle-right { 0%,100% { transform:rotate(0) translateY(0); } 50% { transform:rotate(1.2deg) translateY(-5px); } }
.motion-ready .fx-title.is-settled .motion-line>span strong { animation:title-glow 6s ease-in-out infinite; }
@keyframes title-glow { 0%,100% { text-shadow:0 0 0 transparent; } 50% { text-shadow:0 0 28px rgba(103,210,223,.45); } }
.motion-ready .scroll-cue.is-settled span { animation:cue-bounce 1.8s ease-in-out infinite; }
@keyframes cue-bounce { 0%,100% { translate:0 0; } 50% { translate:0 6px; } }

/* Hero titles replay when the reader scrolls back to the top. */
.motion-ready .hero-title>.meta, .motion-ready .hero-title h1, .motion-ready .hero-subtitle { animation:none; }
.motion-ready .hero-title[data-reveal]>.meta { transition:opacity var(--motion-medium) .1s,translate var(--motion-medium) .1s var(--ease-out-cinematic); }
.motion-ready .hero-title[data-reveal] h1 { transition:opacity var(--motion-cinematic) .25s,clip-path var(--motion-cinematic) .25s var(--ease-out-cinematic),translate var(--motion-cinematic) .25s var(--ease-out-cinematic); clip-path:inset(-20% -10%); }
.motion-ready .hero-title[data-reveal] .hero-subtitle { transition:opacity var(--motion-medium) .7s,translate var(--motion-medium) .7s var(--ease-out-cinematic); }
.motion-ready .hero-title[data-reveal]:not(.is-visible)>.meta, .motion-ready .hero-title[data-reveal]:not(.is-visible) .hero-subtitle { opacity:0; translate:0 12px; }
.motion-ready .hero-title[data-reveal]:not(.is-visible) h1 { opacity:0; clip-path:inset(0 100% 0 0); translate:0 20px; }
.motion-off .hero-title>*, .motion-off .hero-title h1 { opacity:1!important; clip-path:none!important; translate:0 0!important; }
.motion-off .fx-pop, .motion-off .is-settled, .motion-off .is-settled * { animation:none!important; transform:none!important; }
