/* ============================================
   CLOUD SCENE v2 -- Level 4's living sky
   Used by game/js/cloud-scene.js on top of css/scene-kit.css (stage,
   layers, props, generic reactions, particles). This file holds the cloud
   look: the backdrop tiling, the per-zone light, the slow ambient life
   (drifting banks, glowing orbs, breathing stars), the flight, Nimbus's
   modes (front sprite / crossfaded hover / glide strip) and solidity
   stages, the two tap reactions, the island HUD and the "sky restored"
   finale.
   ============================================ */

.fs-root.fs-cloud { background: #1a1638; }

/* Layers: the far and mid layers never swallow taps themselves; tappable
   props opt back in (scene-kit: .fs-prop.tappable .fs-body) so a star in
   the far layer can be tapped through the mid layer */
.fs-cloud .fs-layer-mid { pointer-events: none; }
.fs-cloud .fs-layer-back img { image-rendering: auto; }
.fs-cloud .cs-platform .fs-body > img { image-rendering: auto; }
.fs-cloud .cs-platform { pointer-events: none; }

/* ---- Per-zone light (fixed overlay, crossfaded on arrival) ---- */
.cs-tint {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.6s ease;
  mix-blend-mode: screen;
  pointer-events: none;
}
.cs-tint.on { opacity: 1; }
.no-fade .cs-tint { transition: none; }
.cs-tint-0 { background: radial-gradient(ellipse at 50% 90%, rgba(255, 220, 160, 0.22) 0%, rgba(180, 160, 255, 0.10) 45%, rgba(0, 0, 0, 0) 75%); }
.cs-tint-1 { background: linear-gradient(to bottom, rgba(255, 170, 190, 0.08) 0%, rgba(255, 200, 150, 0.22) 60%, rgba(255, 210, 140, 0.34) 100%); }
.cs-tint-2 { background: linear-gradient(to bottom, rgba(150, 200, 255, 0.10) 0%, rgba(190, 225, 255, 0.24) 60%, rgba(220, 240, 255, 0.32) 100%); }
.cs-tint-3 { background: linear-gradient(to bottom, rgba(200, 150, 255, 0.10) 0%, rgba(210, 170, 255, 0.24) 60%, rgba(230, 190, 255, 0.30) 100%); }

/* The dawn that comes with the correct code */
.cs-dawn {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(255, 190, 140, 0.35) 0%, rgba(255, 220, 170, 0.28) 45%, rgba(255, 245, 210, 0.22) 100%);
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 3s ease;
  pointer-events: none;
}
.fs-cloud .fs-layer-back img { transition: filter 3s ease; }
.fs-cloud.sky-restored .cs-dawn { opacity: 1; }
.fs-cloud.sky-restored .fs-layer-back img { filter: brightness(1.18) saturate(1.2); }

/* ---- Far layer: distant islands beckon, stay lit when done ---- */
.cs-far .fs-body > img { transition: filter 1.2s ease, opacity 1.2s ease; }
.cs-island .fs-body > img { filter: brightness(0.82) saturate(0.85); }
.cs-island.lit .fs-body > img { filter: brightness(1.1) saturate(1.15) drop-shadow(0 0 10px rgba(255, 232, 136, 0.7)); }
.cs-island.cs-here .fs-body > img { opacity: 0; }
.cs-island.cs-here { pointer-events: none; }
.cs-island.cs-here .fs-body { pointer-events: none; }

/* "Tap to continue": the next island (and its HUD icon) glows */
.cs-beckon .fs-body > img { animation: cs-beckon 1.5s ease-in-out infinite; }
@keyframes cs-beckon {
  0%, 100% { filter: brightness(1) drop-shadow(0 0 6px rgba(255, 232, 136, 0.55)); transform: scale(1); }
  50%      { filter: brightness(1.28) drop-shadow(0 0 18px rgba(255, 232, 136, 1)); transform: scale(1.07); }
}
.cs-beckon.top-anchored .fs-body > img { transform-origin: 50% 0; }

