/* ==========================================================================
   Wandernote — THE WORLD LISTENS  (css/spells.css)
   Round 4. js/spells.js casts sixteen spells from the words you write. The
   particle spells live on #spell-canvas and need no CSS beyond geometry; the
   rest are DOM/CSS effects, and this file is all of their looks.

   Loads after css/realm.css, which documents the z-scale inside #world (§0.A
   there). Every layer here is inset:0, pointer-events:none, and ordered UNDER
   #vessels and #veil-layer — nothing a spell does may ever come between the
   reader and the words.

   THE ENGINE'S SIDE OF THE CONTRACT (js/spells.js writes these, nothing else)
     #spell-fog.is-on      --fog-intensity      0..1
     #spell-aurora.is-on   --aurora-intensity   0..1
     #spell-ripple.is-on   --ripple-intensity   0..1
     #spell-flash.is-flash                      two strokes, 130ms each
     #spell-sky   > .moon[.is-risen]            --moon-phase, --moon-lit, left/top
                    .moon__disc / .moon__shadow (the crescent, drawn in SVG)
     #spell-ground > .spell-flower[.is-grown]   --flower-scale, --flower-sway,
                    left/bottom inline; svg 24x30 with __stem/__petals/__petal/__core
     #world.is-windy                            --wind 0..1
     #world.spells-paused                       tab hidden: everything stops
     #world[data-hour-cast="dawn|dusk|night"]   a cast bends the hour for 3 min
     #world[data-spell-tint="<spell>"]          reduced motion: tint, no particles

   Art values (petal pink, moon bone, aurora green) are scene art, not chrome,
   so they are literal here exactly as they are in the paintings in app.html.
   The realm's own light still comes through --veil-glow.
   ========================================================================== */

/* ==========================================================================
   1. LAYER GEOMETRY — all six overlays plus the particle canvas
   ========================================================================== */
#spell-canvas,
#spell-sky,
#spell-ground,
#spell-fog,
#spell-aurora,
#spell-flash,
#spell-ripple {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
#spell-sky    { z-index: var(--z-w-sky); }
#spell-aurora { z-index: var(--z-w-aurora); }
#spell-canvas { z-index: var(--z-w-canvas); width: 100%; height: 100%; display: block; }
#spell-ground { z-index: var(--z-w-ground); }
#spell-fog    { z-index: var(--z-w-fog); }
#spell-flash  { z-index: var(--z-w-weather); }
#spell-ripple { z-index: var(--z-w-weather); }

/* the three intensity-driven layers are invisible until the engine lights them */
#spell-fog,
#spell-aurora,
#spell-ripple {
  opacity: 0;
  transition: opacity 2s var(--ease-soft);   /* matches the engine's 2s ramp */
}
#spell-fog.is-on    { opacity: var(--fog-intensity, 0); }
#spell-aurora.is-on { opacity: var(--aurora-intensity, 0); }
#spell-ripple.is-on { opacity: var(--ripple-intensity, 0); }

/* ==========================================================================
   2. FOG — three bands of gradient drifting at different speeds
   Drift is a transform on a 200%-wide band, so it never repaints layout.
   ========================================================================== */
#spell-fog::before,
#spell-fog::after,
#spell-fog .fog-band {
  content: "";
  position: absolute;
  left: -50%;
  width: 200%;
  background:
    radial-gradient(50% 100% at 20% 50%, rgba(216, 230, 240, 0.5), transparent 72%),
    radial-gradient(40% 100% at 52% 46%, rgba(226, 236, 244, 0.42), transparent 70%),
    radial-gradient(46% 100% at 82% 54%, rgba(206, 222, 236, 0.46), transparent 74%);
  filter: blur(18px);
  will-change: transform;
}
#spell-fog::before {
  top: 48%;
  height: 30%;
  opacity: 0.9;
  animation: fog-drift 58s linear infinite;
}
#spell-fog::after {
  top: 66%;
  height: 26%;
  opacity: 0.7;
  animation: fog-drift 82s linear infinite reverse;
}
#spell-fog .fog-band {
  top: 34%;
  height: 22%;
  opacity: 0.5;
  animation: fog-drift 104s linear infinite;
}
@keyframes fog-drift {
  from { transform: translate3d(-14%, 0, 0); }
  to   { transform: translate3d(14%, 0, 0); }
}
/* wind pushes the bands along and tips them forward */
#world.is-windy #spell-fog::before { animation-duration: calc(58s / (1 + 1.6 * var(--wind, 0.4))); }
#world.is-windy #spell-fog::after  { animation-duration: calc(82s / (1 + 1.6 * var(--wind, 0.4))); }
#world.is-windy #spell-fog .fog-band { animation-duration: calc(104s / (1 + 1.6 * var(--wind, 0.4))); }

