/* ============================================================
   /world.html — full-viewport immersive map page
   Base styles inherit from styles.css; this file overrides + extends
   ============================================================ */

html, body.world-body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: #0a1f2c;
  cursor: grab;
}
body.world-body.is-dragging { cursor: grabbing; }
body.world-body.is-zoomed-in { cursor: zoom-out; }
body.world-body * { box-sizing: border-box; }

/* ---------- STAGE: viewport-level container ---------- */
.world-stage {
  position: fixed;
  inset: 0;
  background:
    /* deep ocean gradient */
    radial-gradient(ellipse at 50% 30%, #2a6c9c 0%, #1a4a73 40%, #0e2c47 80%, #061a2c 100%);
  overflow: hidden;
}

/* ambient ocean texture: subtle wave lines across the entire viewport */
.world-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 38px,
      rgba(255, 255, 255, 0.025) 38px,
      rgba(255, 255, 255, 0.025) 40px
    );
  pointer-events: none;
  opacity: 0.6;
  animation: stage-wave 24s linear infinite;
}
@keyframes stage-wave {
  0%   { background-position: 0 0; }
  100% { background-position: 0 80px; }
}

/* ---------- CAMERA: pan + zoom transform target ---------- */
.world-camera {
  position: absolute;
  /* Centered in viewport at default scale 1 */
  top: 50%;
  left: 50%;
  width: 1448px;
  height: 1086px;
  margin-top: -543px;
  margin-left: -724px;
  transform-origin: 0 0;
  transform: translate(0, 0) scale(1);
  will-change: transform;
}
.world-camera.is-flying {
  transition: transform 1.2s cubic-bezier(.45, 0.05, .25, 1);
}

.world-map-base {
  width: 100%;
  height: 100%;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  border-radius: 0;
  filter: drop-shadow(0 24px 72px rgba(0, 0, 0, 0.45));
  image-rendering: -webkit-optimize-contrast;
  image-rendering: smooth;
}
.world-ambient {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: hidden;
  border-radius: 0;
}
/* Soft vignette that bleeds the deep-ocean page bg INTO the map edges */
.world-camera::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 6;
  background:
    radial-gradient(
      ellipse 92% 88% at 50% 50%,
      transparent 0%,
      transparent 55%,
      rgba(14, 44, 71, 0.45) 78%,
      rgba(10, 31, 47, 0.85) 92%,
      #061a2c 100%
    );
}

/* ---------- FIRST-VISIT FOG-LIFTING INTRO (driven by intro.js) ----------
   Hill-crest reveal: dense mist covers the whole viewport. Painterly
   cloud silhouettes scatter across the screen. The mist fades, the
   clouds drift upward and fade as they rise. Map blur (under the
   intro) clears as the fog lifts. NOT a curtain pull-aside. */
.world-intro {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: auto;
  overflow: hidden;
}
.intro-haze {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 60%,
      rgba(252, 250, 244, 0.85) 0%,
      rgba(252, 250, 244, 0.95) 40%,
      rgba(248, 244, 232, 1.0) 100%);
  opacity: 1;
  transition: opacity 2.6s ease-out;
}
.world-intro.is-lifting .intro-haze {
  opacity: 0;
}
.intro-cloud {
  position: absolute;
  height: auto;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  opacity: 0.92;
  filter: drop-shadow(0 6px 12px rgba(180, 195, 220, 0.20));
  transform: translateY(0);
  transition: transform var(--dur, 2.5s) cubic-bezier(.4, 0.05, .15, 1) var(--delay, 0s),
              opacity   var(--dur, 2.5s) ease-out                       var(--delay, 0s);
}
.world-intro.is-lifting .intro-cloud {
  /* Drift upward by --rise, fade out as we rise */
  transform: translateY(calc(-1 * var(--rise, 40vh)));
  opacity: 0;
}
.world-intro.is-cleared {
  /* Once cleared, soft fade-out of the (already-empty) overlay */
  opacity: 0;
  transition: opacity 0.5s ease;
  pointer-events: none;
}

/* Slight blur on the map while intro is up; clears smoothly when removed */
html.is-intro-active .world-camera {
  filter: blur(4px) brightness(1.05);
}
.world-camera {
  transition: filter 1.8s ease;
}

/* ---------- DAY/NIGHT CYCLE (driven by day-night.js) ----------
   .world-stars sits above the camera but below HUD; tiny twinkling
   dots scattered across the upper sky band. .world-lantern is a soft
   amber radial glow over the campfire near Mindset Mountains.
   .world-stage::after applies a night-dim overlay across the whole
   viewport. All three opacities are CSS vars set by day-night.js. */
