/*
 * Tidal Echo Pelle ambient entrances v19.
 * Additive only: rage/rage2 remain owned by the upstream mood.css/mood.js.
 *
 * v19 — power refactor (the "phone gets hot" fix), look preserved:
 *   1. The moonlight entrance overlay is now torn down after the entrance
 *      (ambient-entrance.js gives it a lifetime + fade-out; v18 left it — and
 *      its animated star layer — running forever).
 *   2. Entrance-star twinkle is opacity-only. The v18 filter:brightness/
 *      drop-shadow keyframes were exactly the per-frame whole-layer repaint
 *      the upstream mood.css warns about; the static glow now lives in a
 *      constant text-free box-shadow on the dots themselves.
 *   3. Meteors no longer run metronome CSS loops. mood.js's three meteors and
 *      our two extensions are all idle by default; ambient-entrance.js fires
 *      randomized showers (16-42s apart, 1-3 meteors, random angle/height/
 *      length) by adding .is-shooting for a single one-shot crossing.
 *      Between showers the sky is genuinely still — zero running animations.
 *   4. Rage noise gets a two-phase life: full fury (upstream 0.5s cadence)
 *      for the first ~9s after entry (body.pelle-fury, set by JS), then a
 *      calmer simmer (1.4s cadence) that keeps the texture at ~1/3 the
 *      recomposite rate.
 *   5. Global idle gate: 75s without interaction → body.pelle-asleep pauses
 *      every persistent mood animation (animation-play-state, so waking is
 *      instant and phase-preserving). ambient-entrance.js owns the timer.
 */
.pelle-ambient-enter {
  position: fixed;
  inset: 0;
  z-index: 2147482998;
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}

.pelle-ambient-enter--desire {
  background:
    radial-gradient(ellipse at 0 50%, rgba(95, 5, 37, .82) 0 8%, transparent 54%),
    radial-gradient(ellipse at 100% 50%, rgba(120, 8, 48, .78) 0 8%, transparent 54%),
    radial-gradient(ellipse at 50% 0, rgba(74, 3, 34, .72) 0 8%, transparent 48%),
    radial-gradient(ellipse at 50% 100%, rgba(112, 4, 43, .78) 0 8%, transparent 50%),
    rgba(49, 0, 20, .22);
  mix-blend-mode: multiply;
  animation: pelle-desire-edge-in 3.8s cubic-bezier(.2,.72,.24,1) both;
}
.pelle-ambient-enter--desire::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 52%, rgba(255,110,148,.18), rgba(83,0,31,.42) 62%, rgba(42,0,16,.72));
  mix-blend-mode: screen;
  animation: pelle-desire-breathe-twice 3.8s ease-in-out both;
}

.pelle-ambient-enter--vuoto {
  background: rgba(34, 37, 43, .08);
  backdrop-filter: grayscale(0) saturate(1);
  -webkit-backdrop-filter: grayscale(0) saturate(1);
  animation: pelle-vuoto-drain 2.15s cubic-bezier(.25,.76,.3,1) both;
}
.pelle-ambient-enter--vuoto::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(231,234,237,.08), rgba(85,90,96,.28), rgba(231,234,237,.08));
  transform: translateX(-110%);
  animation: pelle-vuoto-sweep 1.25s ease-out both;
}

.pelle-ambient-enter--moonlight {
  background:
    radial-gradient(circle at 52% 32%, rgba(82,126,212,.34), transparent 30%),
    linear-gradient(180deg, rgba(3,15,47,.82), rgba(7,26,64,.66) 58%, rgba(2,9,30,.76));
  mix-blend-mode: multiply;
  animation: pelle-moon-wash-in 1.15s ease-out both;
}
/* The JS adds this class ~3.6s in; the animation outranks the wash-in's fill
   value, takes the whole overlay (stars included) to 0, then JS removes it. */
.pelle-ambient-enter--fading {
  animation: pelle-ambient-fade-out .9s ease forwards !important;
}
/* Entrance stars: static gold glow baked into the shadow stack; the twinkle is
   opacity-only (compositor-friendly — v18 animated filter here, which forced a
   whole-layer repaint every frame and is the main reason the phone ran hot). */
