/* All effect visuals live in the browser shell's pointer-transparent layer. */
.browser-shell > .effects-layer {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: hidden;
  pointer-events: none;
  color: #57434c;
  font-family: "Helvetica Neue", "Hiragino Kaku Gothic ProN", Arial, sans-serif;
  user-select: none;
}

.effects-layer .effect {
  position: absolute;
  left: var(--effect-x, 50%);
  top: var(--effect-y, 45%);
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  isolation: isolate;
  --effect-strength: 1;
}
.effects-layer .effect-chaos {
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transform: none;
}

.effects-layer .effect[data-material="gum"] {
  --effect-material-wobble: 1.08;
  --effect-material-tilt: -3deg;
}
.effects-layer .effect[data-material="jelly"] {
  --effect-material-wobble: 1.14;
  --effect-material-tilt: 3deg;
}
.effects-layer .effect[data-material="spring"] {
  --effect-material-wobble: 1.05;
  --effect-material-tilt: -5deg;
}
.effects-layer .effect[data-material="stone"] {
  --effect-material-wobble: 1.02;
  --effect-material-tilt: 1deg;
}
.effects-layer .effect[data-material] {
  border-color: var(--effect-material-outline, #c36f8a);
}

/* The creature itself rises during float; #skin's path and #face's SVG
   translate remain owned by blob-view.mjs. */
.browser-shell #blob.effects-float {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: effect-float-creature 1.9s ease-in-out infinite;
}

/* Gravity and bounce own only the body pose. The view still owns the SVG
   path and face attributes, so its drag geometry remains stable. */
.browser-shell #blob.effects-gravity {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: effect-gravity-creature 1.55s cubic-bezier(.2,.8,.3,1) infinite;
}
.browser-shell #blob.effects-bounce {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: effect-bounce-creature 1.8s cubic-bezier(.2,.85,.25,1) infinite;
}
.browser-shell[data-effect-material="stone"] #blob.effects-gravity {
  animation-duration: 2.15s;
}
.browser-shell[data-effect-material="jelly"] #blob.effects-bounce {
  animation-duration: 2.1s;
}

/* The play surface turns inside the shell. Controls and surrounding page stay still. */
.browser-shell #creature.effects-spin {
  transform-box: fill-box;
  transform-origin: center;
  animation: effect-play-area-spin 2.4s cubic-bezier(.26,.78,.32,1) both;
}

.effects-layer .effect::before,
.effects-layer .effect::after {
  position: absolute;
  pointer-events: none;
}