.world-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
  background: radial-gradient(ellipse at 50% 30%,
    rgba(8, 14, 36, 0.0) 0%,
    rgba(8, 14, 36, 0.75) 100%);
  opacity: var(--night-dim, 0);
  transition: opacity 1s ease;
}
.world-stars {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
  opacity: var(--stars-op, 0);
  transition: opacity 2s ease;
}
.world-star {
  position: absolute;
  background: #fff8e1;
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(255, 248, 225, 0.9);
  animation: star-twinkle 3s ease-in-out infinite;
  pointer-events: none;
}
@keyframes star-twinkle {
  0%, 100% { opacity: 0.35; transform: scale(0.9); }
  50%      { opacity: 1.00; transform: scale(1.10); }
}
.world-lantern {
  position: absolute;
  /* Anchor to camera coords. The Mindset campfire is at canvas (10%, 39%);
     we approximate viewport position via an absolute inset assuming fit-zoom.
     The .world-stage is fixed-fill so percentages here track the viewport. */
  left: 10%;
  top: 64%;
  width: 14vmin;
  height: 14vmin;
  pointer-events: none;
  z-index: 5;
  background: radial-gradient(circle,
    rgba(255, 168, 84, 0.55) 0%,
    rgba(255, 168, 84, 0.18) 35%,
    transparent 70%);
  filter: blur(1px);
  opacity: var(--lantern-op, 0);
  transition: opacity 2s ease;
  animation: lantern-flicker 3.4s ease-in-out infinite;
}
@keyframes lantern-flicker {
  0%, 100% { transform: scale(1.0); }
  50%      { transform: scale(1.08); }
}
.world-camera {
  transition: filter 2s ease;
}

/* ---------- GENERATIVE WEATHER (driven by weather.js) ----------
   Soft sunshaft + drifting cloud shadows + rare rain. All pointer-
   events: none so clicks pass through. Layered above the painted PNG
   but below the spotlight + cards. */
.weather-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
}
.weather-sunshaft {
  position: absolute;
  top: -10%;
  left: -10%;
  width: 60%;
  height: 90%;
  background: radial-gradient(ellipse at center,
    rgba(255, 232, 180, 0.18) 0%,
    rgba(255, 232, 180, 0.10) 35%,
    transparent 70%);
  mix-blend-mode: screen;
  animation: sunshaft-drift 90s linear infinite;
}
@keyframes sunshaft-drift {
  0%   { transform: translate(0, 0); }
  50%  { transform: translate(80%, 12%); }
  100% { transform: translate(0, 0); }
}
.weather-cloud-shadow {
  position: absolute;
  width: 32%;
  height: 22%;
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(20, 30, 50, 0.16) 0%,
    rgba(20, 30, 50, 0.08) 50%,
    transparent 80%);
  mix-blend-mode: multiply;
  filter: blur(8px);
}
.weather-cloud-shadow--a {
  top: 28%; left: -20%;
  animation: cloud-shadow-a 140s linear infinite;
}
.weather-cloud-shadow--b {
  top: 50%; left: -25%;
  width: 26%; height: 18%;
  animation: cloud-shadow-b 200s linear infinite;
  animation-delay: -50s;
}
.weather-cloud-shadow--c {
  top: 12%; left: -30%;
  width: 36%; height: 24%;
  animation: cloud-shadow-a 170s linear infinite;
  animation-delay: -90s;
}
@keyframes cloud-shadow-a {
  0%   { transform: translateX(0); }
  100% { transform: translateX(160vw); }
}
@keyframes cloud-shadow-b {
  0%   { transform: translateX(0); }
  100% { transform: translateX(140vw); }
}

.weather-rain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.5s ease;
  z-index: 4;
}
.weather-rain.is-falling { opacity: 0.55; }
.rain-streak {
  position: absolute;
  top: -20px;
  width: 1px;
  background: linear-gradient(180deg,
    rgba(180, 200, 220, 0.0) 0%,
    rgba(200, 220, 240, 0.7) 100%);
  animation: rain-fall linear infinite;
}
@keyframes rain-fall {
  0%   { transform: translateY(0); }
  100% { transform: translateY(110vh); }
}

/* ---------- HIDDEN EASTER EGGS (driven by easter-eggs.js) ----------
   Tiny pulsing markers scattered on the map — deliberately subtle so
   the user has to look. Found state stored in localStorage; the
   .egg-badge bottom-left tracks "N of 5 found" once any are found. */
.eggs-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 8;
}
.egg {
  position: absolute;
  width: 42px;
  height: 42px;
  margin: -21px 0 0 -21px;
  background: transparent;
  border: 0;
  cursor: pointer;
  pointer-events: auto;
  padding: 0;
  outline: none;
}
/* Mobile: the painted map shrinks to fit the viewport so 42px buttons end up
   gigantic relative to the painting. Scale them with the viewport instead. */
