/* ============================================================
 * SHADOW SPARKS — Umbral Animation Layer
 * ============================================================
 * A separate animation style for Shadow Sparks. Applied ON TOP of
 * the Universal Spark Animation System (primitives.css / engine.js),
 * never replacing it.
 *
 * Design law: Shadow Sparks don't just move — they MANIFEST.
 * Regular Sparks breathe, glow, and warm. Shadow Sparks emerge
 * from darkness, flicker like cast shadows, dissolve at the edges,
 * and leave afterimages. The figure feels like it stands BEHIND
 * a veil of darkness, not in front of it.
 *
 * Usage: add class `shd` to the spark portrait wrapper, plus one
 * shadow-archetype class (e.g. `shd-flicker-strike`). For the full
 * vertical shadow-play (Ray: "Above and below as well"), include the
 * spatial layers inside the wrapper:
 *
 *   <div class="shd shd-archetype-dread-presence">
 *     <div class="shd-below"></div>  <!-- ground: pooling, tendrils -->
 *     <img src="...">
 *     <div class="shd-above"></div>  <!-- sky: wisps, crown, descent -->
 *   </div>
 *
 * DNA parameters:
 *   --shd-amp : intensity multiplier (0.0 - 1.0, default 0.6)
 *   --shd-dur : duration multiplier (default 1.0)
 *   --shd-tint: umbral tint color (default #1a1a2e)
 *
 * Accessibility: all continuous motion is disabled under
 * prefers-reduced-motion (see bottom of file).
 * ============================================================ */

.shd {
  --shd-amp: 0.6;
  --shd-dur: 1.0;
  --shd-tint: #1a1a2e;
  position: relative;
  isolation: isolate;
}

/* The umbral veil: a living darkness that sits over the figure.
 * All shadow primitives play through this layer. */