.pelle-ambient-enter--moonlight .pelle-ambient-stars {
  position: absolute;
  width: 2.8px;
  height: 2.8px;
  left: 52%;
  top: 34%;
  border-radius: 50%;
  background: #fff9cf;
  box-shadow:
    0 0 4px 1px rgba(226,239,255,.5),
    -148px -52px 0 0 rgba(255,249,207,.9), -96px 71px 0 0 rgba(255,255,255,.85),
    -54px -88px 0 0 rgba(183,214,255,.95), -24px 36px 0 0 rgba(255,249,207,.9),
    24px -61px 0 0 rgba(255,255,255,.95), 55px 51px 0 0 rgba(181,213,255,.9),
    88px -17px 0 0 rgba(255,249,207,.92), 126px 73px 0 0 rgba(255,255,255,.86),
    161px -69px 0 0 rgba(181,213,255,.9), 197px 19px 0 0 rgba(255,249,207,.92);
  opacity: .72;
  animation: pelle-moon-stars-twinkle 3.35s ease-in-out infinite;
}
.pelle-ambient-enter--moonlight .pelle-ambient-stars::before,
.pelle-ambient-enter--moonlight .pelle-ambient-stars::after {
  content: "";
  position: absolute;
  width: 1.9px;
  height: 1.9px;
  border-radius: 50%;
  background: rgba(255,255,255,.9);
}
.pelle-ambient-enter--moonlight .pelle-ambient-stars::before {
  left: -118px;
  top: 95px;
  box-shadow:
    34px -131px 0 0 rgba(180,215,255,.8),
    92px -42px 0 0 rgba(255,250,211,.78),
    184px -116px 0 0 rgba(255,255,255,.82),
    251px -35px 0 0 rgba(180,215,255,.76);
  animation: pelle-moon-twinkle-soft 2.9s ease-in-out infinite alternate;
}
.pelle-ambient-enter--moonlight .pelle-ambient-stars::after {
  left: 41px;
  top: -104px;
  box-shadow:
    -181px 39px 0 0 rgba(255,250,211,.72),
    -72px 147px 0 0 rgba(180,215,255,.78),
    73px 68px 0 0 rgba(255,255,255,.86),
    144px 155px 0 0 rgba(255,250,211,.76);
  animation: pelle-moon-twinkle-soft 3.7s ease-in-out .65s infinite alternate-reverse;
}

/*
 * Moonlight steady-state sky — v20 "static beauty" pass.
 * On ProMotion phones ANY running animation holds the display at high refresh
 * and the compositor awake — even a compositor-friendly opacity loop. So the
 * steady sky is now fully static (painted once, then free), and all life in
 * it is event-driven one-shots: meteors + individual star flares.
 */

/* Star field: kill the upstream 3.7s opacity loop; hold a fixed brightness
   between the old min/max. The twinkle now happens via .mood-sparkle events.
   v21: the atlas itself is redrawn — five static depth layers replacing the
   upstream three: faint stardust, small white/ice, medium gold/white, bright
   haloed heroes, and one rare grand hero with a companion (a double star).
   All static gradients — painted once, then free. */
body[data-mood="moonlight"] .mood-star-layer {
  animation: none;
  opacity: calc(1.55 * var(--mood-moon-star, 0.6));
  background-image:
    /* stardust (110px tile): tiny, faint — depth floor */
    radial-gradient(1.1px 1.1px at 22px 14px,  oklch(0.95 0.01 255 / 0.55), transparent 55%),
    radial-gradient(1px 1px at 68px 92px,      oklch(0.93 0.01 255 / 0.5),  transparent 55%),
    radial-gradient(1.2px 1.2px at 96px 40px,  oklch(0.96 0.01 255 / 0.6),  transparent 55%),
    radial-gradient(1px 1px at 44px 66px,      oklch(0.92 0.01 90 / 0.45),  transparent 55%),
    /* small stars (150px tile): white and ice-blue */
    radial-gradient(1.5px 1.5px at 18px 30px,  oklch(0.98 0.02 255), transparent 55%),
    radial-gradient(1.4px 1.4px at 94px 12px,  oklch(0.96 0.02 250 / 0.95), transparent 55%),
    radial-gradient(1.6px 1.6px at 130px 86px, oklch(0.98 0.02 255), transparent 55%),
    radial-gradient(1.4px 1.4px at 54px 122px, oklch(0.95 0.02 88 / 0.9), transparent 55%),
    /* medium stars (230px tile): gold/white mix */
    radial-gradient(2.4px 2.4px at 50px 70px,   oklch(0.99 0.03 90), transparent 50%),
    radial-gradient(2.2px 2.2px at 180px 34px,  oklch(0.97 0.02 255), transparent 50%),
    radial-gradient(2.6px 2.6px at 122px 188px, oklch(0.99 0.04 88), transparent 50%),
    /* haloed heroes (320px tile): bright core + soft gold/blue bloom */
    radial-gradient(3.4px 3.4px at 70px 128px,  oklch(1 0 0), oklch(0.92 0.07 88 / 0.55) 45%, transparent 72%),
    radial-gradient(3.2px 3.2px at 244px 250px, oklch(1 0 0), oklch(0.9 0.06 255 / 0.5) 45%, transparent 72%),
    /* grand hero + companion (480px tile): one big haloed star with a tiny
       neighbor — reads as a double star, the atlas's signature */
    radial-gradient(4.6px 4.6px at 150px 210px, oklch(1 0 0), oklch(0.93 0.08 90 / 0.6) 40%, oklch(0.9 0.07 90 / 0.18) 62%, transparent 78%),
    radial-gradient(1.6px 1.6px at 163px 223px, oklch(0.97 0.02 255 / 0.9), transparent 55%);
  background-repeat: repeat;
  background-size:
    110px 110px, 110px 110px, 110px 110px, 110px 110px,
    150px 150px, 150px 150px, 150px 150px, 150px 150px,
    230px 230px, 230px 230px, 230px 230px,
    320px 320px, 320px 320px,
    480px 480px, 480px 480px;
}