/* Sneeze: a soft cloud and paper-confetti puff. */
.effects-layer .effect-sneeze { z-index: 3; }
.effects-layer .sneeze-cloud {
  position: absolute;
  left: -52px;
  top: -23px;
  min-width: 104px;
  padding: 8px 12px;
  border: 2px solid var(--effect-material-secondary, #e4afbf);
  border-radius: 60% 44% 55% 48%;
  background: #fff9f5;
  color: var(--effect-material-outline, #b2607f);
  box-shadow: 0 5px 0 #edcbd6;
  font-size: 12px;
  font-weight: 700;
  text-align: center;
  white-space: nowrap;
  animation: effect-pop 2.1s ease-out both;
}
.effects-layer .sneeze-bit {
  position: absolute;
  left: -5px;
  top: -5px;
  color: var(--effect-material-primary, #e997af);
  font-size: calc(11px + var(--effect-strength) * 3px);
  font-weight: 800;
  animation: effect-burst 2s cubic-bezier(.2,.75,.25,1) var(--burst-delay, 0ms) both;
}
.effects-layer .sneeze-bit:nth-child(3n) { color: #eac95e; }
.effects-layer .sneeze-bit:nth-child(3n + 1) { color: #8fcfc9; }

/* Rain: friendly eggs tumble down over the little browser window. */
.effects-layer .effect-rain {
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transform: none;
}
.effects-layer .rain-egg {
  position: absolute;
  left: var(--egg-x);
  top: -32px;
  display: block;
  color: #fff9e8;
  font-size: 26px;
  filter: drop-shadow(0 2px 0 #d7b98a66);
  animation: effect-egg-fall 1.6s cubic-bezier(.28,.6,.48,1) var(--egg-delay, 0ms) infinite;
  transform-origin: 50% 70%;
}

/* Float: bubbles and a tiny handwritten caption. */
.effects-layer .effect-float { z-index: 2; width:180px; height:100px; }
.effects-layer .float-label {
  position: absolute;
  left: -28px;
  top: -40px;
  color: var(--effect-material-outline, #5fa9ae);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .08em;
  white-space: nowrap;
  transform: rotate(-7deg);
  animation: effect-label-float 2.4s ease-in-out both;
}
.effects-layer .float-bubble {
  position: absolute;
  left: var(--bubble-x);
  top: 18px;
  display: block;
  width: var(--bubble-size);
  height: var(--bubble-size);
  border: 2px solid var(--effect-material-secondary, #8bcfc8);
  border-radius: 50%;
  background: #dff7ee99;
  box-shadow: inset 3px 3px 0 #fff9f5aa;
  animation: effect-bubble 2.5s ease-in-out var(--bubble-delay, 0ms) both;
}

/* Heavy mode drops toward the floor and leaves a small, readable cue. */
.effects-layer .effect-gravity {
  z-index: 3;
  width: 170px;
  height: 150px;
}
.effects-layer .gravity-caption,
.effects-layer .gravity-mark {
  position: absolute;
  color: var(--effect-material-secondary, #806a72);
  font-weight: 800;
  white-space: nowrap;
  pointer-events: none;
}
.effects-layer .gravity-caption {
  left: -56px;
  top: -58px;
  padding: 6px 9px;
  border: 2px solid var(--effect-material-secondary, #c19aa8);
  border-radius: 48% 55% 45% 52%;
  background: #fff9f1;
  font-size: 12px;
  transform: rotate(var(--effect-material-tilt, -2deg));
  animation: effect-gravity-caption 1.55s ease-out both;
}
.effects-layer .gravity-mark {
  left: 50%;
  top: 8px;
  color: var(--effect-material-outline, #7e6b86);
  font-size: 22px;
  animation: effect-gravity-mark 1.55s ease-in both;
}

/* Bounce shows anticipation, lift, landing, and a floor ring. */
.effects-layer .effect-bounce {
  z-index: 3;
  width: 220px;
  height: 160px;
}
.effects-layer .effect-bounce::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -28px;
  width: 150px;
  height: 30px;
  border: 3px solid var(--effect-material-secondary, #d4a5b7);
  border-radius: 50%;
  opacity: .8;
  transform: translateX(-50%) scale(.45);
  animation: effect-bounce-ring 1.8s ease-out infinite;
}
.effects-layer .bounce-caption {
  position: absolute;
  left: -45px;
  top: -62px;
  z-index: 2;
  color: var(--effect-material-secondary, #a75c78);
  font-size: 14px;
  font-weight: 900;
  white-space: nowrap;
  transform: rotate(calc(var(--effect-material-tilt, -2deg) * -1));
  animation: effect-bounce-caption 1.8s ease-out infinite;
}
.effects-layer .bounce-mark {
  position: absolute;
  left: calc(50% + (var(--bounce-index, 0) - 1) * 30px);
  bottom: 4px;
  color: var(--effect-material-outline, #b65b79);
  font-size: 19px;
  font-weight: 900;
  animation: effect-bounce-dot 1.8s ease-in-out calc(var(--bounce-index, 0) * 80ms) infinite;
}

/* Clones: CSS-drawn tiny faces keep this effect light and text-friendly. */
.effects-layer .effect-clones { z-index: 2; width: 210px; height: 115px; }
.effects-layer .tiny-clone {
  position: absolute;
  left: var(--clone-x);
  top: 42px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 2px solid var(--effect-material-outline, #c36f8a);
  border-radius: 48% 52% 45% 55%;
  background: linear-gradient(145deg, var(--effect-material-primary, #ffd8df), var(--effect-material-secondary, #ee91aa));
  color: var(--effect-material-outline, #81445a);
  font-size: 13px;
  font-weight: 800;
  box-shadow: 0 3px 0 #b9617b55;
  animation: effect-clone 3.1s ease-in-out var(--clone-delay, 0ms) both;
  transform: translateX(-50%) rotate(var(--clone-tilt, 0deg));
}
.effects-layer .tiny-clone::before {
  content: "•  •";
  position: absolute;
  top: 4px;
  font-size: 7px;
  letter-spacing: 4px;
}

/* Chorus: each bubble says something the current character actually says. */
.effects-layer .effect-chorus { z-index: 4; width: 220px; height: 130px; }
.effects-layer .chorus-bubble {
  position: absolute;
  left: var(--chorus-x);
  top: 18px;
  max-width: 92px;
  padding: 7px 10px;
  border: 2px solid var(--effect-material-secondary, #efc963);
  border-radius: 52% 45% 50% 46%;
  background: #fff9df;
  color: var(--effect-material-outline, #8b6940);
  font-size: 12px;
  font-weight: 700;
  text-align: center;
  white-space: nowrap;
  box-shadow: 0 4px 0 #dfbf6755;
  animation: effect-chorus 2.5s ease-in-out var(--chorus-delay, 0ms) both;
  transform: translateX(-50%);
}

/* Spin: rings make the play area feel dizzy without rotating the draggable SVG. */
.effects-layer .effect-spin {
  z-index: 1;
  width: 240px;
  height: 180px;
}
.effects-layer .spin-caption {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 4;
  color: #a995c0;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  transform: translate(-50%, -50%) rotate(-10deg);
  animation: effect-caption 2.35s ease-out both;
}
.effects-layer .spin-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  display: block;
  width: 100%;
  height: 68%;
  border: 3px dashed var(--effect-material-secondary, #b19ac7);
  opacity: .68;
  border-radius: 50%;
  animation: effect-spin 2.2s linear both;
  transform: translate(-50%, -50%) rotate(0deg);
}
.effects-layer .spin-ring-2 {
  width: 82%;
  height: 52%;
  border-color: #e3bd64aa;
  animation-direction: reverse;
  animation-duration: 1.85s;
}
.effects-layer .spin-ring-3 {
  width: 118%;
  height: 83%;
  border-color: #e99bb1aa;
  animation-duration: 2.8s;
}

/* Giant: the view owns #face's translate/rotate; this inner group owns only scale. */
.browser-shell #face-size.effects-giant {
  transform-box: fill-box;
  transform-origin: center;
  animation: effect-giant 2.85s cubic-bezier(.26,.9,.32,1) both;
}
.effects-layer .effect-giant { z-index: 5; }
.effects-layer .giant-caption {
  position: absolute;
  left: -48px;
  top: -70px;
  padding: 7px 10px;
  border: 2px solid var(--effect-material-secondary, #d77995);
  border-radius: 45% 55% 48% 52%;
  background: #fff0f3;
  color: var(--effect-material-outline, #a84f6d);
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
  transform: rotate(7deg);
  animation: effect-pop 2.6s ease-out both;
}

/* Each action gets its own small face/skin beat. Longhand motion properties
   compose with the view's #face transform and the disco transform. */
.browser-shell[data-effect-material="gum"] {
  --effect-reaction-shift: -3px;
  --effect-reaction-tilt: -3deg;
  --effect-reaction-wobble: 1.08;
  --effect-reaction-speed: .95s;
}
.browser-shell[data-effect-material="jelly"] {
  --effect-reaction-shift: 4px;
  --effect-reaction-tilt: 3deg;
  --effect-reaction-wobble: 1.14;
  --effect-reaction-speed: 1.24s;
}
.browser-shell[data-effect-material="spring"] {
  --effect-reaction-shift: -2px;
  --effect-reaction-tilt: -5deg;
  --effect-reaction-wobble: 1.05;
  --effect-reaction-speed: .72s;
}
.browser-shell[data-effect-material="stone"] {
  --effect-reaction-shift: 1px;
  --effect-reaction-tilt: 1deg;
  --effect-reaction-wobble: 1.02;
  --effect-reaction-speed: 1.55s;
}
.browser-shell[data-effect-reaction] #skin,
.browser-shell[data-effect-reaction] #face-vibe,
.browser-shell[data-effect-reaction] #eye-left,
.browser-shell[data-effect-reaction] #eye-right {
  transform-box: fill-box;
  transform-origin: center;
}
.browser-shell[data-effect-reaction="sneeze"] #skin,
.browser-shell[data-effect-reaction="sneeze"] #face-vibe,
.browser-shell[data-effect-reaction="sneeze"] #eye-left,
.browser-shell[data-effect-reaction="sneeze"] #eye-right { animation: effect-pose-sneeze var(--effect-reaction-speed) ease-out both; }
.browser-shell[data-effect-reaction="rain"] #skin,
.browser-shell[data-effect-reaction="rain"] #face-vibe,
.browser-shell[data-effect-reaction="rain"] #eye-left,
.browser-shell[data-effect-reaction="rain"] #eye-right { animation: effect-pose-rain var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="float"] #skin,
.browser-shell[data-effect-reaction="float"] #face-vibe,
.browser-shell[data-effect-reaction="float"] #eye-left,
.browser-shell[data-effect-reaction="float"] #eye-right { animation: effect-pose-float var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="clones"] #skin,
.browser-shell[data-effect-reaction="clones"] #face-vibe,
.browser-shell[data-effect-reaction="clones"] #eye-left,
.browser-shell[data-effect-reaction="clones"] #eye-right { animation: effect-pose-clones var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="chorus"] #skin,
.browser-shell[data-effect-reaction="chorus"] #face-vibe,
.browser-shell[data-effect-reaction="chorus"] #eye-left,
.browser-shell[data-effect-reaction="chorus"] #eye-right { animation: effect-pose-chorus var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="spin"] #skin,
.browser-shell[data-effect-reaction="spin"] #face-vibe,
.browser-shell[data-effect-reaction="spin"] #eye-left,
.browser-shell[data-effect-reaction="spin"] #eye-right { animation: effect-pose-spin var(--effect-reaction-speed) linear both; }
.browser-shell[data-effect-reaction="giant"] #skin,
.browser-shell[data-effect-reaction="giant"] #face-vibe,
.browser-shell[data-effect-reaction="giant"] #eye-left,
.browser-shell[data-effect-reaction="giant"] #eye-right { animation: effect-pose-giant var(--effect-reaction-speed) cubic-bezier(.25,.9,.3,1) both; }
.browser-shell[data-effect-reaction="chaos"] #skin,
.browser-shell[data-effect-reaction="chaos"] #face-vibe,
.browser-shell[data-effect-reaction="chaos"] #eye-left,
.browser-shell[data-effect-reaction="chaos"] #eye-right { animation: effect-pose-chaos var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="gravity"] #skin,
.browser-shell[data-effect-reaction="gravity"] #face-vibe,
.browser-shell[data-effect-reaction="gravity"] #eye-left,
.browser-shell[data-effect-reaction="gravity"] #eye-right { animation: effect-pose-gravity var(--effect-reaction-speed) ease-in both; }
.browser-shell[data-effect-reaction="bounce"] #skin,
.browser-shell[data-effect-reaction="bounce"] #face-vibe,
.browser-shell[data-effect-reaction="bounce"] #eye-left,
.browser-shell[data-effect-reaction="bounce"] #eye-right { animation: effect-pose-bounce var(--effect-reaction-speed) cubic-bezier(.2,.85,.25,1) both; }

.browser-shell[data-effect-reaction="sneeze"] #mouth { animation: effect-mouth-sneeze var(--effect-reaction-speed) ease-out both; }
.browser-shell[data-effect-reaction="rain"] #mouth { animation: effect-mouth-rain var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="float"] #mouth { animation: effect-mouth-float var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="clones"] #mouth { animation: effect-mouth-clones var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="chorus"] #mouth { animation: effect-mouth-chorus var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="spin"] #mouth { animation: effect-mouth-spin var(--effect-reaction-speed) linear both; }
.browser-shell[data-effect-reaction="giant"] #mouth { animation: effect-mouth-giant var(--effect-reaction-speed) cubic-bezier(.25,.9,.3,1) both; }
.browser-shell[data-effect-reaction="chaos"] #mouth { animation: effect-mouth-chaos var(--effect-reaction-speed) ease-in-out both; }
.browser-shell[data-effect-reaction="gravity"] #mouth { animation: effect-mouth-gravity var(--effect-reaction-speed) ease-in both; }
.browser-shell[data-effect-reaction="bounce"] #mouth { animation: effect-mouth-bounce var(--effect-reaction-speed) cubic-bezier(.2,.85,.25,1) both; }

/* Habits alter the rhythm without replacing the material pose. */
.browser-shell[data-effect-habit="hummer"] #face-vibe { animation-timing-function: steps(4, end); }
.browser-shell[data-effect-habit="twins"] #face-vibe { animation-timing-function: ease-in-out; animation-iteration-count: 2; }
.browser-shell[data-effect-habit="face"] #mouth { filter: drop-shadow(0 1px 0 var(--effect-material-secondary, #d77897)); }
.browser-shell[data-effect-habit="float"] #face-vibe { animation-delay: 80ms; }
.browser-shell[data-effect-habit="spin"] #face-vibe { animation-iteration-count: 2; }
.browser-shell[data-effect-habit="still"] #skin,
.browser-shell[data-effect-habit="still"] #face-vibe { animation-duration: 2.2s; }

@keyframes effect-pose-sneeze {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  32% { translate: var(--effect-reaction-shift) -3px; rotate: var(--effect-reaction-tilt); scale: var(--effect-reaction-wobble) .94; }
  58% { translate: calc(var(--effect-reaction-shift) * -1) 1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: .96 1.04; }
}
@keyframes effect-pose-rain {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  35% { translate: 0 2px; rotate: var(--effect-reaction-tilt); scale: .97 var(--effect-reaction-wobble); }
  70% { translate: var(--effect-reaction-shift) -1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: 1.02 .98; }
}
@keyframes effect-pose-float {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  50% { translate: var(--effect-reaction-shift) -5px; rotate: var(--effect-reaction-tilt); scale: .98 var(--effect-reaction-wobble); }
}
@keyframes effect-pose-clones {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  28% { translate: calc(var(--effect-reaction-shift) * -1) 0; rotate: var(--effect-reaction-tilt); scale: .96 1.04; }
  68% { translate: var(--effect-reaction-shift) -2px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: var(--effect-reaction-wobble) .96; }
}
@keyframes effect-pose-chorus {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  34% { translate: var(--effect-reaction-shift) 0; rotate: var(--effect-reaction-tilt); scale: .98 var(--effect-reaction-wobble); }
  66% { translate: calc(var(--effect-reaction-shift) * -1) 1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: var(--effect-reaction-wobble) .98; }
}
@keyframes effect-pose-spin {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  50% { translate: 0 -2px; rotate: calc(var(--effect-reaction-tilt) * 2); scale: .98 var(--effect-reaction-wobble); }
}
@keyframes effect-pose-giant {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  36% { translate: 0 -3px; rotate: var(--effect-reaction-tilt); scale: var(--effect-reaction-wobble) var(--effect-reaction-wobble); }
  68% { translate: 0 1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: 1.04 1.04; }
}
@keyframes effect-pose-chaos {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  25% { translate: var(--effect-reaction-shift) -2px; rotate: var(--effect-reaction-tilt); scale: .96 var(--effect-reaction-wobble); }
  50% { translate: calc(var(--effect-reaction-shift) * -1) 1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: var(--effect-reaction-wobble) .96; }
  75% { translate: 0 -1px; rotate: var(--effect-reaction-tilt); scale: 1 1; }
}
@keyframes effect-pose-gravity {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  35% { translate: var(--effect-reaction-shift) 4px; rotate: var(--effect-reaction-tilt); scale: 1.05 .9; }
  64% { translate: 0 1px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: var(--effect-reaction-wobble) .94; }
}
@keyframes effect-pose-bounce {
  0%, 100% { translate: 0 0; rotate: 0deg; scale: 1 1; }
  18% { translate: 0 3px; rotate: var(--effect-reaction-tilt); scale: 1.08 .84; }
  55% { translate: var(--effect-reaction-shift) -7px; rotate: calc(var(--effect-reaction-tilt) * -1); scale: .95 var(--effect-reaction-wobble); }
  78% { translate: 0 2px; rotate: var(--effect-reaction-tilt); scale: 1.05 .9; }
}

@keyframes effect-mouth-sneeze { 0%,100% { scale: 1 1; } 38% { scale: 1.5 .62; } 62% { scale: .78 1.25; } }
@keyframes effect-mouth-rain { 0%,100% { scale: 1 1; } 38% { scale: .82 1.36; } 70% { scale: 1.16 .9; } }
@keyframes effect-mouth-float { 0%,100% { scale: 1 1; } 50% { scale: .72 1.5; } }
@keyframes effect-mouth-clones { 0%,100% { scale: 1 1; } 30% { scale: 1.32 .8; } 64% { scale: .72 1.2; } }
@keyframes effect-mouth-chorus { 0%,100% { scale: .82 .9; } 32% { scale: 1.5 1.2; } 66% { scale: .64 .82; } }
@keyframes effect-mouth-spin { 0%,100% { scale: 1 1; } 50% { scale: 1.25 .72; } }
@keyframes effect-mouth-giant { 0%,100% { scale: 1 1; } 36% { scale: 2 1.6; } 68% { scale: 1.2 1.05; } }
@keyframes effect-mouth-chaos { 0%,100% { scale: 1 1; } 25% { scale: .7 1.3; } 50% { scale: 1.4 .72; } 75% { scale: .86 1.18; } }
@keyframes effect-mouth-gravity { 0%,100% { scale: .86 1.08; } 36% { scale: 1.2 .7; } 66% { scale: .72 1.18; } }
@keyframes effect-mouth-bounce { 0%,100% { scale: 1 1; } 18% { scale: 1.4 .66; } 55% { scale: .72 1.35; } 78% { scale: 1.18 .82; } }

@keyframes effect-pop {
  0% { opacity: 0; transform: translateY(8px) scale(.78) rotate(-4deg); }
  18% { opacity: 1; transform: translateY(0) scale(1.04) rotate(2deg); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-17px) scale(.94) rotate(3deg); }
}
@keyframes effect-burst {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.4) rotate(0deg); }
  18% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--burst-x), var(--burst-y)) scale(1.15) rotate(180deg); }
}
@keyframes effect-egg-fall {
  0% { opacity: 0; transform: translateY(-28px) rotate(0deg); }
  12% { opacity: 1; }
  86% { opacity: 1; }
  100% { opacity: 0; transform: translateY(calc(var(--rain-distance, 540px) + 80px)) rotate(var(--egg-tilt)); }
}
@keyframes effect-label-float {
  0% { opacity: 0; transform: translateY(18px) rotate(-7deg); }
  18% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-45px) rotate(-3deg); }
}
@keyframes effect-float-creature {
  0% { transform: translateY(0) rotate(0deg); }
  16% { transform: translateY(calc(var(--effect-hover-height, 28px) * -.45)) rotate(-1.5deg); }
  42% { transform: translateY(calc(var(--effect-hover-height, 28px) * -1)) rotate(1.2deg); }
  70% { transform: translateY(calc(var(--effect-hover-height, 28px) * -.68)) rotate(-.8deg); }
  100% { transform: translateY(0) rotate(0deg); }
}
@keyframes effect-gravity-creature {
  0% { transform: translateY(0) scale(1, 1); }
  14% { transform: translateY(calc(var(--effect-fall-distance, 80px) * .45)) scale(1.02, .97); }
  31% { transform: translateY(var(--effect-fall-distance, 80px)) scale(1.14, .84); }
  43% { transform: translateY(calc(var(--effect-fall-distance, 80px) * .88)) scale(1.04, .95); }
  58% { transform: translateY(var(--effect-fall-distance, 80px)) scale(1.1, .88); }
  74% { transform: translateY(calc(var(--effect-fall-distance, 80px) * .94)) scale(1.04, .95); }
  100% { transform: translateY(0) scale(1, 1); }
}
@keyframes effect-bounce-creature {
  0% { transform: translateY(0) scale(1, 1); }
  14% { transform: translateY(4px) scale(1.14, .82); }
  32% { transform: translateY(calc(var(--effect-bounce-height, 34px) * -.55)) scale(.96, 1.06); }
  52% { transform: translateY(calc(var(--effect-bounce-height, 34px) * -1)) scale(.92, 1.1); }
  72% { transform: translateY(0) scale(1.08, .9); }
  86% { transform: translateY(-5px) scale(.98, 1.02); }
  100% { transform: translateY(0) scale(1, 1); }
}
@keyframes effect-play-area-spin {
  0% { transform: rotate(0deg) scale(1); }
  32% { transform: rotate(118deg) scale(1.015); }
  68% { transform: rotate(276deg) scale(1.01); }
  100% { transform: rotate(360deg) scale(1); }
}
@keyframes effect-bubble {
  0% { opacity: 0; transform: translateY(36px) scale(.7); }
  18% { opacity: .95; }
  100% { opacity: 0; transform: translate(var(--bubble-drift, 5px), -96px) scale(1.1); }
}
@keyframes effect-clone {
  0% { opacity: 0; transform: translate(-50%, 22px) rotate(var(--clone-tilt)); }
  18% { opacity: 1; }
  72% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -15px) rotate(calc(var(--clone-tilt) * -1)); }
}
@keyframes effect-chorus {
  0% { opacity: 0; transform: translate(-50%, 12px) scale(.76) rotate(-5deg); }
  18% { opacity: 1; transform: translate(-50%, 0) scale(1.04) rotate(2deg); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -22px) scale(.92) rotate(4deg); }
}
@keyframes effect-caption {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.8) rotate(-10deg); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.08) rotate(18deg); }
}
@keyframes effect-spin {
  0% { opacity: 0; transform: translate(-50%, -50%) rotate(0deg) scale(.82); }
  18% { opacity: .8; }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(360deg) scale(1.08); }
}
@keyframes effect-giant {
  0% { transform: scale(.86); }
  18% { transform: scale(2.6); }
  65% { transform: scale(2.18); }
  100% { transform: scale(1); }
}
@keyframes effect-gravity-caption {
  0%, 100% { opacity: 0; translate: 0 8px; }
  18%, 74% { opacity: 1; translate: 0 0; }
  86% { opacity: .65; translate: 0 -4px; }
}
@keyframes effect-gravity-mark {
  0% { opacity: 0; translate: 0 -16px; }
  28%, 70% { opacity: 1; translate: 0 18px; }
  100% { opacity: 0; translate: 0 32px; }
}
@keyframes effect-bounce-ring {
  0%, 14% { opacity: .75; scale: .45 1; }
  52% { opacity: .22; scale: 1.12 .7; }
  100% { opacity: 0; scale: 1.38 .35; }
}
@keyframes effect-bounce-caption {
  0%, 100% { opacity: 0; translate: 0 8px; scale: .8; }
  18%, 72% { opacity: 1; translate: 0 0; scale: 1; }
  88% { opacity: .35; translate: 0 -7px; scale: .92; }
}
@keyframes effect-bounce-dot {
  0%, 100% { opacity: 0; translate: 0 4px; }
  25% { opacity: .85; translate: 0 -12px; }
  62% { opacity: .35; translate: 0 -22px; }
}

.browser-shell[data-effect-reaction="gravity"] #floor-shadow {
  transform-box: fill-box;
  transform-origin: center;
  animation: effect-gravity-shadow 1.55s ease-in-out both;
}
.browser-shell[data-effect-reaction="bounce"] #floor-shadow {
  transform-box: fill-box;
  transform-origin: center;
  animation: effect-bounce-shadow 1.8s ease-in-out infinite;
}
@keyframes effect-gravity-shadow {
  0%, 100% { scale: 1 1; opacity: .15; }
  32% { scale: 1.3 .7; opacity: .3; }
  58% { scale: 1.18 .82; opacity: .25; }
}
@keyframes effect-bounce-shadow {
  0%, 100% { scale: 1 1; opacity: .2; }
  30% { scale: 1.3 .72; opacity: .34; }
  55% { scale: .62 1.08; opacity: .1; }
  78% { scale: 1.18 .82; opacity: .28; }
}

@media (prefers-reduced-motion: reduce) {
  .effects-layer .effect,
  .effects-layer .effect *,
  .browser-shell #blob.effects-float,
  .browser-shell #blob.effects-gravity,
  .browser-shell #blob.effects-bounce,
  .browser-shell #creature.effects-spin,
  .browser-shell #face-size.effects-giant,
  .browser-shell[data-effect-reaction] #skin,
  .browser-shell[data-effect-reaction] #face-vibe,
  .browser-shell[data-effect-reaction] #eye-left,
  .browser-shell[data-effect-reaction] #eye-right,
  .browser-shell[data-effect-reaction] #mouth,
  .browser-shell[data-effect-reaction] #floor-shadow {
    animation: none !important;
    transition: none !important;
  }
  .browser-shell #blob.effects-float { transform: translateY(calc(var(--effect-hover-height, 28px) * -1)); }
  .browser-shell #blob.effects-gravity { transform: translateY(var(--effect-fall-distance, 80px)) scale(1.08, .9); }
  .browser-shell #blob.effects-bounce { transform: scale(1.06, .92); }
  .browser-shell #creature.effects-spin { transform: rotate(0deg); }
  .browser-shell #face-size.effects-giant { transform: scale(1.28); }
  .effects-layer .rain-egg { top:30%; }
  .browser-shell[data-effect-reaction="sneeze"] #mouth { scale: 1.3 .72; }
  .browser-shell[data-effect-reaction="rain"] #mouth { scale: .86 1.24; }
  .browser-shell[data-effect-reaction="float"] #mouth { scale: .78 1.4; }
  .browser-shell[data-effect-reaction="clones"] #mouth { scale: 1.22 .82; }
  .browser-shell[data-effect-reaction="chorus"] #mouth { scale: 1.35 1.12; }
  .browser-shell[data-effect-reaction="spin"] #mouth { scale: 1.18 .78; }
  .browser-shell[data-effect-reaction="giant"] #mouth { scale: 1.65 1.35; }
  .browser-shell[data-effect-reaction="chaos"] #mouth { scale: 1.16 .86; }
  .browser-shell[data-effect-reaction="gravity"] #mouth { scale: .82 1.12; }
  .browser-shell[data-effect-reaction="bounce"] #mouth { scale: 1.32 .74; }
}