@media (max-width: 720px), (hover: none) and (pointer: coarse) {
  .egg {
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
  }
  .egg-icon { opacity: 0.7; }
  /* Touch-target buffer: keep visible icon small but expand the hit
     area via an invisible padding extension so tapping still works. */
  .egg::before {
    content: "";
    position: absolute;
    inset: -14px;
  }
}
.egg-icon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Darkening + saturation now baked into the PNG pixels themselves
     (scripts/darken-eggs.py). CSS keeps just the drop-shadow since
     filter changes weren't taking effect across browser cache. */
  filter: drop-shadow(0 5px 10px rgba(20, 30, 50, 0.45));
  opacity: 1;
  transition: opacity .3s ease, transform .3s cubic-bezier(.5, 0, .15, 1);
  pointer-events: none;
}
.egg-pulse {
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--egg-color, #fff) 70%, transparent) 0%,
    transparent 70%);
  opacity: 0;
  animation: egg-pulse 2.6s ease-out infinite;
  pointer-events: none;
}
@keyframes egg-pulse {
  0%   { transform: scale(0.4); opacity: 0.55; }
  100% { transform: scale(1.8); opacity: 0;    }
}
.egg:hover .egg-icon,
.egg:focus-visible .egg-icon {
  opacity: 1;
  transform: scale(1.12);
}
.egg.is-found .egg-pulse { display: none; }
.egg.is-found .egg-icon  { opacity: 0.55; filter: drop-shadow(0 3px 6px rgba(20, 30, 50, 0.25)) saturate(0.7); }