/* Richer night: replaces the upstream two-stop scrim with a layered static
   sky — warm moon halo top-right, violet-indigo deep-space bloom, a faint
   diagonal milky-way band, and a cool horizon glow. Zero animation. */
body[data-mood="moonlight"] .chat-wallpaper::before {
  background:
    radial-gradient(140% 90% at 78% -12%, oklch(0.58 0.09 92 / 0.22), transparent 42%),
    radial-gradient(90% 60% at 16% 6%, oklch(0.3 0.1 285 / 0.34), transparent 60%),
    linear-gradient(115deg, transparent 30%, oklch(0.46 0.06 265 / 0.1) 46%, oklch(0.52 0.05 250 / 0.15) 52%, transparent 68%),
    radial-gradient(120% 55% at 50% 108%, oklch(0.36 0.08 230 / 0.3), transparent 62%),
    linear-gradient(180deg, oklch(0.1 0.05 var(--mood-moon-hue, 255) / 0.55), oklch(0.07 0.05 calc(var(--mood-moon-hue, 255) + 6) / 0.7));
}

/* Star flares: five .mood-sparkle dots injected next to the meteors.
   ambient-entrance.js repositions one every few seconds and runs a single
   one-shot flare (transform+opacity) — the "twinkle", as discrete events. */
body[data-mood="moonlight"] .mood-sparkle {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  pointer-events: none;
  background: oklch(0.99 0.03 92);
  box-shadow:
    0 0 6px 1px oklch(0.95 0.06 90 / 0.8),
    0 0 14px 3px oklch(0.9 0.08 88 / 0.35);
  opacity: 0;
}
body[data-mood="moonlight"] .mood-sparkle--cool {
  background: oklch(0.98 0.02 250);
  box-shadow:
    0 0 6px 1px oklch(0.92 0.04 250 / 0.8),
    0 0 14px 3px oklch(0.85 0.05 252 / 0.35);
}
body[data-mood="moonlight"] .mood-sparkle.is-flaring {
  animation: pelle-star-flare var(--spk-dur, 1.4s) ease-in-out both;
}

/*
 * Meteors — event-driven showers (v19).
 * Default state for ALL meteors (upstream 1-3 + our 4/5): no animation at all.
 * This overrides both mood.css's 30s/43s loops and v18's 9.6s/17.2s loops.
 * ambient-entrance.js fires a shower by setting per-meteor CSS vars
 * (--mtr-angle/--mtr-dur, plus top/width inline) and adding .is-shooting;
 * the one-shot crossing ends, the class comes off, the GPU goes idle.
 */
body[data-mood="moonlight"] .mood-meteor {
  height: 3.2px;
  box-shadow: 0 0 9px oklch(0.94 0.07 88 / 0.72);
  animation: none;
}
body[data-mood="moonlight"] .mood-meteor--2 { height: 2.8px; }
body[data-mood="moonlight"] .mood-meteor--3 { height: 2.6px; }
body[data-mood="moonlight"] .mood-meteor--4 { top: 33%; width: 49%; height: 3px;   --mtr-angle: 31deg; --mtr-peak: .86; }
body[data-mood="moonlight"] .mood-meteor--5 { top: 44%; width: 38%; height: 2.6px; --mtr-angle: 28deg; --mtr-peak: .74; }
body[data-mood="moonlight"] .mood-meteor.is-shooting {
  animation: pelle-moon-shower-once var(--mtr-dur, 3.2s) linear both;
}

/*
 * Rage noise — two-phase life (v19). mood.css declares mood-noise at 0.5s
 * (6 recomposites/s). Entry keeps that fury for ~9s via body.pelle-fury
 * (set by ambient-entrance.js), then the simmer below cuts the cadence to
 * ~1.4 jumps/s — the grain texture stays, the heat mostly goes.
 */
body[data-mood="rage"]  .chat-wallpaper::after,
body[data-mood="rage2"] .chat-wallpaper::after {
  animation-duration: 1.4s;
}
body.pelle-fury[data-mood="rage"]  .chat-wallpaper::after,
body.pelle-fury[data-mood="rage2"] .chat-wallpaper::after {
  animation-duration: 0.5s;
}

