@keyframes confetti-rise {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg);   opacity: 1; }
  100% { transform: translate3d(var(--sx, 0vw), calc(-1 * var(--h, 60vh)), 0) rotate(540deg); opacity: 1; }
}

@keyframes confetti-drop {
  0%   { transform: translate3d(var(--sx, 0vw), calc(-1 * var(--h, 60vh)), 0) rotate(540deg); opacity: 1; }
  100% { transform: translate3d(calc(var(--sx, 0vw) * 1.15), 0, 0) rotate(1080deg); opacity: 0; }
}
@keyframes confetti-right  { from { transform: translateX(-120vw) rotate(0deg) } to { transform: translateX(110vw)  rotate(720deg) } }
@keyframes confetti-left   { from { transform: translateX(120vw)  rotate(0deg) } to { transform: translateX(-110vw) rotate(-720deg) } }
@keyframes confetti-burst-drop {
  0% {
    transform: translate3d(0, 0, 0) rotate(0deg);
    opacity: 1;
  }
  55% {
    transform: translate3d(var(--sx, 0vw), calc(-1 * var(--h, 60vh)), 0) rotate(540deg);
    opacity: 1;
  }
  100% {
    transform: translate3d(calc(var(--sx, 0vw) * 1.15), 0, 0) rotate(1080deg);
    opacity: 0;
  }
}

.confetti-root {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 50;
}

.confetti {
  position: absolute;
  width: 10px;
  height: 10px;
  background-color: #f0f0f0; /* JS will overwrite */
  animation: confetti-fall var(--dur, 4s) linear var(--delay, 0s) forwards;
  will-change: transform, opacity;
}