.egg-card {
  position: fixed;
  bottom: 90px;
  left: 50%;
  transform: translate(-50%, 18px);
  width: clamp(280px, 36vw, 400px);
  background: rgba(253, 247, 234, 0.985);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-radius: 14px;
  padding: 22px 26px 24px;
  box-shadow: 0 22px 56px rgba(0, 0, 0, 0.35);
  border-left: 3px solid var(--card-accent, #C7643F);
  z-index: 30;
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s ease, transform .35s cubic-bezier(.5, 0, .15, 1);
  font-family: var(--font-body);
}
.egg-card.is-active {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
.egg-card-close {
  position: absolute;
  top: 8px;
  right: 10px;
  width: 28px;
  height: 28px;
  background: transparent;
  border: 0;
  font-size: 1.4rem;
  cursor: pointer;
  color: var(--stone, #7a8693);
  line-height: 1;
}
.egg-card-eyebrow {
  font-family: var(--font-hand, "Caveat", cursive);
  font-size: 1.05rem;
  color: var(--card-accent, #C7643F);
  display: inline-block;
  transform: rotate(-1.2deg);
  margin-bottom: 4px;
}
.egg-card-title {
  font-family: var(--font-hand, "Caveat", cursive);
  font-size: clamp(1.5rem, 2.4vw, 1.8rem);
  margin: 0 0 8px;
  color: var(--ink, #1e2830);
  letter-spacing: 0.01em;
}
.egg-card-body {
  font-size: 0.95rem;
  color: var(--charcoal, #424c55);
  line-height: 1.55;
}
.egg-card-body p { margin: 0 0 10px; }
.egg-card-body em { color: var(--ocean-deep, #1e3850); font-style: italic; }
.egg-card-body a {
  color: var(--card-accent, #C7643F);
  text-decoration: none;
  font-weight: 500;
  border-bottom: 1px solid currentColor;
}

.egg-badge {
  position: fixed;
  bottom: 18px;
  left: 18px;
  z-index: 25;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 7px 11px;
  background: rgba(253, 247, 234, 0.96);
  border: 1px solid rgba(30, 40, 48, 0.10);
  border-radius: 999px;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.20);
  font-family: var(--font-hand, "Caveat", cursive);
  font-size: 1.0rem;
  color: var(--ocean-deep, #1e3850);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .4s ease, transform .4s ease;
  pointer-events: none;
}
.egg-badge.is-visible { opacity: 1; transform: translateY(0); }
.egg-badge.is-complete { color: var(--terracotta, #C7643F); }
.egg-badge-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--terracotta, #C7643F);
  box-shadow: 0 0 6px rgba(199, 100, 63, 0.5);
}

/* ---------- FOG-OF-WAR SPOTLIGHT (active when a region is focused) ---------- */
.world-spotlight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 7;          /* above the camera (z auto/0), below HUD/cards */
  opacity: 0;
  transition: opacity 1.2s ease;
  background: radial-gradient(
    circle at var(--spot-x, 50%) var(--spot-y, 50%),
    transparent 0%,
    transparent 22vh,
    rgba(6, 26, 44, 0.42) 48vh,
    rgba(6, 26, 44, 0.78) 90vh
  );
}
body.world-body.is-zoomed-in .world-spotlight {
  opacity: 1;
  transition: opacity 0.6s ease;
}

/* ---------- WORLD-PAGE EXTRA AMBIENT DENSITY ---------- */
/* Third cloud — slower, lower */
.world-ambient .cloud-c {
  width: 16%;
  top: 17%;
  opacity: 0.7;
  animation: cloud-drift 165s linear infinite;
  animation-delay: -110s;
}

/* (palm-tree overlays removed) */

/* ---------- BIRD-FLAP SPRITE ANIMATION (real wing flapping via 3-frame sheet) ----------
   birds-flap.png is 2172×724 = 3 frames of 724×724 (wings up / out / down).
   We display ONE frame at a time by sizing the bg to 300% width and shifting
   background-position. Each element flies its own path AND flaps wings. */
.world-ambient .ambient-birds-flap {
  position: absolute;
  background-image: url('assets/world/birds-flap.png?v=20260527190000');
  background-repeat: no-repeat;
  background-size: 300% 100%;
  background-position: 0% 0%;
  aspect-ratio: 1 / 1;
}
@keyframes bird-flap {
  0%, 32%      { background-position:   0% 0%; }
  33%, 65%     { background-position:  50% 0%; }
  66%, 100%    { background-position: 100% 0%; }
}

.world-ambient .birds-a {
  width: 5%;
  top: 14%;
  left: -8%;
  opacity: 0.95;
  animation:
    birds-fly-right 42s linear infinite,
    bird-flap        0.42s steps(1, end) infinite;
}
.world-ambient .birds-b {
  width: 4%;
  top: 26%;
  left: 108%;
  opacity: 0.85;
  animation:
    birds-fly-left  52s linear infinite,
    bird-flap        0.5s steps(1, end) infinite;
  animation-delay: -22s, 0s;
}
@keyframes birds-fly-right {
  0%   { transform: translate(0, 0); }
  50%  { transform: translate(58vw, -10px); }
  100% { transform: translate(116vw, 4px); }
}
@keyframes birds-fly-left {
  0%   { transform: translate(0, 0)        scaleX(-1); }
  50%  { transform: translate(-58vw, -8px) scaleX(-1); }
  100% { transform: translate(-116vw, 2px) scaleX(-1); }
}

/* ---------- OGOPOGO-DIVE SPRITE (3-frame: surfaced / tilting / submerging) ----------
   ogopogo-dive.png is 2172×724 = 3 frames of 724×724.
   We cycle through the dive sequence ONCE per surfacing cycle, holding on the
   surfaced pose for most of the visible window, then tilting & diving back. */
.world-ambient .ambient-ogopogo-dive {
  position: absolute;
  width: 9%;
  background-image: url('assets/world/ogopogo-dive.png?v=20260527190000');
  background-repeat: no-repeat;
  background-size: 300% 100%;
  background-position: 0% 0%;
  aspect-ratio: 1 / 1;
  opacity: 0;
  animation:
    ogopogo-frame 120s steps(1, end) infinite,
    ogopogo-fade  120s ease-in-out infinite;
}
/* Cycle is 120s. Visible window ~14.4s. With 3 instances staggered by 40s,
   the system guarantees ~25s clear gap between every appearance.
   Frame map: A=0%, B=50%, C=100%
     0-88%      : hidden (~105.6s)
     88-90%     : C (~2.4s) ← fade in
     90-92%     : B (~2.4s)
     92-96.5%   : A HOLD (~5.4s)
     96.5-98.5% : B (~2.4s)
     98.5-99.5% : C (~1.2s) ← fade out
     99.5-100%  : hidden (~0.6s)
*/
@keyframes ogopogo-frame {
  0%, 87.99%      { background-position:   0% 0%; }
  88%, 89.99%     { background-position: 100% 0%; }   /* C — emerging */
  90%, 91.99%     { background-position:  50% 0%; }   /* B */
  92%, 96.49%     { background-position:   0% 0%; }   /* A — HOLD */
  96.5%, 98.49%   { background-position:  50% 0%; }   /* B */
  98.5%, 99.99%   { background-position: 100% 0%; }   /* C — submerging */
  100%            { background-position:   0% 0%; }
}
@keyframes ogopogo-fade {
  0%, 88%   { opacity: 0; }
  89.5%     { opacity: 1; }   /* fade in over ~1.8s */
  98.5%     { opacity: 1; }
  99.5%     { opacity: 0; }   /* fade out over ~1.2s */
  100%      { opacity: 0; }
}

/* Three ogopogo instances at different water locations.
   Animation-delay 0, -40s, -80s spreads them across the 120s cycle,
   guaranteeing ~25s gap between any two appearances. */
.world-ambient .ogo-1 {
  left: 1%; top: 78%;
  /* faces left, delay 0 */
}
.world-ambient .ogo-2 {
  left: 78%; top: 4%;
  transform: scaleX(-1);                     /* faces right */
  animation-delay: -40s, -40s;
}
.world-ambient .ogo-3 {
  left: 50%; top: 92%;
  width: 8%;                                 /* slightly smaller */
  animation-delay: -80s, -80s;
}

/* Two boats — both clearly in open water away from any coastline */
.world-ambient .boat-a {
  position: absolute;
  width: 5.5%;
  left: 92%;
  top: 60%;
  animation: boat-drift-a 64s ease-in-out infinite;
}
.world-ambient .boat-b {
  position: absolute;
  width: 4.2%;
  left: 90%;
  top: 88%;
  animation: boat-drift-b 88s ease-in-out infinite;
  animation-delay: -30s;
}
@keyframes boat-drift-a {
  0%, 100% { transform: translate(0, 0)        rotate(2deg); }
  25%      { transform: translate(-30px, 8px)  rotate(0.5deg); }
  50%      { transform: translate(-90px, 14px) rotate(-2deg); }
  75%      { transform: translate(-30px, 6px)  rotate(0.5deg); }
}
@keyframes boat-drift-b {
  0%, 100% { transform: translate(0, 0)       rotate(-1deg); }
  50%      { transform: translate(50px, -6px) rotate(2deg); }
}

/* Four fish: jumping at varied positions and timings, smoother arcs */
/* Fish scattered across the four sides of the map so they fire continuously
   throughout the cycle, not just when ogopogo surfaces. Different durations
   AND different delays — not one of them syncs with another. */
/* 6 fish — bumped slightly larger so they're more noticeable, spread on 4 sides */
.world-ambient .fish-a {
  position: absolute;
  width: 3.2%;
  left: 8%;
  top: 8%;            /* top-west open water */
  opacity: 0;
  animation: fish-leap-a 11s linear infinite;
}
.world-ambient .fish-b {
  position: absolute;
  width: 3.0%;
  left: 94%;
  top: 62%;           /* east open water */
  opacity: 0;
  animation: fish-leap-b 17s linear infinite;
  animation-delay: -3s;
}
.world-ambient .fish-c {
  position: absolute;
  width: 2.8%;
  left: 4%;
  top: 90%;           /* south-west water */
  opacity: 0;
  animation: fish-leap-a 19s linear infinite;
  animation-delay: -8s;
}
.world-ambient .fish-d {
  position: absolute;
  width: 3.4%;
  left: 70%;
  top: 95%;           /* south open water */
  opacity: 0;
  animation: fish-leap-b 13s linear infinite;
  animation-delay: -5s;
}
.world-ambient .fish-e {
  position: absolute;
  width: 2.8%;
  left: 88%;
  top: 28%;           /* east-north open water */
  opacity: 0;
  animation: fish-leap-a 15s linear infinite;
  animation-delay: -10s;
}
.world-ambient .fish-f {
  position: absolute;
  width: 3.2%;
  left: 38%;
  top: 96%;           /* south-center open water */
  opacity: 0;
  animation: fish-leap-b 21s linear infinite;
  animation-delay: -14s;
}
@keyframes fish-leap-a {
  0%, 90%, 100% { opacity: 0;   transform: translate(0, 18px)    rotate(0deg)  scale(0.9); }
  91%           { opacity: 0.4; transform: translate(2px, 14px)  rotate(-18deg) scale(0.95); }
  92.5%         { opacity: 1;   transform: translate(6px, 4px)   rotate(-22deg) scale(1); }
  94%           { opacity: 1;   transform: translate(12px, -8px) rotate(-10deg) scale(1.02); }
  95.5%         { opacity: 1;   transform: translate(18px, -14px) rotate(0deg) scale(1.04); }
  97%           { opacity: 1;   transform: translate(24px, -8px)  rotate(10deg) scale(1.02); }
  98.2%         { opacity: 0.9; transform: translate(30px, 6px)   rotate(22deg) scale(1); }
  99%           { opacity: 0.4; transform: translate(32px, 14px)  rotate(35deg) scale(0.95); }
  99.5%         { opacity: 0;   transform: translate(33px, 18px)  rotate(45deg) scale(0.9); }
}
@keyframes fish-leap-b {
  0%, 88%, 100% { opacity: 0;   transform: translate(0, 14px)    rotate(0deg)  scaleX(-1); }
  89%           { opacity: 0.4; transform: translate(-2px, 10px)  rotate(18deg) scaleX(-1); }
  90.5%         { opacity: 1;   transform: translate(-6px, 0)     rotate(22deg) scaleX(-1); }
  92%           { opacity: 1;   transform: translate(-12px, -10px) rotate(10deg) scaleX(-1); }
  93.5%         { opacity: 1;   transform: translate(-18px, -14px) rotate(0deg) scaleX(-1); }
  95%           { opacity: 1;   transform: translate(-24px, -8px)  rotate(-10deg) scaleX(-1); }
  96.5%         { opacity: 0.9; transform: translate(-30px, 6px)   rotate(-22deg) scaleX(-1); }
  97.5%         { opacity: 0.4; transform: translate(-32px, 14px)  rotate(-35deg) scaleX(-1); }
  98%           { opacity: 0;   transform: translate(-33px, 18px)  rotate(-45deg) scaleX(-1); }
}

/* One foam stroke on the south shore (under Lifestyle). Removed the rotated
   west-shore one because it was clipping through the Mindset rocks. */
.world-ambient .foam-a {
  position: absolute;
  left: 38%;
  top: 80%;
  width: 14%;
  opacity: 0.55;
  animation: foam-wash 4.2s ease-in-out infinite;
}
.world-ambient .foam-b { display: none; }

/* Two sparkle clusters in different parts of the open ocean */
.world-ambient .sparkle-a {
  position: absolute;
  left: 4%;
  top: 60%;
  width: 12%;
  opacity: 0.7;
  animation: sparkle-twinkle 4.5s ease-in-out infinite;
}
.world-ambient .sparkle-b {
  position: absolute;
  left: 84%;
  top: 22%;
  width: 11%;
  opacity: 0.6;
  animation: sparkle-twinkle 5.5s ease-in-out infinite;
  animation-delay: -2.2s;
}

/* Ogopogo on the world page — slightly larger, in lower-left open water */
.world-ambient .ambient-ogopogo {
  position: absolute;
  width: 10%;
  left: 1%;
  top: 84%;
}
.world-ambient .ambient-smoke {
  position: absolute;
  width: 3%;
  left: 13%;
  top: 16%;
}

/* Region click targets — fully invisible. No box outlines, no halos, no
   hover backgrounds. Clicking anywhere within the rectangle opens that
   region's sidebar; users navigate by cursor pointer + the painted
   island's natural visual cues. */
.world-region {
  position: absolute;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0;
  outline: none;
}
.world-region:focus-visible {
  /* Keyboard a11y: a soft dashed ring is the only visible affordance */
  outline: 2px dashed rgba(253, 247, 234, 0.5);
  outline-offset: 4px;
  border-radius: 18px;
}

/* Set --region-accent on each */
.wr-creditcards { --region-accent: #1F6374; }
.wr-dtc         { --region-accent: #4E7C9B; }
.wr-mindset     { --region-accent: #6A4E93; }
.wr-taxes       { --region-accent: #8B6F47; }
.wr-amazon      { --region-accent: #C7643F; }
.wr-travel      { --region-accent: #5A8E64; }
.wr-lifestyle   { --region-accent: #E38763; }

/* Region positions (percent of canvas) — calibrated to new ChatGPT base map (1448x1086) */
.wr-mindset     { left:  4%; top: 14%; width: 24%; height: 24%; }
.wr-creditcards { left: 30%; top: 10%; width: 22%; height: 22%; }
.wr-dtc         { left: 53%; top: 12%; width: 22%; height: 22%; }
.wr-taxes       { left: 33%; top: 32%; width: 22%; height: 20%; }
.wr-amazon      { left: 58%; top: 37%; width: 28%; height: 22%; }
.wr-travel      { left:  8%; top: 52%; width: 27%; height: 28%; }
.wr-lifestyle   { left: 37%; top: 58%; width: 24%; height: 24%; }

/* ---------- HUD: top-left back button ---------- */
.world-hud-top {
  position: fixed;
  top: 18px;
  left: 18px;
  z-index: 20;
}
.world-back {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 10px 18px 10px 8px;
  background: rgba(253, 247, 234, 0.96);
  border: 1px solid rgba(30, 40, 48, 0.10);
  border-radius: 999px;
  font-family: var(--font-hand);
  font-size: 1.15rem;
  color: var(--ocean-deep);
  text-decoration: none;
  border-bottom: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
  transition: background .2s ease, color .2s ease, transform .2s ease;
}
.world-back:hover {
  background: var(--shell);
  color: var(--terracotta);
  transform: translateY(-1px);
}
.world-back .compass-rose {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  color: var(--ocean-deep);
  animation: compass-spin 36s linear infinite;
}
.world-back:hover .compass-rose { color: var(--terracotta); }

/* ---------- HUD: zoom controls (top-right) ---------- */
.world-hud-controls {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: rgba(253, 247, 234, 0.96);
  border: 1px solid rgba(30, 40, 48, 0.10);
  border-radius: 14px;
  padding: 6px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.world-hud-controls button {
  width: 38px;
  height: 38px;
  background: transparent;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 500;
  color: var(--ocean-deep);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: background .15s ease, color .15s ease;
}
.world-hud-controls button:hover,
.world-hud-controls button:focus-visible {
  background: var(--ocean-foam);
  color: var(--terracotta);
  outline: none;
}
.world-hud-controls button#zoom-reset svg {
  display: block;
}

/* ---------- HUD: ambient ocean volume bar (bottom-right) ---------- */
.audio-bar {
  position: fixed;
  bottom: 18px;
  right: 18px;
  z-index: 25;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px 8px 12px;
  background: rgba(253, 247, 234, 0.96);
  border: 1px solid rgba(30, 40, 48, 0.10);
  border-radius: 999px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
  color: var(--ocean-deep);
}
.audio-bar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.audio-bar-icon .audio-icon-waves {
  transition: opacity .25s ease;
}
.audio-bar-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 110px;
  height: 4px;
  background: rgba(30, 40, 48, 0.18);
  border-radius: 2px;
  outline: none;
  cursor: pointer;
  margin: 0;
}
.audio-bar-slider:focus-visible {
  outline: 2px dashed var(--terracotta);
  outline-offset: 4px;
}
.audio-bar-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--terracotta);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.30);
  cursor: pointer;
}
.audio-bar-slider::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--terracotta);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.30);
  cursor: pointer;
}
.audio-bar-slider::-moz-range-track {
  background: rgba(30, 40, 48, 0.18);
  height: 4px;
  border-radius: 2px;
}

/* ---------- HUD: hint line (bottom) ---------- */
.world-hud-hint {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  font-family: var(--font-hand);
  font-size: 1.2rem;
  color: rgba(253, 247, 234, 0.88);
  letter-spacing: 0.01em;
  pointer-events: none;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  transition: opacity .35s ease;
}
.world-hud-hint.is-faded { opacity: 0; }

/* ---------- REGION PREVIEW CARD ---------- */
.world-region-card {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: clamp(340px, 38vw, 480px);
  background: rgba(253, 247, 234, 0.985);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: 25;
  padding: clamp(28px, 3.6vw, 48px) clamp(24px, 3vw, 42px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  transform: translateX(110%);
  opacity: 0;
  transition: transform .75s cubic-bezier(.5, 0, .15, 1) .15s,
              opacity .55s ease .15s;
  border-left: 1px solid rgba(30, 40, 48, 0.06);
  box-shadow: -22px 0 56px rgba(0, 0, 0, 0.35);
}
.world-region-card.is-active {
  transform: translateX(0);
  opacity: 1;
}
.card-close {
  position: absolute;
  top: 16px;
  right: 18px;
  width: 36px;
  height: 36px;
  background: transparent;
  border: 1px solid rgba(30, 40, 48, 0.12);
  border-radius: 50%;
  cursor: pointer;
  font-size: 1.4rem;
  font-weight: 400;
  color: var(--stone);
  line-height: 1;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-close:hover {
  background: rgba(30, 40, 48, 0.08);
  color: var(--ink);
  border-color: rgba(30, 40, 48, 0.2);
}

/* Card body — built dynamically by world.js */
.card-body {
  padding-top: 12px;
}
.card-eyebrow {
  font-family: var(--font-hand);
  font-size: 1.45rem;
  color: var(--card-accent, var(--terracotta));
  display: inline-block;
  transform: rotate(-1.2deg);
  margin-bottom: 4px;
}
.card-title {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3.2vw, 2.6rem);
  margin: 0 0 16px;
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.08;
}
.card-tease {
  font-size: 1.02rem;
  color: var(--charcoal);
  line-height: 1.55;
  margin: 0 0 28px;
}
.card-section-label {
  display: block;
  font-family: var(--font-hand);
  font-size: 1.1rem;
  color: var(--card-accent, var(--terracotta));
  margin: 0 0 12px;
  letter-spacing: 0.01em;
  transform: rotate(-1deg);
  width: fit-content;
}
.card-postcards {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 32px;
}
.card-postcard {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 13px 16px;
  background: white;
  border-radius: 10px;
  border-bottom: none;
  box-shadow: 0 1px 3px rgba(30, 40, 48, 0.05);
  text-decoration: none;
  color: var(--ink);
  border-left: 3px solid var(--card-accent, var(--terracotta));
  transition: transform .2s ease, box-shadow .2s ease;
}
.card-postcard:hover {
  transform: translateX(2px) translateY(-2px);
  box-shadow: 0 8px 20px rgba(30, 40, 48, 0.10);
}
.card-postcard .pp-label {
  font-family: var(--font-hand);
  font-size: 1rem;
  color: var(--card-accent, var(--terracotta));
  flex-shrink: 0;
  min-width: 56px;
  letter-spacing: 0.02em;
}
.card-postcard strong {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.3;
}

.card-treasure {
  background: var(--shell-deep);
  border: 1px dashed color-mix(in srgb, var(--card-accent, var(--terracotta)) 65%, transparent);
  border-radius: 14px;
  padding: 18px 20px;
  margin: 0 0 28px;
  position: relative;
  transform: rotate(-0.5deg);
}
.card-treasure-label {
  font-family: var(--font-hand);
  font-size: 1rem;
  color: var(--card-accent, var(--terracotta));
  display: block;
  margin-bottom: 4px;
}
.card-treasure h4 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 500;
  color: var(--ink);
  margin: 0 0 8px;
}
.card-treasure p {
  font-size: 0.92rem;
  color: var(--charcoal);
  margin: 0;
  line-height: 1.5;
}

.card-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  background: var(--card-accent, var(--terracotta));
  color: var(--shell);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.98rem;
  font-weight: 600;
  text-decoration: none;
  border-bottom: none;
  align-self: flex-start;
  transition: transform .2s ease, box-shadow .2s ease, filter .2s ease;
}
.card-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 18px rgba(30, 40, 48, 0.18);
  filter: brightness(1.05);
}
.card-cta::after {
  content: "→";
  transition: transform .2s ease;
}
.card-cta:hover::after { transform: translateX(4px); }

/* ---------- Mobile / touch: simpler layout, no pan/zoom ----------
   Triggers on any narrow viewport (≤720px) OR any device that lacks a
   precise pointer (phone in landscape, tablet, etc.). Touch devices in
   landscape often report viewport widths >720px but still want the
   bottom-sheet card UX, not the desktop right-side panel. */
@media (max-width: 720px), (hover: none) and (pointer: coarse) {
  .world-camera {
    width: 100vw;
    height: calc(100vw * 880 / 1168);
    margin: 0;
    top: 50%;
    left: 0;
    transform-origin: 0 0;
    transform: translate(0, -50%) scale(1) !important;
  }
  body.world-body { overflow-y: auto; }
  .world-stage { position: relative; min-height: 100vh; }
  .world-hud-controls { display: none; }
  .world-hud-hint { font-size: 1rem; bottom: 10px; }
  .world-region-card {
    width: 100vw;
    bottom: 0;
    top: auto;
    height: 78vh;
    border-radius: 22px 22px 0 0;
    transform: translateY(110%);
    border-left: 0;
    border-top: 1px solid rgba(30, 40, 48, 0.08);
    box-shadow: 0 -22px 56px rgba(0, 0, 0, 0.35);
  }
  .world-region-card.is-active { transform: translateY(0); }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .compass-rose { animation: none !important; }
  .world-camera { transition-duration: .25s !important; }
  .world-region-card { transition-duration: .25s !important; transition-delay: 0s !important; }
  .world-stage::before { animation: none !important; }
}

/* ============================================================
   Perlin-noise ambient takeover (immersive /world.html)
   ambient.js sets `js-ambient-on` on <html> and drives boats /
   ogopogo / fish via rAF. Disable the legacy keyframes here too.
   ============================================================ */
html.js-ambient-on .world-ambient .boat-a,
html.js-ambient-on .world-ambient .boat-b,
html.js-ambient-on .world-ambient .ambient-ogopogo-dive,
html.js-ambient-on .world-ambient .ambient-fish {
  animation: none !important;
}

/* ============================================================
   Cinemagraph water layer (immersive /world.html)
   Looping video sits ABOVE the painted island PNG, masked to the
   ocean region so only the water animates. The mask is the same
   1448x1086 grayscale extracted from base-island.png — white
   pixels reveal the video, black pixels hide it (showing the
   painted island underneath).
   ============================================================ */
.map-water-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  -webkit-mask-image: url('assets/world/base-island-water-mask.png?v=20260527190000');
  mask-image: url('assets/world/base-island-water-mask.png?v=20260527190000');
  -webkit-mask-mode: luminance;
  mask-mode: luminance;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
.map-water-layer video {
  width: 100%;
  height: 100%;
  object-fit: fill; /* Stretch the 1280x720 squished source back to 4:3 */
  display: block;
}
@media (prefers-reduced-motion: reduce) {
  .map-water-layer { display: none; }
}

/* SFTB_AUDIT_FOCUS_VISIBLE — audit F5.1 (2026-05-27)
 * High-contrast keyboard focus indicators for /world/ interactive elements */
.world-region:focus-visible,
.map-back:focus-visible,
.world-region-card .card-postcard:focus-visible,
.world-region-card .card-cta:focus-visible {
  outline: 3px solid #C7643F !important;
  outline-offset: 3px;
  border-radius: 4px;
}