.shd::after {
  content: "";
  position: absolute;
  inset: -8%;
  pointer-events: none;
  z-index: 2;
  background:
    radial-gradient(ellipse at 50% 60%,
      transparent 42%,
      rgba(10, 10, 22, calc(0.55 * var(--shd-amp))) 78%,
      rgba(5, 5, 12, calc(0.85 * var(--shd-amp))) 100%);
  opacity: 0.9;
  animation: shd-veil-breathe calc(7s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-veil-breathe {
  0%, 100% { opacity: 0.75; transform: scale(1); }
  50%      { opacity: 0.95; transform: scale(1.04); }
}

/* ------------------------------------------------------------
 * 1. shd-umbral-fade — emergence from / dissolution into darkness.
 * The signature shadow idle: the figure is never fully "there".
 * ------------------------------------------------------------ */
.shd-umbral-fade,
.shd .shd-umbral-fade {
  animation: shd-umbral-fade calc(9s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-umbral-fade {
  0%, 100% {
    opacity: 0.82;
    filter: brightness(0.72) saturate(0.7);
  }
  35% {
    opacity: 1;
    filter: brightness(0.95) saturate(0.9);
  }
  65% {
    opacity: 0.88;
    filter: brightness(0.78) saturate(0.75);
  }
}

/* ------------------------------------------------------------
 * 2. shd-flicker — brief instability, like a shadow cast by
 * flickering candlelight. Used for assassin/trickster archetypes.
 * ------------------------------------------------------------ */
.shd-flicker {
  animation: shd-flicker calc(5.2s * var(--shd-dur)) steps(1, end) infinite;
}

@keyframes shd-flicker {
  0%, 91%, 96%, 100% {
    opacity: 1;
    filter: brightness(0.9);
  }
  92% {
    opacity: 0.55;
    filter: brightness(0.5) blur(1px);
  }
  94% {
    opacity: 0.85;
    filter: brightness(0.7);
  }
}

/* ------------------------------------------------------------
 * 3. shd-edge-dissolve — edges soften and bleed into darkness.
 * Moth/grief archetypes: the figure is always slightly unwinding.
 * ------------------------------------------------------------ */
.shd-edge-dissolve {
  animation: shd-edge-dissolve calc(11s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-edge-dissolve {
  0%, 100% {
    filter: brightness(0.85) blur(0px);
    -webkit-mask-image: radial-gradient(ellipse at center, black 62%, transparent 98%);
    mask-image: radial-gradient(ellipse at center, black 62%, transparent 98%);
  }
  50% {
    filter: brightness(0.68) blur(1.5px);
    -webkit-mask-image: radial-gradient(ellipse at center, black 48%, transparent 92%);
    mask-image: radial-gradient(ellipse at center, black 48%, transparent 92%);
  }
}

/* ------------------------------------------------------------
 * 4. shd-afterimage — movement leaves a shadow echo.
 * Applied during motion states; the trail is the shadow lagging.
 * ------------------------------------------------------------ */
.shd-afterimage {
  animation: shd-afterimage calc(2.4s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-afterimage {
  0%, 100% {
    transform: translateX(0);
    filter: brightness(0.85) drop-shadow(0 0 0 transparent);
  }
  25% {
    transform: translateX(calc(-6px * var(--shd-amp)));
    filter: brightness(0.7) drop-shadow(calc(10px * var(--shd-amp)) 0 6px rgba(0, 0, 0, 0.7));
  }
  75% {
    transform: translateX(calc(6px * var(--shd-amp)));
    filter: brightness(0.7) drop-shadow(calc(-10px * var(--shd-amp)) 0 6px rgba(0, 0, 0, 0.7));
  }
}

/* ------------------------------------------------------------
 * 5. shd-silhouette-deepen — the figure darkens toward pure
 * silhouette. Dread-presence archetype: power reads as darkness.
 * ------------------------------------------------------------ */
.shd-silhouette-deepen {
  animation: shd-silhouette-deepen calc(8s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-silhouette-deepen {
  0%, 100% {
    filter: brightness(0.85) contrast(1.05) saturate(0.8);
  }
  50% {
    filter: brightness(0.38) contrast(1.35) saturate(0.25);
  }
}

/* ------------------------------------------------------------
 * 6. shd-veil-pass — a band of deeper darkness sweeps across
 * the figure, like a cloud crossing the moon. Trickster/veil-dance.
 * ------------------------------------------------------------ */
.shd-veil-pass::before {
  content: "";
  position: absolute;
  inset: -10%;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 30%,
    rgba(0, 0, 0, calc(0.55 * var(--shd-amp))) 50%,
    transparent 70%);
  background-size: 280% 100%;
  animation: shd-veil-pass calc(6.5s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-veil-pass {
  0%   { background-position: 120% 0; opacity: 0; }
  15%  { opacity: 1; }
  55%  { opacity: 1; }
  70%, 100% { background-position: -20% 0; opacity: 0; }
}

/* ------------------------------------------------------------
 * 7. shd-umbral-breath — breathing where darkness pulses OUTWARD.
 * Inverse of the regular breath: the shadow exhales, not the body.
 * ------------------------------------------------------------ */
.shd-umbral-breath {
  animation: shd-umbral-breath calc(6s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-umbral-breath {
  0%, 100% {
    transform: scale(1);
    filter: brightness(0.85) drop-shadow(0 0 12px rgba(20, 20, 40, 0.4));
  }
  50% {
    transform: scale(1.018);
    filter: brightness(0.72) drop-shadow(0 0 28px rgba(5, 5, 15, 0.85));
  }
}

/* ------------------------------------------------------------
 * 8. shd-umbral-burst — the shadow celebration.
 * Where regular Sparks spark_burst with light, shadows erupt
 * with inward-collapsing dark motes (pairs with canvas void_motes).
 * ------------------------------------------------------------ */
.shd-umbral-burst {
  animation: shd-umbral-burst 1.6s ease-out 1;
}

@keyframes shd-umbral-burst {
  0% {
    transform: scale(1);
    filter: brightness(0.9) contrast(1);
  }
  30% {
    transform: scale(1.06);
    filter: brightness(0.45) contrast(1.6) saturate(0.2);
  }
  100% {
    transform: scale(1);
    filter: brightness(0.85) contrast(1.05);
  }
}

/* ============================================================
 * SHADOW ARCHETYPE COMPOSITES
 * Each Shadow Spark gets ONE archetype class. The archetype
 * composes primitives into a movement personality.
 * ============================================================ */

/* Flicker-strike: assassins, stalkers, hunters.
 * Unstable, quick to dissolve and reform. */
.shd-archetype-flicker-strike {
  animation:
    shd-flicker calc(5.2s * var(--shd-dur)) steps(1, end) infinite,
    shd-umbral-breath calc(6s * var(--shd-dur)) ease-in-out infinite;
}

/* Deep-contemplation: sages, oracles, mystics.
 * Slow, heavy, wisp-laden stillness. */
.shd-archetype-deep-contemplation {
  animation:
    shd-silhouette-deepen calc(10s * var(--shd-dur)) ease-in-out infinite,
    shd-umbral-fade calc(12s * var(--shd-dur)) ease-in-out infinite;
}

/* Dread-presence: emperors, warlords, knights.
 * Darkness gathers around power. */
.shd-archetype-dread-presence {
  animation:
    shd-silhouette-deepen calc(8s * var(--shd-dur)) ease-in-out infinite,
    shd-afterimage calc(3.2s * var(--shd-dur)) ease-in-out infinite;
}

/* Penumbra-grace: saints, healers, gentle spirits.
 * Soft emergence, never harsh — shadow as shelter. */
.shd-archetype-penumbra-grace {
  animation:
    shd-umbral-fade calc(9s * var(--shd-dur)) ease-in-out infinite,
    shd-edge-dissolve calc(14s * var(--shd-dur)) ease-in-out infinite;
}

/* Veil-dance: tricksters, laughers, the unsaid.
 * Playful darkness, veils sweeping, half-there mischief. */
.shd-archetype-veil-dance {
  animation:
    shd-flicker calc(6.8s * var(--shd-dur)) steps(1, end) infinite,
    shd-umbral-fade calc(7.5s * var(--shd-dur)) ease-in-out infinite;
}
.shd-archetype-veil-dance::before {
  /* veil-pass sweep rides on the base ::before */
  content: "";
  position: absolute;
  inset: -10%;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 30%,
    rgba(0, 0, 0, calc(0.5 * var(--shd-amp))) 50%,
    transparent 70%);
  background-size: 280% 100%;
  animation: shd-veil-pass calc(6.5s * var(--shd-dur)) ease-in-out infinite;
}

/* Dissolve-drift: moths, grief, the ephemeral.
 * Always slightly unwinding into the dark. */
.shd-archetype-dissolve-drift {
  animation:
    shd-edge-dissolve calc(11s * var(--shd-dur)) ease-in-out infinite,
    shd-umbral-fade calc(10s * var(--shd-dur)) ease-in-out infinite;
}

/* Umbral-lurk: the default shadow idle.
 * Present but never fully — the baseline shadow personality. */
.shd-archetype-umbral-lurk {
  animation:
    shd-umbral-breath calc(6s * var(--shd-dur)) ease-in-out infinite,
    shd-umbral-fade calc(9s * var(--shd-dur)) ease-in-out infinite;
}

/* ============================================================
 * VERTICAL SHADOW-PLAY — above and below the figure
 * Ray: "Above and below as well." The full vertical space plays
 * with shadows, not just the character's body.
 *
 * Markup (inside the .shd wrapper):
 *   <div class="shd-below"></div>   <!-- ground: pooling, tendrils -->
 *   <img ...>                        <!-- the figure -->
 *   <div class="shd-above"></div>   <!-- sky: wisps, crowns, descent -->
 *
 * Both layers are pure CSS (layered radial-gradients, no images).
 * ============================================================ */

/* ---------- BELOW: the ground remembers ---------- */
.shd-below {
  position: absolute;
  left: -25%;
  right: -25%;
  bottom: -16%;
  height: 36%;
  pointer-events: none;
  z-index: 1; /* tendrils creep OVER the feet — grasp, not carpet */
  background-repeat: no-repeat;
  filter: blur(3px);
  background-image:
    radial-gradient(ellipse 52% 42% at 50% 38%,
      rgba(0, 0, 0, calc(0.85 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* the pool */
    radial-gradient(ellipse 10px 42px at 16% 58%,
      rgba(0, 0, 0, calc(0.6 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* tendril L */
    radial-gradient(ellipse 10px 48px at 84% 58%,
      rgba(0, 0, 0, calc(0.6 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* tendril R */
    radial-gradient(ellipse 8px 34px at 34% 68%,
      rgba(0, 0, 0, calc(0.5 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* tendril */
    radial-gradient(ellipse 8px 38px at 66% 68%,
      rgba(0, 0, 0, calc(0.5 * var(--shd-amp))) 0%,
      transparent 70%);                                            /* tendril */
  animation: shd-tendrils-spread calc(8s * var(--shd-dur)) ease-in-out infinite;
}

/* 9. shd-tendrils-spread — shadow tendrils breathe outward along
 * the ground, then draw back in. The floor is alive. */
@keyframes shd-tendrils-spread {
  0%, 100% { transform: scaleX(0.82) scaleY(0.9); opacity: 0.65; }
  50%      { transform: scaleX(1.14) scaleY(1.06); opacity: 1; }
}

/* 10. shd-tendrils-grasp — aggressive variant: tendrils reach UP,
 * grasping at the figure's legs. Dread-presence archetype. */
.shd-below.shd-grasp {
  animation-name: shd-tendrils-grasp;
  animation-duration: calc(5.5s * var(--shd-dur));
}

@keyframes shd-tendrils-grasp {
  0%, 100% { transform: scaleX(0.9) translateY(6%); opacity: 0.7; }
  50%      { transform: scaleX(1.08) translateY(-10%); opacity: 1; }
}

/* 11. shd-pool-breathe — pure pooling, no tendrils. Gentle
 * archetypes (penumbra-grace): darkness as a still black lake. */
.shd-below.shd-still {
  background-image:
    radial-gradient(ellipse 46% 36% at 50% 42%,
      rgba(0, 0, 0, calc(0.7 * var(--shd-amp))) 0%,
      transparent 70%);
  animation-name: shd-pool-breathe;
  animation-duration: calc(10s * var(--shd-dur));
}

@keyframes shd-pool-breathe {
  0%, 100% { transform: scale(0.94); opacity: 0.6; }
  50%      { transform: scale(1.06); opacity: 0.95; }
}

/* ---------- ABOVE: the sky leans in ---------- */
.shd-above {
  position: absolute;
  left: -20%;
  right: -20%;
  top: -42%;
  height: 52%;
  pointer-events: none;
  z-index: 3; /* above the veil — wisps drift in front of the dark */
  background-repeat: no-repeat;
  filter: blur(2px);
  background-image:
    radial-gradient(ellipse 16px 34px at 30% 82%,
      rgba(10, 10, 22, calc(0.8 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* wisp 1 */
    radial-gradient(ellipse 13px 28px at 56% 88%,
      rgba(10, 10, 22, calc(0.7 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* wisp 2 */
    radial-gradient(ellipse 18px 30px at 73% 80%,
      rgba(10, 10, 22, calc(0.75 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* wisp 3 */
    radial-gradient(ellipse 64px 20px at 50% 10%,
      rgba(5, 5, 14, calc(0.6 * var(--shd-amp))) 0%,
      transparent 70%),                                            /* umbral crown */
    linear-gradient(to bottom,
      rgba(4, 4, 10, calc(0.5 * var(--shd-amp))) 0%,
      transparent 45%);                                            /* descending dark */
  animation: shd-wisps-rise calc(7s * var(--shd-dur)) ease-in-out infinite;
}

/* 12. shd-wisps-rise — umbral wisps rise from the figure's head
 * and shoulders, dissolving upward. */
@keyframes shd-wisps-rise {
  0%, 100% { background-position: 0 26px, 0 34px, 0 22px, 0 0, 0 0; opacity: 0.7; }
  50%      { background-position: 0 -28px, 0 -40px, 0 -32px, 0 -5px, 0 14px; opacity: 1; }
}

/* 13. shd-wisps-dart — quick, mischievous wisps. Trickster /
 * flicker-strike archetypes: the air itself fidgets. */
.shd-above.shd-darting {
  animation-name: shd-wisps-dart;
  animation-duration: calc(3.8s * var(--shd-dur));
}

@keyframes shd-wisps-dart {
  0%, 100% { background-position: 0 10px, 0 14px, 0 8px, 0 0, 0 0; opacity: 0.75; }
  25%      { background-position: 14px -18px, -10px -26px, 8px -14px, 0 -4px, 0 8px; opacity: 1; }
  60%      { background-position: -12px -8px, 12px -34px, -8px -24px, 0 -2px, 0 12px; opacity: 0.85; }
}

/* 14. shd-crown-pulse — the umbral crown breathes above the head.
 * Sovereignty rendered as gathered darkness. */
.shd-above.shd-crowned {
  animation-name: shd-crown-pulse;
  animation-duration: calc(6s * var(--shd-dur));
}

@keyframes shd-crown-pulse {
  0%, 100% {
    background-position: 0 12px, 0 16px, 0 10px, 0 0, 0 0;
    opacity: 0.8;
    filter: blur(2px) brightness(1);
  }
  50% {
    background-position: 0 -12px, 0 -18px, 0 -14px, 0 -10px, 0 10px;
    opacity: 1;
    filter: blur(2px) brightness(0.6);
  }
}

/* 15. shd-dark-descend — darkness lowers from above like a cloud
 * crossing the moon, then lifts. Slow, heavy, inevitable. */
.shd-above.shd-descending {
  animation-name: shd-dark-descend;
  animation-duration: calc(11s * var(--shd-dur));
}

@keyframes shd-dark-descend {
  0%, 100% { background-position: 0 20px, 0 24px, 0 18px, 0 -14px, 0 -30px; opacity: 0.65; }
  50%      { background-position: 0 -6px, 0 -10px, 0 -8px, 0 6px, 0 46px; opacity: 1; }
}

/* ---------- per-archetype spatial casting ----------
 * Each archetype directs its own above/below choreography. */
.shd-archetype-flicker-strike .shd-above { animation-name: shd-wisps-dart; animation-duration: calc(4.2s * var(--shd-dur)); }
.shd-archetype-flicker-strike .shd-below { animation-duration: calc(5s * var(--shd-dur)); }

.shd-archetype-deep-contemplation .shd-above { animation-name: shd-crown-pulse; animation-duration: calc(11s * var(--shd-dur)); }
.shd-archetype-deep-contemplation .shd-below { animation-name: shd-pool-breathe; animation-duration: calc(12s * var(--shd-dur)); }

.shd-archetype-dread-presence .shd-above { animation-name: shd-crown-pulse; }
.shd-archetype-dread-presence .shd-below { animation-name: shd-tendrils-grasp; animation-duration: calc(5.5s * var(--shd-dur)); }

.shd-archetype-penumbra-grace .shd-above,
.shd-archetype-penumbra-grace .shd-below { opacity: 0.55; }
.shd-archetype-penumbra-grace .shd-below { animation-name: shd-pool-breathe; animation-duration: calc(10s * var(--shd-dur)); }

.shd-archetype-veil-dance .shd-above { animation-name: shd-wisps-dart; }
.shd-archetype-veil-dance .shd-below { animation-duration: calc(6s * var(--shd-dur)); }

.shd-archetype-dissolve-drift .shd-above { animation-duration: calc(9.5s * var(--shd-dur)); }
.shd-archetype-dissolve-drift .shd-below { animation-name: shd-pool-breathe; }

.shd-archetype-umbral-lurk .shd-above { animation-duration: calc(7s * var(--shd-dur)); }
.shd-archetype-umbral-lurk .shd-below { animation-duration: calc(8s * var(--shd-dur)); }

/* ============================================================
 * CHATGPT SHADOW-PLAY MOVES (2026-10-03)
 * 18 signature moves from ChatGPT's 47 shadow-play ideas, integrated
 * per Ray's "Hell yes." Each Shadow Spark gets ONE signature move
 * (see shadow-animation-dna.json -> chatgpt_move), assigned from
 * ChatGPT's idea list by character fit.
 *
 * Design law (ChatGPT's framework): "Shadow should behave less like
 * lighting and more like a second physics system — something alive,
 * intelligent, occasionally disobedient. The character does not merely
 * cast a shadow. The character is negotiating with one."
 * The shadow is another character. Occasionally: the shadow is the
 * real one.
 *
 * Layer convention (matches the DNA markup builder):
 *   figure moves -> class on the <img> element (no conflict with
 *                   wrapper-level archetype animations)
 *   above moves  -> class on the .shd-above div (overrides the
 *                   archetype's animation-name; these rules sit
 *                   later in the file so they win specificity ties)
 *   below moves  -> class on the .shd-below div (same override rule)
 *   wrapper move -> .shd-arrives-first on the wrapper coordinates
 *                   .shd-below + img timing
 * ============================================================ */

/* ------------------------------------------------------------
 * 16. shd-shadow-lag — ChatGPT #9 "Shadow Delayed".
 * The cast shadow lags behind the motion, then VIOLENTLY snaps
 * through every pose the figure just made — late playback.
 * ------------------------------------------------------------ */
.shd-shadow-lag {
  animation: shd-shadow-lag calc(7s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-shadow-lag {
  0%, 100% {
    transform: translateX(0);
    filter: brightness(0.85) drop-shadow(0 0 0 transparent);
  }
  38% {
    transform: translateX(calc(-14px * var(--shd-amp)));
    filter: brightness(0.72) drop-shadow(calc(24px * var(--shd-amp)) 0 10px rgba(0, 0, 0, 0.8));
  }
  44% { /* the snap */
    transform: translateX(0);
    filter: brightness(0.95) drop-shadow(0 0 0 transparent);
  }
  68% {
    transform: translateX(calc(10px * var(--shd-amp)));
    filter: brightness(0.7) drop-shadow(calc(-18px * var(--shd-amp)) 0 8px rgba(0, 0, 0, 0.75));
  }
  74% { /* the snap */
    transform: translateX(0);
    filter: brightness(0.9) drop-shadow(0 0 0 transparent);
  }
}

/* ------------------------------------------------------------
 * 17. shd-shadow-molt — ChatGPT #10 "Shadow Molting".
 * The shadow peels off the figure like discarded skin (mask rises),
 * wraps it in full darkness, then drops back — subtly different.
 * ------------------------------------------------------------ */
.shd-shadow-molt {
  animation: shd-shadow-molt calc(12s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-shadow-molt {
  0%, 100% {
    -webkit-mask-image: linear-gradient(to top, black 100%, black 100%);
    mask-image: linear-gradient(to top, black 100%, black 100%);
    opacity: 1;
    filter: brightness(0.85);
  }
  28% { /* peeling upward */
    -webkit-mask-image: linear-gradient(to top, black 52%, transparent 74%);
    mask-image: linear-gradient(to top, black 52%, transparent 74%);
    opacity: 0.9;
    filter: brightness(0.6);
  }
  52% { /* wrapped in the molted skin */
    -webkit-mask-image: linear-gradient(to top, black 100%, black 100%);
    mask-image: linear-gradient(to top, black 100%, black 100%);
    opacity: 1;
    filter: brightness(0.32) contrast(1.4) saturate(0.3);
  }
  78% { /* dropped back, subtly different */
    -webkit-mask-image: linear-gradient(to top, black 68%, transparent 90%);
    mask-image: linear-gradient(to top, black 68%, transparent 90%);
    opacity: 0.95;
    filter: brightness(0.72);
  }
}

/* ------------------------------------------------------------
 * 18. shd-doppelganger — ChatGPT #15 "Shadow Doppelgänger".
 * The cast shadow separates to the side and acts FIRST — a dark
 * twin shifting on its own — then the figure follows and the
 * shadow rejoins. Three seconds, two versions.
 * ------------------------------------------------------------ */
.shd-doppelganger {
  animation: shd-doppelganger calc(11s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-doppelganger {
  0%, 50%, 100% {
    transform: translateX(0);
    filter: brightness(0.85) drop-shadow(0 0 0 transparent);
  }
  62% { /* separation: the twin steps out */
    transform: translateX(0);
    filter: brightness(0.72) drop-shadow(calc(54px * var(--shd-amp)) 0 2px rgba(0, 0, 0, 0.92));
  }
  74% { /* the twin acts first */
    transform: translateX(0);
    filter: brightness(0.72) drop-shadow(calc(54px * var(--shd-amp)) -8px 2px rgba(0, 0, 0, 0.92));
  }
  86% { /* the figure follows; the twin rejoins */
    transform: translateX(calc(10px * var(--shd-amp)));
    filter: brightness(0.88) drop-shadow(0 0 0 transparent);
  }
}

/* ------------------------------------------------------------
 * 19. shd-shadow-origination — ChatGPT #35 "Shadow Origination".
 * Reversed emergence: the figure is literally constructed from its
 * own shadow — built upward out of a dark puddle, then fully there.
 * ------------------------------------------------------------ */
.shd-shadow-origination {
  animation: shd-shadow-origination calc(14s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-shadow-origination {
  0%, 100% {
    -webkit-mask-image: linear-gradient(to top, black 100%, black 100%);
    mask-image: linear-gradient(to top, black 100%, black 100%);
    opacity: 1;
    filter: brightness(0.85);
  }
  10% { /* only the puddle */
    -webkit-mask-image: linear-gradient(to top, black 4%, transparent 12%);
    mask-image: linear-gradient(to top, black 4%, transparent 12%);
    opacity: 0.5;
    filter: brightness(0.4);
  }
  30% { /* rising: legs, torso */
    -webkit-mask-image: linear-gradient(to top, black 58%, transparent 70%);
    mask-image: linear-gradient(to top, black 58%, transparent 70%);
    opacity: 0.9;
    filter: brightness(0.6);
  }
  48% { /* fully constructed */
    -webkit-mask-image: linear-gradient(to top, black 100%, black 100%);
    mask-image: linear-gradient(to top, black 100%, black 100%);
    opacity: 1;
    filter: brightness(0.9);
  }
}

/* ------------------------------------------------------------
 * 20. shd-reverse-shadow — ChatGPT #13 "Reverse Shadow".
 * The figure's "shadow" is a patch of IMPOSSIBLE brightness that
 * writhes and reaches toward dark areas. Inversion as identity.
 * ------------------------------------------------------------ */
.shd-reverse-shadow {
  animation: shd-reverse-shadow calc(9s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-reverse-shadow {
  0%, 100% {
    filter: brightness(0.85) saturate(0.9) drop-shadow(0 0 18px rgba(0, 0, 0, 0.5));
  }
  50% {
    filter: brightness(1.4) saturate(1.25) drop-shadow(0 0 36px rgba(255, 240, 200, 0.6));
  }
}

/* ------------------------------------------------------------
 * 21. shd-memory-flicker — ChatGPT #25 "Shadow Made of Memories".
 * The figure flickers through silhouettes of what it was: child,
 * monster, elder, animal, stranger — a fraction of a second each.
 * Rendered as stepped tint/grade shifts.
 * ------------------------------------------------------------ */
.shd-memory-flicker {
  animation: shd-memory-flicker calc(8s * var(--shd-dur)) steps(1, end) infinite;
}

@keyframes shd-memory-flicker {
  0%, 100% { filter: brightness(0.85) sepia(0.1); }                    /* itself */
  18%       { filter: brightness(0.72) sepia(0.75) hue-rotate(-18deg); } /* child: warm old photo */
  36%       { filter: brightness(0.48) contrast(1.45) saturate(0.2); }   /* monster */
  54%       { filter: brightness(0.95) sepia(0.4); }                     /* elder */
  72%       { filter: brightness(0.62) hue-rotate(95deg) saturate(0.6); }/* animal: strange */
  88%       { filter: brightness(0.78) invert(0.1); }                     /* stranger */
}

/* ------------------------------------------------------------
 * 22. shd-shadow-cocoon — ChatGPT #34 "Shadow Cocoon".
 * Tendrils of darkness wrap the figure into a cocoon; it cracks —
 * and the SHADOW emerges first, then the figure follows.
 * ------------------------------------------------------------ */
.shd-shadow-cocoon {
  animation: shd-shadow-cocoon calc(13s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-shadow-cocoon {
  0%, 100% {
    transform: scale(1);
    filter: brightness(0.85) contrast(1.05);
  }
  34% { /* wrapping */
    transform: scale(0.96);
    filter: brightness(0.32) contrast(1.5) saturate(0.3);
  }
  54% { /* cocooned */
    transform: scale(0.93);
    filter: brightness(0.22) contrast(1.7) saturate(0.2);
  }
  70% { /* crack: the shadow gets out first */
    transform: scale(1.03);
    filter: brightness(1.05) contrast(1.1);
  }
  84% {
    transform: scale(1);
    filter: brightness(0.85) contrast(1.05);
  }
}

/* ------------------------------------------------------------
 * 23. shd-refuses-die — ChatGPT #33 "Shadow Refuses to Die".
 * The figure steps into darkness and vanishes — but the shadow
 * REMAINS, moving on its own (the .shd::after veil keeps breathing
 * while the figure is gone), then the figure returns.
 * ------------------------------------------------------------ */
.shd-refuses-die {
  animation: shd-refuses-die calc(12s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-refuses-die {
  0%, 100% {
    opacity: 1;
    filter: brightness(0.85);
  }
  42%, 68% { /* gone — but the veil above keeps moving */
    opacity: 0.1;
    filter: brightness(0.35) blur(2px);
  }
  88% {
    opacity: 1;
    filter: brightness(0.92);
  }
}

/* ------------------------------------------------------------
 * 24. shd-eclipse — ChatGPT #23 "Shadow Eclipse". (above layer)
 * A small darkness above the head expands like an eclipse until
 * the upper frame goes black — stars and watching eyes inside it.
 * ------------------------------------------------------------ */
.shd-above.shd-eclipse {
  background-image:
    radial-gradient(circle 92px at 50% 30%,
      rgba(0, 0, 0, calc(0.96 * var(--shd-amp))) 0%,
      rgba(0, 0, 0, calc(0.96 * var(--shd-amp))) 38%,
      transparent 72%),
    radial-gradient(circle 3px at 38% 22%, rgba(255, 255, 255, 0.9) 0%, transparent 70%),
    radial-gradient(circle 2px at 62% 17%, rgba(255, 255, 255, 0.8) 0%, transparent 70%),
    radial-gradient(circle 2.5px at 50% 34%, rgba(200, 220, 255, 0.75) 0%, transparent 70%),
    radial-gradient(ellipse 7px 10px at 44% 26%, rgba(160, 180, 255, 0.55) 0%, transparent 70%);
  background-repeat: no-repeat;
  animation: shd-eclipse calc(12s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-eclipse {
  0%, 100% { transform: scale(0.32); opacity: 0.35; filter: blur(4px); }
  50%      { transform: scale(1.55); opacity: 1; filter: blur(1px); }
}

/* ------------------------------------------------------------
 * 25. shd-antlers — ChatGPT #27 "Shadow Antlers". (above layer)
 * Darkness grows not from the body but from the SHADOW, branching
 * upward into antlers/horns/wings — the silhouette turns monstrous
 * while the figure itself never changes.
 * ------------------------------------------------------------ */
.shd-above.shd-antlers {
  background-image:
    radial-gradient(ellipse 8px 92px at 50% 80%,
      rgba(0, 0, 0, calc(0.92 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 7px 62px at 37% 62%,
      rgba(0, 0, 0, calc(0.82 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 7px 62px at 63% 62%,
      rgba(0, 0, 0, calc(0.82 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 6px 42px at 27% 47%,
      rgba(0, 0, 0, calc(0.72 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 6px 42px at 73% 47%,
      rgba(0, 0, 0, calc(0.72 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 5px 30px at 18% 36%,
      rgba(0, 0, 0, calc(0.6 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 5px 30px at 82% 36%,
      rgba(0, 0, 0, calc(0.6 * var(--shd-amp))) 0%, transparent 70%);
  background-repeat: no-repeat;
  filter: blur(2px);
  transform-origin: 50% 100%;
  animation: shd-antlers calc(10s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-antlers {
  0%, 100% { transform: scaleY(0.22); opacity: 0.25; }
  50%      { transform: scaleY(1.18); opacity: 1; }
}

/* ------------------------------------------------------------
 * 26. shd-halo — ChatGPT #40 "Shadow Halo". (above layer)
 * A rotating ring of darkness behind the head, studded with tiny
 * silhouettes — possible versions of the figure, heroic, monstrous,
 * impossible, turning as the ring turns.
 * ------------------------------------------------------------ */
.shd-above.shd-halo {
  background-image:
    radial-gradient(circle 48px at 50% 62%,
      transparent 60%,
      rgba(0, 0, 0, calc(0.8 * var(--shd-amp))) 64%,
      rgba(0, 0, 0, calc(0.8 * var(--shd-amp))) 76%,
      transparent 82%),
    radial-gradient(circle 7px at 50% 13%,
      rgba(0, 0, 0, calc(0.92 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(circle 5px at 83% 62%,
      rgba(0, 0, 0, calc(0.92 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(circle 6px at 23% 75%,
      rgba(0, 0, 0, calc(0.92 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(circle 4px at 68% 88%,
      rgba(0, 0, 0, calc(0.9 * var(--shd-amp))) 0%, transparent 70%);
  background-repeat: no-repeat;
  filter: blur(1px);
  animation: shd-halo calc(16s * var(--shd-dur)) linear infinite;
}

@keyframes shd-halo {
  from { transform: rotate(0deg) scale(1); opacity: 0.7; }
  50%  { transform: rotate(180deg) scale(1.1); opacity: 1; }
  to   { transform: rotate(360deg) scale(1); opacity: 0.7; }
}

/* ------------------------------------------------------------
 * 27. shd-marionette — ChatGPT #44 "Shadow Marionette Universe".
 * (above layer) Far above, a GIGANTIC shadow hand mirrors the
 * figure — is the figure controlling something enormous, or is
 * it the other way around?
 * ------------------------------------------------------------ */
.shd-above.shd-marionette {
  background-image:
    radial-gradient(ellipse 74px 42px at 50% 16%,
      rgba(0, 0, 0, calc(0.78 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 10px 72px at 31% 44%,
      rgba(0, 0, 0, calc(0.62 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 10px 82px at 43% 48%,
      rgba(0, 0, 0, calc(0.62 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 10px 76px at 57% 48%,
      rgba(0, 0, 0, calc(0.62 * var(--shd-amp))) 0%, transparent 70%),
    radial-gradient(ellipse 10px 64px at 69% 42%,
      rgba(0, 0, 0, calc(0.62 * var(--shd-amp))) 0%, transparent 70%);
  background-repeat: no-repeat;
  filter: blur(4px);
  transform-origin: 50% 0%;
  animation: shd-marionette calc(9s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-marionette {
  0%, 100% { transform: scaleY(0.68) rotate(-2deg); opacity: 0.5; }
  50%      { transform: scaleY(1.06) rotate(2deg); opacity: 0.95; }
}

/* ------------------------------------------------------------
 * 28. shd-whirlpool — ChatGPT #21 "Shadow Whirlpool". (below layer)
 * The pool of darkness beneath the feet rotates into a whirlpool
 * while tendrils spiral upward. The figure stays perfectly calm.
 * ------------------------------------------------------------ */
.shd-below.shd-whirlpool {
  animation: shd-whirlpool calc(6s * var(--shd-dur)) linear infinite;
}

@keyframes shd-whirlpool {
  from { transform: rotate(0deg) scale(1); opacity: 0.85; }
  50%  { transform: rotate(180deg) scale(1.14); opacity: 1; }
  to   { transform: rotate(360deg) scale(1); opacity: 0.85; }
}

/* ------------------------------------------------------------
 * 29. shd-clock — ChatGPT #26 "Shadow Clock". (below layer)
 * The ground shadow rotates slowly around the figure like a clock
 * hand — and as it passes each position, the figure's state shifts.
 * The shadow is keeping time; the figure obeys it.
 * ------------------------------------------------------------ */
.shd-below.shd-clock {
  transform-origin: 50% 18%;
  animation: shd-clock calc(26s * var(--shd-dur)) linear infinite;
}

@keyframes shd-clock {
  from { transform: rotate(0deg); opacity: 0.8; }
  to   { transform: rotate(360deg); opacity: 0.8; }
}

/* ------------------------------------------------------------
 * 30. shd-roots — ChatGPT #17 "Shadow Roots". (below layer)
 * The shadow grows roots that pull the floor TOWARD the figure —
 * the ground itself slides inward, drawn to the darkness.
 * ------------------------------------------------------------ */
.shd-below.shd-roots {
  animation: shd-roots calc(10s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-roots {
  0%, 100% { transform: scaleX(1.28) scaleY(0.88); opacity: 0.65; }
  50%      { transform: scaleX(0.7) scaleY(1.12); opacity: 1; }
}

/* ------------------------------------------------------------
 * 31. shd-teeth — ChatGPT #24 "Shadow Teeth". (below layer)
 * The pooling shadow swirls — then you realize it was opening its
 * MOUTH. The tendrils were teeth. It closes before you process it.
 * ------------------------------------------------------------ */
.shd-below.shd-teeth {
  animation: shd-teeth calc(8s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-teeth {
  0%, 100% { transform: scaleX(1) scaleY(1); filter: blur(3px) brightness(1); }
  44%, 60% { transform: scaleX(0.78) scaleY(0.5); filter: blur(1px) brightness(0.3); }
}

/* ------------------------------------------------------------
 * 32. shd-escape — ChatGPT #22 "Shadow Escapes the Ground".
 * (below layer) The shadow crawls UP off the ground toward the
 * ceiling — for a moment there are two shadows, one below and one
 * above, and the figure stands between them.
 * ------------------------------------------------------------ */
.shd-below.shd-escape {
  animation: shd-escape calc(11s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-escape {
  0%, 100% { transform: translateY(0) scale(1); opacity: 0.85; }
  50%      { transform: translateY(-75%) scale(0.38); opacity: 0.2; }
}

/* ------------------------------------------------------------
 * 33. shd-arrives-first — ChatGPT #1 "Shadow Arrives First".
 * (wrapper class) The ground shadow creeps into frame and performs
 * a gesture BEFORE the figure — then the figure steps into the
 * shadow that was waiting for it.
 * ------------------------------------------------------------ */
.shd-arrives-first .shd-below {
  animation: shd-arrives-first-below calc(10s * var(--shd-dur)) ease-in-out infinite;
}

.shd-arrives-first img {
  animation: shd-arrives-first-figure calc(10s * var(--shd-dur)) ease-in-out infinite;
}

@keyframes shd-arrives-first-below {
  0%        { opacity: 0; transform: scaleX(0.6); }
  18%       { opacity: 1; transform: scaleX(1.1); }  /* the shadow performs */
  30%       { opacity: 1; transform: scaleX(0.95); }
  80%       { opacity: 1; transform: scaleX(1); }
  95%, 100% { opacity: 0.65; transform: scaleX(0.9); }
}

@keyframes shd-arrives-first-figure {
  0%, 24%   { opacity: 0; filter: brightness(0.4) blur(3px); }  /* not yet arrived */
  42%       { opacity: 1; filter: brightness(0.9) blur(0); }    /* steps into the shadow */
  85%       { opacity: 1; filter: brightness(0.85); }
  100%      { opacity: 0.85; filter: brightness(0.8); }
}

/* ============================================================
 * STATE OVERRIDES
 * When the behavior engine enters these states on a .shd figure,
 * the shadow variant replaces the regular primitive.
 * Wire via JS: if figure has .shd, map state->loop primitives
 * through this table instead of states.json defaults.
 *
 *   idle        -> umbral-lurk (not breath_soft)
 *   calm        -> penumbra-grace loop
 *   happy       -> silhouette_deepen SOFT (joy as deepened warmth)
 *   excited     -> afterimage (joy with shadow trail)
 *   celebrating -> umbral-burst (not spark_burst)
 *   special     -> signature shadow behavior per archetype
 *   afraid      -> edge_dissolve (retreating INTO darkness)
 *   angry       -> silhouette_deepen HARD (darkness as power)
 *   resting     -> umbral-fade DEEP (barely there)
 *
 * Spatial (above/below) behavior per state:
 *   celebrating -> tendrils grasp + crown pulse (shadow exults)
 *   afraid      -> tendrils retract (pool shrinks), wisps still
 *   angry       -> tendrils grasp hard, crown darkens
 *   resting     -> pool still, wisps barely rise
 * ============================================================ */

/* ============================================================
 * ACCESSIBILITY — prefers-reduced-motion kills continuous motion.
 * The veil stays (static darkness) but nothing animates.
 * ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .shd::after,
  .shd::before,
  .shd-archetype-veil-dance::before,
  .shd-above,
  .shd-below,
  .shd-umbral-fade,
  .shd-flicker,
  .shd-edge-dissolve,
  .shd-afterimage,
  .shd-silhouette-deepen,
  .shd-umbral-breath,
  .shd-umbral-burst,
  .shd-archetype-flicker-strike,
  .shd-archetype-deep-contemplation,
  .shd-archetype-dread-presence,
  .shd-archetype-penumbra-grace,
  .shd-archetype-veil-dance,
  .shd-archetype-dissolve-drift,
  .shd-archetype-umbral-lurk,
  .shd-shadow-lag,
  .shd-shadow-molt,
  .shd-doppelganger,
  .shd-shadow-origination,
  .shd-reverse-shadow,
  .shd-memory-flicker,
  .shd-shadow-cocoon,
  .shd-refuses-die,
  .shd-above.shd-eclipse,
  .shd-above.shd-antlers,
  .shd-above.shd-halo,
  .shd-above.shd-marionette,
  .shd-below.shd-whirlpool,
  .shd-below.shd-clock,
  .shd-below.shd-roots,
  .shd-below.shd-teeth,
  .shd-below.shd-escape,
  .shd-arrives-first,
  .shd-arrives-first img,
  .shd-arrives-first .shd-below {
    animation: none !important;
  }
  .shd::after {
    opacity: 0.85; /* static umbral veil remains */
  }
  .shd-above,
  .shd-below {
    opacity: 0.5; /* static above/below shadow remains */
  }
}