/*
 * Global idle gate (v19): body.pelle-asleep is added by ambient-entrance.js
 * after 75s without interaction (and immediately when the page is hidden),
 * removed on the first touch/scroll/keypress. play-state:paused (not none)
 * so waking resumes mid-phase with no restart pop.
 */
body.pelle-asleep .mood-star-layer,
body.pelle-asleep .mood-meteor,
body.pelle-asleep .mood-sparkle,
body.pelle-asleep .pelle-ambient-stars,
body.pelle-asleep .chat-wallpaper::before,
body.pelle-asleep .chat-wallpaper::after,
body.pelle-asleep .msg--assistant .assistant-msg,
body.pelle-asleep .msg--assistant .assistant-msg::before,
body.pelle-asleep .msg--assistant .assistant-msg::after,
body.pelle-asleep[data-mood="desire"]::after,
body.pelle-asleep .pelle-current-reply .fx-whisper,
body.pelle-asleep .pelle-current-reply .fx-red,
body.pelle-asleep .fx-shake,
body.pelle-asleep .fx-glitch::before,
body.pelle-asleep .fx-glitch::after {
  animation-play-state: paused !important;
}

@keyframes pelle-ambient-fade-out {
  from { opacity: .76; }   /* pick up where the wash-in's fill left the overlay */
  to   { opacity: 0; }
}
@keyframes pelle-desire-edge-in {
  0% { opacity: 0; transform: scale(1.16); }
  14% { opacity: .92; transform: scale(1.03); }
  42% { opacity: .58; transform: scale(1); }
  58% { opacity: .94; transform: scale(1.015); }
  82% { opacity: .54; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}
@keyframes pelle-desire-breathe-twice {
  0%, 100% { opacity: 0; }
  18%, 59% { opacity: .92; }
  39%, 80% { opacity: .25; }
}
@keyframes pelle-vuoto-drain {
  0% {
    opacity: 0;
    backdrop-filter: grayscale(0) saturate(1);
    -webkit-backdrop-filter: grayscale(0) saturate(1);
  }
  32% {
    opacity: 1;
    backdrop-filter: grayscale(1) saturate(0);
    -webkit-backdrop-filter: grayscale(1) saturate(0);
  }
  67% {
    opacity: 1;
    backdrop-filter: grayscale(1) saturate(0);
    -webkit-backdrop-filter: grayscale(1) saturate(0);
  }
  100% {
    opacity: 0;
    backdrop-filter: grayscale(1) saturate(0);
    -webkit-backdrop-filter: grayscale(1) saturate(0);
  }
}
@keyframes pelle-vuoto-sweep {
  0% { transform: translateX(-110%); opacity: 0; }
  35% { opacity: .65; }
  100% { transform: translateX(110%); opacity: 0; }
}
@keyframes pelle-moon-wash-in {
  0% { opacity: 0; filter: brightness(1.35); }
  38% { opacity: .92; filter: brightness(1); }
  100% { opacity: .76; filter: brightness(1); }
}
/* Opacity-only twinkle (v19) — the glow is static in the shadow stack above. */
@keyframes pelle-moon-stars-twinkle {
  0%, 100% { opacity: .34; }
  18% { opacity: .72; }
  29% { opacity: .98; }
  43% { opacity: .5; }
  68% { opacity: .9; }
  82% { opacity: .44; }
}
@keyframes pelle-moon-twinkle-soft {
  0%   { opacity: .28; }
  100% { opacity: 1; }
}
/* One-shot star flare: bloom up, settle back. transform+opacity only. */
@keyframes pelle-star-flare {
  0%   { opacity: 0; transform: scale(0.4); }
  35%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.5); }
}
/* One-shot crossing: whole duration = one pass, fade at both ends. */
@keyframes pelle-moon-shower-once {
  0% {
    transform: rotate(var(--mtr-angle, 30deg)) translateX(-60vw);
    opacity: 0;
  }
  6% {
    opacity: calc(var(--mtr-peak, 1) * var(--mood-moon-meteor, 1));
  }
  86% {
    opacity: calc(var(--mtr-peak, 1) * var(--mood-moon-meteor, 1) * 0.55);
  }
  100% {
    transform: rotate(var(--mtr-angle, 30deg)) translateX(95vw);
    opacity: 0;
  }
}

html[data-pelle-disabled] .pelle-ambient-enter {
  display: none !important;
}
@media (prefers-reduced-motion: reduce) {
  .pelle-ambient-enter,
  .pelle-ambient-enter::after,
  .pelle-ambient-stars {
    display: none !important;
    animation: none !important;
  }
  body[data-mood="moonlight"] .mood-meteor {
    animation: none !important;
  }
}