/* ---- Ambient life (slow, no tap) ---- */
/* Far cloud banks and the snow cloud drift a little way and back (~35-55 s) */
.fs-cloud .fs-body.idle-drift { animation: cs-drift calc(var(--dur, 5s) * 8) ease-in-out infinite; animation-delay: calc(var(--delay, 0s) * 6); }
@keyframes cs-drift {
  0%, 100% { transform: translate(-14px, 0); }
  50%      { transform: translate(14px, -3px); }
}
/* The memory orbs glow and breathe, still */
.fs-cloud .fs-body.idle-glow > img { animation: cs-orb-glow calc(var(--dur, 5s) * 1.2) ease-in-out infinite; animation-delay: var(--delay, 0s); }
@keyframes cs-orb-glow {
  0%, 100% { filter: brightness(1) drop-shadow(0 0 6px rgba(255, 232, 136, 0.45)); }
  50%      { filter: brightness(1.22) drop-shadow(0 0 14px rgba(255, 232, 136, 0.85)); }
}

/* ---- Stars ---- */
.cs-star .fs-body > img { animation: cs-star-breathe var(--dur, 4s) ease-in-out infinite; }
@keyframes cs-star-breathe { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
.fs-body.cs-twinkle { animation: cs-twinkle 0.9s ease-out; }
@keyframes cs-twinkle {
  0%   { transform: scale(1) rotate(0deg); filter: brightness(1); }
  30%  { transform: scale(1.9) rotate(20deg); filter: brightness(2) drop-shadow(0 0 12px #fff8d0); }
  100% { transform: scale(1) rotate(0deg); filter: brightness(1); }
}
.fs-firefly.cs-twinkle-star {
  width: 4px;
  height: 4px;
  background: radial-gradient(circle, #ffffff 0%, #fff4d0 50%, rgba(255, 240, 200, 0) 100%);
  box-shadow: 0 0 6px 2px rgba(255, 250, 220, 0.7);
  animation: fs-firefly-blink 3.2s ease-in-out infinite;
}
.fs-mote.cs-mote {
  width: 3px;
  height: 3px;
  background: #ffe888;
  box-shadow: 0 0 5px rgba(255, 232, 136, 0.9);
  animation-name: cs-mote-rise;
}
@keyframes cs-mote-rise {
  0%   { opacity: 0; transform: translate(0, 0); }
  15%  { opacity: 0.9; }
  85%  { opacity: 0.5; }
  100% { opacity: 0; transform: translate(var(--dx, 20px), calc(var(--dy, 80px) * -1)); }
}
.fs-firefly.cs-sparkle {
  width: 6px;
  height: 6px;
  background: radial-gradient(circle, #ffffff 0%, #fff4c8 40%, rgba(255, 232, 136, 0.4) 65%, transparent 100%);
  box-shadow: 0 0 8px 2px rgba(255, 240, 180, 0.8);
}

/* ---- Tap reactions: a done island sparkles (cs-glow-pulse), Nimbus shimmers ---- */
.fs-body.cs-glow-pulse { animation: cs-glow-pulse 0.9s ease-out; }
@keyframes cs-glow-pulse {
  0%   { filter: brightness(1); transform: scale(1); }
  30%  { filter: brightness(1.5) drop-shadow(0 0 14px rgba(255, 255, 255, 0.9)); transform: scale(1.05); }
  100% { filter: brightness(1); transform: scale(1); }
}
.fs-prop.lit .fs-body > img { filter: brightness(1.35) drop-shadow(0 0 14px rgba(255, 220, 120, 0.9)); transition: filter 0.6s ease; }
.fs-body.cs-rise { animation: cs-rise 3s ease-out forwards; }
@keyframes cs-rise {
  0%   { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(-260px); opacity: 0; }
}

/* ---- Nimbus ---- */
.cs-nimbus { transition: opacity 1.2s ease; }
.cs-nimbus .fs-body { position: relative; transform: translateY(var(--fly-y, 0px)); }
.cs-nimbus-body { transform-origin: 50% 100%; }
.cs-nimbus-glow {
  position: absolute;
  left: 50%;
  bottom: 10%;
  width: 150%;
  height: 90%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255, 240, 200, 0.28) 0%, rgba(200, 180, 255, 0.14) 45%, rgba(0, 0, 0, 0) 70%);
  mix-blend-mode: screen;
  pointer-events: none;
  animation: cs-glow-breathe 4.2s ease-in-out infinite;
}
@keyframes cs-glow-breathe { 0%, 100% { opacity: 0.7; transform: translateX(-50%) scale(1); } 50% { opacity: 1; transform: translateX(-50%) scale(1.06); } }
.cs-nimbus .cs-nimbus-sway { position: relative; transform-origin: 50% 100%; }
.cs-nimbus img.cs-nimbus-front {
  display: block;
  height: var(--h);
  filter: drop-shadow(0 0 10px rgba(200, 190, 255, 0.45));
  transform: scaleX(var(--face, 1));
  animation: cs-float 4.2s ease-in-out infinite;
  transform-origin: 50% 100%;
}
.cs-nimbus.talking img.cs-nimbus-front { animation-duration: 1.6s; }
/* The idle hover ('drift'): the approved sprite on its float bob plus a very
   slow sway, a fuller breathing glow and four starlight motes drifting up
   around her -- continuous, nothing steps */
.cs-nimbus.mode-drift .cs-nimbus-sway { animation: cs-hover-sway 6.4s ease-in-out infinite; }
.cs-nimbus.mode-drift .cs-nimbus-glow { animation-duration: 5.6s; }
.cs-nimbus-mote {
  position: absolute;
  left: 50%;
  bottom: 30%;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: radial-gradient(circle, #ffffff 0%, #fff4c8 45%, rgba(255, 232, 136, 0) 100%);
  box-shadow: 0 0 6px 2px rgba(255, 240, 180, 0.7);
  opacity: 0;
  pointer-events: none;
  animation: cs-nimbus-mote var(--mdur, 5s) ease-in-out infinite;
  animation-delay: var(--mdelay, 0s);
  animation-play-state: paused;
}
.cs-nimbus.mode-drift .cs-nimbus-mote { animation-play-state: running; }
.cs-nimbus-mote.m1 { --mdur: 5.2s; --mdelay: 0s;    --mx0: -42px; --mx1: -30px; }
.cs-nimbus-mote.m2 { --mdur: 6.1s; --mdelay: -2.1s; --mx0: 38px;  --mx1: 52px; }
.cs-nimbus-mote.m3 { --mdur: 5.7s; --mdelay: -3.4s; --mx0: -20px; --mx1: -46px; }
.cs-nimbus-mote.m4 { --mdur: 6.6s; --mdelay: -1.2s; --mx0: 48px;  --mx1: 30px; }
@keyframes cs-nimbus-mote {
  0%   { opacity: 0;   transform: translate(var(--mx0), 0) scale(0.6); }
  20%  { opacity: 0.9; }
  80%  { opacity: 0.5; }
  100% { opacity: 0;   transform: translate(var(--mx1), -120px) scale(1); }
}
@keyframes cs-float {
  0%, 100% { transform: scaleX(var(--face, 1)) translateY(0); }
  50%      { transform: scaleX(var(--face, 1)) translateY(-7px); }
}
/* Solidity: 0 = a weak signal (flicker), 1 -> 2 -> 3 as her memories return */
.cs-nimbus.solid-0 .cs-nimbus-body { opacity: 0.45; animation: cs-flicker 2.8s ease-in-out infinite; }
.cs-nimbus.solid-1 .cs-nimbus-body { opacity: 0.65; animation: cs-flicker-soft 4s ease-in-out infinite; }
.cs-nimbus.solid-2 .cs-nimbus-body { opacity: 0.85; }
.cs-nimbus.solid-3 .cs-nimbus-body { opacity: 1; }
.cs-nimbus .cs-nimbus-body { transition: opacity 1.2s ease; }
@keyframes cs-flicker {
  0%, 100% { opacity: 0.45; filter: brightness(1); }
  8%  { opacity: 0.25; filter: brightness(1.6); }
  11% { opacity: 0.5; }
  38% { opacity: 0.4; }
  41% { opacity: 0.22; filter: brightness(1.8); }
  44% { opacity: 0.48; filter: brightness(1); }
  70% { opacity: 0.42; }
  73% { opacity: 0.3; }
}
@keyframes cs-flicker-soft {
  0%, 100% { opacity: 0.65; }
  20%  { opacity: 0.55; }
  23%  { opacity: 0.72; filter: brightness(1.3); }
  26%  { opacity: 0.62; filter: brightness(1); }
  60%  { opacity: 0.6; }
}
.fs-body.cs-brighten { animation: cs-brighten 1.4s ease-out; }
@keyframes cs-brighten {
  0%   { filter: brightness(1); }
  25%  { filter: brightness(2.2) drop-shadow(0 0 30px rgba(255, 245, 200, 1)); }
  100% { filter: brightness(1); }
}
.cs-nimbus.materializing .cs-nimbus-body { animation: cs-materialize 1.4s ease-out; }
@keyframes cs-materialize {
  0%   { opacity: 0; filter: brightness(2.4) blur(4px); transform: translateY(20px) scale(0.9); }
  25%  { opacity: 0.6; filter: brightness(1.8) blur(1px); }
  35%  { opacity: 0.15; }
  45%  { opacity: 0.7; }
  60%  { opacity: 0.3; filter: brightness(1.6); }
  100% { opacity: 1; filter: brightness(1) blur(0); transform: translateY(0) scale(1); }
}
/* Eva touches her: a soft shimmer, no spin */
.fs-body.cs-nimbus-shimmer { animation: cs-nimbus-shimmer 1.4s ease-in-out; }
@keyframes cs-nimbus-shimmer {
  0%, 100% { filter: brightness(1); }
  35%      { filter: brightness(1.45) drop-shadow(0 0 22px rgba(255, 245, 200, 0.9)); }
  70%      { filter: brightness(1.15) drop-shadow(0 0 10px rgba(255, 245, 200, 0.5)); }
}
/* Flight on the front sprite (before the flight art is approved): a lean into the wind */
.cs-nimbus.mode-lean img.cs-nimbus-front {
  animation: none;
  transform: scaleX(var(--face, 1)) rotate(calc(var(--face, 1) * 14deg)) translateX(calc(var(--face, 1) * 6px));
  transition: transform 0.5s ease;
  filter: drop-shadow(0 0 14px rgba(255, 240, 200, 0.7));
}
/* The front sprite fades out under the hover cells and back (0.6 s) so the
   swap at the start and end of every line is a dissolve, not a snap */
.cs-nimbus img.cs-nimbus-front { transition: opacity 0.6s ease; }
.cs-nimbus.mode-hover img.cs-nimbus-front { opacity: 0; }

/* The hover: the three approved front cells stacked over the front sprite and
   crossfaded on a slow cycle (1 -> 2 -> 3 -> 2 -> 1, 2.4 s per step) while the
   whole figure rides the float bob and a very slow sway. Hold-and-fade: the
   outgoing cell stays solid underneath while the incoming one fades in on top
   and only then drops away, so her body never goes see-through -- only the
   hair dissolves from one drift to the next. Nothing steps. */
.cs-nimbus .cs-nimbus-hover {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: var(--hover-w, 152px);
  height: var(--h);
  transform: translateX(-50%) scaleX(var(--face, 1));
  transform-origin: 50% 100%;
  opacity: 0;
  transition: opacity 0.6s ease;
  pointer-events: none;
  animation: cs-hover-float 4.2s ease-in-out infinite;
}
.cs-nimbus.mode-hover .cs-nimbus-hover { opacity: 1; }
@keyframes cs-hover-float {
  0%, 100% { transform: translateX(-50%) scaleX(var(--face, 1)) translateY(0); }
  50%      { transform: translateX(-50%) scaleX(var(--face, 1)) translateY(-7px); }
}
.cs-nimbus .cs-hover-sway {
  position: absolute;
  inset: 0;
  transform-origin: 50% 100%;
  animation: cs-hover-sway 6.4s ease-in-out infinite;
}
@keyframes cs-hover-sway {
  0%, 100% { transform: rotate(-1.1deg); }
  50%      { transform: rotate(1.1deg); }
}
.cs-nimbus .cs-hover-cell {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: 300% 100%;
  image-rendering: pixelated;
  filter: drop-shadow(0 0 10px rgba(200, 190, 255, 0.45));
  opacity: 0;
  animation: 9.6s ease-in-out infinite;
}
.cs-nimbus .cs-hover-cell.c1 { animation-name: cs-hover-c1; opacity: 1; z-index: 1; }
.cs-nimbus .cs-hover-cell.c2 { animation-name: cs-hover-c2; z-index: 3; }
.cs-nimbus .cs-hover-cell.c3 { animation-name: cs-hover-c3; z-index: 1; }
/* four 2.4 s segments: 1 under, 2 in | 2 under, 3 in | 3 under, 2 in | 2 under, 1 in */
@keyframes cs-hover-c1 {
  0%      { opacity: 1; z-index: 1; }
  24.99%  { opacity: 1; z-index: 1; }
  25%     { opacity: 0; z-index: 1; }
  75%     { opacity: 0; z-index: 3; }
  100%    { opacity: 1; z-index: 3; }
}
@keyframes cs-hover-c2 {
  0%      { opacity: 0; z-index: 3; }
  25%     { opacity: 1; z-index: 3; }
  25.01%  { opacity: 1; z-index: 1; }
  49.99%  { opacity: 1; z-index: 1; }
  50%     { opacity: 0; z-index: 3; }
  75%     { opacity: 1; z-index: 3; }
  75.01%  { opacity: 1; z-index: 1; }
  100%    { opacity: 1; z-index: 1; }
}
@keyframes cs-hover-c3 {
  0%      { opacity: 0; z-index: 3; }
  25%     { opacity: 0; z-index: 3; }
  50%     { opacity: 1; z-index: 3; }
  50.01%  { opacity: 1; z-index: 1; }
  74.99%  { opacity: 1; z-index: 1; }
  75%     { opacity: 0; z-index: 1; }
  100%    { opacity: 0; z-index: 1; }
}

/* Frame-stepped glide strip (approved cells, feet -- well, cloud legs -- on
   the baseline). The strip art faces right; --face flips it. */
.cs-nimbus .cs-nimbus-strip {
  display: none;
  height: var(--h);
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
  transform-origin: 50% 100%;
  transform: scaleX(var(--face, 1));
  filter: drop-shadow(0 0 10px rgba(200, 190, 255, 0.45));
  pointer-events: none;
}
.cs-nimbus.mode-glide .cs-nimbus-strip { display: block; width: var(--glide-w, 260px); background-size: 400% 100%; }
.cs-nimbus.mode-glide img.cs-nimbus-front { display: none; }
.cs-nimbus.mode-glide .cs-nimbus-hover { display: none; }

/* The fragment on its way to her */
.cs-fragment img { filter: drop-shadow(0 0 14px rgba(255, 232, 136, 0.95)); }

/* ---- Flight: wind streaks in the foreground, wisps ---- */
.cs-streak {
  position: absolute;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0));
  pointer-events: none;
  z-index: 30;
}
.cs-wisp img { filter: blur(0.5px); }
.cs-fore .fs-prop img { filter: brightness(0.94) saturate(0.95); }

/* ---- Island HUD (outside the scaled stage) ---- */
/* Top-right, clear of the spell seals at the top centre */
.cs-island-hud {
  position: absolute;
  top: 2%;
  right: 1.5%;
  display: flex;
  gap: clamp(0.4rem, 1.2vw, 0.8rem);
  z-index: 15;
}
.cs-hud-island {
  width: clamp(34px, 6vw, 52px);
  height: clamp(34px, 6vw, 52px);
  border-radius: 50%;
  background: rgba(12, 16, 30, 0.55);
  border: 2px solid rgba(218, 186, 106, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.6s ease, box-shadow 0.6s ease, transform 0.3s ease;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cs-hud-island img {
  width: 76%;
  height: 76%;
  image-rendering: pixelated;
  filter: grayscale(1) brightness(0.45);
  opacity: 0.6;
  transition: filter 0.8s ease, opacity 0.8s ease;
}
.cs-hud-island.done { border-color: var(--gold, #daba6a); box-shadow: 0 0 10px rgba(255, 232, 136, 0.45); }
.cs-hud-island.done img { filter: none; opacity: 1; }
.cs-hud-island.beckon { animation: cs-hud-beckon 1.5s ease-in-out infinite; }
.cs-hud-island.beckon img { filter: grayscale(0.2) brightness(1); opacity: 1; }
@keyframes cs-hud-beckon {
  0%, 100% { box-shadow: 0 0 4px rgba(255, 232, 136, 0.4); transform: scale(1); border-color: rgba(255, 232, 136, 0.5); }
  50%      { box-shadow: 0 0 16px rgba(255, 232, 136, 0.95); transform: scale(1.08); border-color: #ffe888; }
}

@media (prefers-reduced-motion: reduce) {
  .cs-nimbus-glow, .cs-nimbus img.cs-nimbus-front, .cs-star .fs-body > img, .cs-beckon .fs-body > img,
  .cs-hud-island.beckon, .cs-nimbus.solid-0 .cs-nimbus-body, .cs-nimbus.solid-1 .cs-nimbus-body,
  .cs-nimbus .cs-nimbus-hover, .cs-nimbus .cs-hover-sway, .cs-nimbus .cs-hover-cell, .cs-nimbus .cs-nimbus-sway, .cs-nimbus-mote,
  .fs-cloud .fs-body.idle-drift, .fs-cloud .fs-body.idle-glow > img { animation: none !important; }
  .cs-beckon .fs-body > img { filter: brightness(1.2) drop-shadow(0 0 14px rgba(255, 232, 136, 0.9)); }
  .cs-tint, .cs-dawn, .cs-far .fs-body > img { transition: none; }
}