/* ==========================================================================
   3. AURORA — two ribbons, hue-shifting, never a hard edge
   ========================================================================== */
#spell-aurora::before,
#spell-aurora::after {
  content: "";
  position: absolute;
  left: -30%;
  width: 160%;
  height: 46%;
  filter: blur(34px);
  mix-blend-mode: screen;
  will-change: transform;
}
#spell-aurora::before {
  top: -6%;
  background: linear-gradient(180deg,
    transparent,
    rgba(123, 240, 180, 0.44) 42%,
    rgba(79, 199, 216, 0.3) 72%,
    transparent);
  border-radius: 0 0 60% 40% / 0 0 100% 100%;
  animation: aurora-breathe 26s var(--ease-in-out) infinite;
}
#spell-aurora::after {
  top: 10%;
  height: 38%;
  background: linear-gradient(180deg,
    transparent,
    rgba(168, 242, 214, 0.3) 48%,
    transparent);
  border-radius: 0 0 40% 60% / 0 0 100% 100%;
  animation: aurora-breathe 38s var(--ease-in-out) -9s infinite reverse;
}
@keyframes aurora-breathe {
  0%, 100% { transform: translate3d(-4%, 0, 0) skewX(-4deg) scaleY(1); opacity: 0.78; }
  34%      { transform: translate3d(3%, 2%, 0) skewX(5deg) scaleY(1.14); opacity: 1; }
  68%      { transform: translate3d(6%, -1%, 0) skewX(-2deg) scaleY(0.92); opacity: 0.7; }
}

/* ==========================================================================
   4. LIGHTNING — a silent flash. Two strokes, 130ms each, and a brief lift of
   the whole scene so the realm itself is what flickers, not a white rectangle.
   ========================================================================== */
#spell-flash {
  background: radial-gradient(120% 80% at 50% 0%,
    rgba(236, 244, 255, 0.9), rgba(198, 218, 248, 0.34) 46%, transparent 78%);
  opacity: 0;
  transition: opacity 220ms var(--ease-out);
}
#spell-flash.is-flash { opacity: 0.62; transition-duration: 40ms; }
#world:has(#spell-flash.is-flash) #realm-scenes { filter: brightness(1.28) contrast(1.04); }
#realm-scenes { transition: filter 180ms var(--ease-out); }

/* ==========================================================================
   5. WATER — light-ripple rings over the lower third
   ========================================================================== */
#spell-ripple {
  background:
    repeating-radial-gradient(circle at 38% 88%,
      rgba(214, 236, 248, 0.22) 0 2px, transparent 2px 46px),
    repeating-radial-gradient(circle at 72% 96%,
      rgba(200, 228, 246, 0.18) 0 2px, transparent 2px 62px);
  mask-image: linear-gradient(180deg, transparent 42%, #000 82%);
  -webkit-mask-image: linear-gradient(180deg, transparent 42%, #000 82%);
  animation: ripple-out 9s linear infinite;
}
@keyframes ripple-out {
  from { background-size: 100% 100%, 100% 100%; }
  to   { background-size: 116% 116%, 124% 124%; }
}

/* ==========================================================================
   6. FLOWERS — blossoms sprouting along the ground band (DOM, capped at 24)
   The engine plants them with left/bottom percentages and a stagger; the growth
   itself is this transition, from nothing in the soil to an open flower.
   ========================================================================== */
.spell-flower {
  position: absolute;
  width: 24px;
  height: 30px;
  transform-origin: 50% 100%;
  transform: scale(0) rotate(var(--flower-sway, 0deg));
  opacity: 0;
  transition: transform 760ms var(--ease-out), opacity 520ms var(--ease-out);
  will-change: transform;
}
.spell-flower svg { width: 100%; height: 100%; display: block; overflow: visible; }
.spell-flower.is-grown {
  opacity: 1;
  transform: scale(var(--flower-scale, 1)) rotate(var(--flower-sway, 0deg));
}
.spell-flower__petals {
  transform-origin: 12px 13px;
  animation: flower-breathe 6s var(--ease-in-out) infinite;
}
.spell-flower__petal { filter: drop-shadow(0 0 4px rgba(246, 211, 228, 0.6)); }
.spell-flower__core  { filter: drop-shadow(0 0 6px rgba(255, 233, 168, 0.8)); }
.spell-flower__stem  { opacity: 0.9; }
@keyframes flower-breathe {
  0%, 100% { transform: rotate(0deg) scale(1); }
  50%      { transform: rotate(3deg) scale(1.04); }
}
/* wind: the whole flower leans and the petals hurry */
#world.is-windy .spell-flower {
  animation: flower-sway calc(2.6s / (0.6 + var(--wind, 0.4))) var(--ease-in-out) infinite;
}
#world.is-windy .spell-flower__petals {
  animation-duration: calc(6s / (0.6 + var(--wind, 0.4)));
}
@keyframes flower-sway {
  0%, 100% { rotate: calc(var(--flower-sway, 0deg) - 4deg); }
  50%      { rotate: calc(var(--flower-sway, 0deg) + 6deg); }
}

/* ==========================================================================
   7. THE MOON — rises into #spell-sky, phase from the real date
   ========================================================================== */
.moon {
  width: clamp(56px, 7vw, 96px);
  height: clamp(56px, 7vw, 96px);
  opacity: 0;
  transform: translate3d(0, 26px, 0) scale(0.94);
  transition: opacity 2.4s var(--ease-soft), transform 2.4s var(--ease-soft);
  filter: drop-shadow(0 0 26px rgba(243, 240, 221, 0.5))
          drop-shadow(0 0 70px rgba(226, 232, 248, 0.28));
}
.moon svg { width: 100%; height: 100%; display: block; overflow: visible; }
.moon.is-risen {
  opacity: calc(0.5 + 0.5 * var(--moon-lit, 0.7));
  transform: translate3d(0, 0, 0) scale(1);
}
.moon__disc { animation: moon-pulse 12s var(--ease-in-out) infinite; }
@keyframes moon-pulse {
  0%, 100% { opacity: 0.94; }
  50%      { opacity: 1; }
}

/* the shooting star js/realm.js sends across the sky on Enter (spec §6) */
#spell-sky .shooting-star {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: #FFF8E6;
  box-shadow: 0 0 10px 2px rgba(255, 248, 230, 0.8);
  animation: shooting-star 1100ms var(--ease-out) both;
}
#spell-sky .shooting-star::after {
  content: "";
  position: absolute;
  right: 1px;
  top: 0;
  width: 120px;
  height: 1.5px;
  background: linear-gradient(270deg, rgba(255, 248, 230, 0.85), transparent);
  transform-origin: 100% 50%;
}
@keyframes shooting-star {
  0%   { opacity: 0; translate: 0 0; }
  14%  { opacity: 1; }
  100% { opacity: 0; translate: -46vw 26vh; }
}

/* ==========================================================================
   8. HOUR CASTS + REDUCED-MOTION TINTS
   A cast of dawn/dusk/night bends the world's hour for three minutes; a cast
   under reduced motion does nothing BUT tint. Both are washes over the scene,
   both are capped low, and neither one ever touches the veil or the text.
   ========================================================================== */
#world[data-hour-cast] #realm-scenes::after { transition: background 3s linear; }
#world[data-hour-cast="dawn"] #realm-scenes::after {
  background:
    radial-gradient(70% 50% at 22% 78%, color-mix(in srgb, var(--veil-glow) 42%, transparent), transparent 70%),
    linear-gradient(180deg, rgba(58, 44, 86, 0.34), rgba(244, 186, 140, 0.22) 62%, rgba(255, 214, 164, 0.16));
  opacity: 0.74;
}
#world[data-hour-cast="dusk"] #realm-scenes::after {
  background:
    radial-gradient(66% 48% at 78% 74%, color-mix(in srgb, var(--veil-glow) 42%, transparent), transparent 72%),
    linear-gradient(180deg, rgba(28, 24, 58, 0.42), rgba(134, 84, 120, 0.24) 58%, rgba(226, 148, 104, 0.18));
  opacity: 0.78;
}
#world[data-hour-cast="night"] #realm-scenes::after {
  background:
    radial-gradient(60% 44% at 50% 14%, color-mix(in srgb, var(--veil-glow) 34%, transparent), transparent 66%),
    linear-gradient(180deg, rgba(8, 10, 26, 0.5), rgba(12, 16, 38, 0.34) 60%, rgba(6, 8, 20, 0.44));
  opacity: 0.86;
}

/* tint-only casts: one wash per spell family, 0.2 alpha ceiling */
#world[data-spell-tint] #realm-scenes { transition: filter 2s var(--ease-soft); }
#world[data-spell-tint="rain"]      #realm-scenes { filter: saturate(0.86) brightness(0.9) hue-rotate(-6deg); }
#world[data-spell-tint="snow"]      #realm-scenes { filter: saturate(0.7) brightness(1.06); }
#world[data-spell-tint="stars"]     #realm-scenes { filter: brightness(1.04) contrast(1.06); }
#world[data-spell-tint="flowers"]   #realm-scenes { filter: saturate(1.14) hue-rotate(-10deg); }
#world[data-spell-tint="embers"]    #realm-scenes { filter: sepia(0.16) saturate(1.2) brightness(1.04); }
#world[data-spell-tint="wind"]      #realm-scenes { filter: contrast(1.04); }
#world[data-spell-tint="fog"]       #realm-scenes { filter: saturate(0.72) brightness(1.08) blur(1.2px); }
#world[data-spell-tint="moon"]      #realm-scenes { filter: saturate(0.8) brightness(1.08) hue-rotate(6deg); }
#world[data-spell-tint="dawn"]      #realm-scenes { filter: sepia(0.12) saturate(1.12) brightness(1.1); }
#world[data-spell-tint="dusk"]      #realm-scenes { filter: saturate(1.08) brightness(0.94) hue-rotate(-12deg); }
#world[data-spell-tint="night"]     #realm-scenes { filter: saturate(0.84) brightness(0.82); }
#world[data-spell-tint="aurora"]    #realm-scenes { filter: saturate(1.2) hue-rotate(14deg); }
#world[data-spell-tint="fireflies"] #realm-scenes { filter: brightness(1.08) sepia(0.08); }
#world[data-spell-tint="lightning"] #realm-scenes { filter: brightness(1.14) contrast(1.1); }
#world[data-spell-tint="leaves"]    #realm-scenes { filter: sepia(0.18) saturate(1.16); }
#world[data-spell-tint="water"]     #realm-scenes { filter: saturate(1.06) hue-rotate(-8deg) brightness(1.02); }

/* ==========================================================================
   9. PAUSED — the tab went away. Nothing animates behind the user's back.
   ========================================================================== */
#world.spells-paused #spell-fog::before,
#world.spells-paused #spell-fog::after,
#world.spells-paused #spell-fog .fog-band,
#world.spells-paused #spell-aurora::before,
#world.spells-paused #spell-aurora::after,
#world.spells-paused #spell-ripple,
#world.spells-paused .spell-flower,
#world.spells-paused .spell-flower__petals,
#world.spells-paused .moon__disc { animation-play-state: paused; }

/* ==========================================================================
  10. REDUCED MOTION — the spells become weather you can look at, not weather
  that moves. The engine already stops casting particles; this stills the rest.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  #spell-fog::before,
  #spell-fog::after,
  #spell-fog .fog-band,
  #spell-aurora::before,
  #spell-aurora::after,
  #spell-ripple,
  #spell-sky .shooting-star,
  .spell-flower__petals,
  .moon__disc { animation: none; }

  #spell-fog::before  { transform: none; }
  #spell-fog::after   { transform: none; }
  #spell-aurora::before,
  #spell-aurora::after { transform: skewX(-3deg); opacity: 0.8; }

  #spell-sky .shooting-star { display: none; }

  .spell-flower { transition: opacity 240ms linear; transform: scale(var(--flower-scale, 1)); }
  .spell-flower.is-grown { transform: scale(var(--flower-scale, 1)); }

  .moon { transition: opacity 300ms linear; transform: none; }
  .moon.is-risen { transform: none; }

  #spell-flash,
  #spell-flash.is-flash { transition: opacity 300ms linear; }
  #spell-flash.is-flash { opacity: 0.3; }
  #world:has(#spell-flash.is-flash) #realm-scenes { filter: brightness(1.1); }

  #world.is-windy .spell-flower { animation: none; }
}
