.btn-fx-glow {
    box-shadow: 0 0 .5rem var(--bs-primary), 0 0 1.25rem var(--bs-primary);
}

@keyframes btn-pop {
    0% {
        transform: scale(1)
    }

    50% {
        transform: scale(1.06)
    }

    100% {
        transform: scale(1)
    }
}

.btn-fx-pop {
    animation: btn-pop 180ms ease-out;
}

.btn-fx-ripple {
    position: relative;
    overflow: hidden;
}

.btn-fx-ripple .fx-ripple {
    position: absolute;
    border-radius: 50%;
    transform: scale(0);
    width: .5rem;
    height: .5rem;
    opacity: .35;
    background: currentColor;
    pointer-events: none;
    animation: fx-ripple 500ms ease-out;
}

@keyframes fx-ripple {
    to {
        transform: scale(20);
        opacity: 0;
    }
}

.fx-xp {
    position: fixed;
    z-index: 1080;
    font-weight: 700;
    padding: .125rem .375rem;
    border-radius: 999px;
    background: var(--bs-success);
    color: var(--brand-primary);
    pointer-events: none;
    transform: translate(-50%, 0);
    opacity: 0;
    animation: fx-xp 900ms cubic-bezier(.2, .9, .2, 1) forwards;
}

@keyframes fx-xp {
    0% {
        transform: translate(-50%, 6px);
        opacity: .0
    }

    20% {
        opacity: 1
    }

    80% {
        opacity: 1
    }

    100% {
        transform: translate(-50%, -28px);
        opacity: 0
    }
}

/* Confetti */
.fx-confetti {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 1075;
}

.fx-confetti i {
    position: absolute;
    width: .45rem;
    height: .45rem;
    border-radius: 2px;
    opacity: .9;
    /* Sequence: quick launch → slower fall+sway → spin */
    animation:
        fx-launch var(--fx-launch-dur, 180ms) cubic-bezier(.15, .9, .2, 1) forwards,
        fx-fall-sway var(--fx-fall-dur, 2400ms) linear var(--fx-launch-dur, 180ms) forwards,
        fx-spin var(--fx-spin-dur, 2200ms) linear forwards;
    will-change: transform, opacity;
}

/* Short sideways launch burst (relative to the button center) */
@keyframes fx-launch {
    0% {
        transform: translate(0, 0) scale(1);
        opacity: .98;
    }

    100% {
        transform: translate(var(--launch-x, 0px), var(--launch-y, -40px)) scale(0.98);
    }
}

/* lively fall centered around the click */
@keyframes fx-fall-sway {
  /* start at click + launch offsets */
  0%   { transform: translate(var(--launch-x, 0px), var(--launch-y, -40px)) rotate(0deg); opacity:.98; }

  /* random lateral offsets at checkpoints */
  20%  { transform: translate(calc(var(--launch-x, 0px) + var(--amp1, 0px)),  20vh) rotate(120deg); }
  40%  { transform: translate(calc(var(--launch-x, 0px) + var(--amp2, 0px)),  40vh) rotate(240deg); }
  60%  { transform: translate(calc(var(--launch-x, 0px) + var(--amp3, 0px)),  65vh) rotate(420deg); }
  80%  { transform: translate(calc(var(--launch-x, 0px) + var(--amp4, 0px)),  90vh) rotate(600deg); }

  /* finish near the click with small end drift */
  100% { transform: translate(calc(var(--launch-x, 0px) + var(--end-drift, 0px)), 110vh) rotate(720deg); opacity:.95; }
}


/* Separate spin; duration randomized via CSS var */
@keyframes fx-spin {
    from {}

    to {}
}

/* Reduce motion: disable the animation */
@media (prefers-reduced-motion: reduce) {
    .fx-confetti i {
        animation: none;
        opacity: 0;
    }
}

/* Fireworks */
.fx-fireworks {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1076; /* just above confetti */
}

/* Particles ----------------------------------------------------- */
.fx-fireworks i {
  /* vivid color + shared timings via vars */
  --h: 0; /* set from JS */
  --c: hsl(var(--h, 0) 95% 62%);
  position: absolute;
  width: .35rem;
  height: .35rem;
  border-radius: 50%;
  /* glowy pearl instead of flat color */
  background: radial-gradient(circle at 40% 40%, #fff 0%, var(--c) 45%, rgba(255,255,255,0) 75%);
  transform: translate(-50%, -50%);
  opacity: 0;

  /* soft neon bloom */
  filter: drop-shadow(0 0 .25rem var(--c)) drop-shadow(0 0 .6rem var(--c));

  animation:
    fw-pop var(--fw-pop-dur, 80ms) ease-out,
    fw-fly var(--fw-fly-dur, 900ms) cubic-bezier(.15,.9,.2,1) forwards,
    fw-fade var(--fw-fade-dur, 900ms) linear forwards,
    fw-flicker var(--fw-fly-dur, 900ms) linear forwards;
}

/* pop in */
@keyframes fw-pop {
  0%   { opacity: 0; transform: translate(-50%,-50%) scale(.4); }
  100% { opacity: 1; transform: translate(-50%,-50%) scale(1); }
}

/* path: JS sets --dx / --dy */
@keyframes fw-fly {
  to { transform: translate(calc(-50% + var(--dx, 0px)), calc(-50% + var(--dy, 0px))) scale(.98); }
}

/* fade out near the end */
@keyframes fw-fade {
  0%   { opacity: 1; }
  80%  { opacity: .92; }
  100% { opacity: 0; }
}

/* subtle glow flicker for energy */
@keyframes fw-flicker {
  0%, 100% { filter: drop-shadow(0 0 .25rem var(--c)) drop-shadow(0 0 .6rem var(--c)); }
  50%      { filter: drop-shadow(0 0 .35rem var(--c)) drop-shadow(0 0 .9rem var(--c)); }
}

/* Accessibility */
@media (prefers-reduced-motion: reduce) {
  .fx-fireworks i { animation: none !important; opacity: 0 !important; }
}

/* Rockets */
.fx-fireworks .fw-rocket {
  --h: 0; /* set from JS */
  --c: hsl(var(--h, 0) 90% 60%);
  position: absolute;
  width: 2px;
  height: 14px;
  border-radius: 2px;
  background: #fff;
  /* stronger colored glow */
  box-shadow: 0 0 6px #fff, 0 0 14px var(--c), 0 0 26px var(--c);
  transform: translate(-50%, -50%);
  opacity: .95;
  animation:
    rocket-rise var(--r-dur, 950ms) cubic-bezier(.15,.9,.2,1) forwards,
    rocket-fade var(--r-dur, 950ms) linear forwards,
    rocket-flicker var(--r-dur, 950ms) linear forwards;
}

/* soft plume */
.fx-fireworks .fw-rocket::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 6px;
  height: 18px;
  transform: translate(-50%, 0);
  background: radial-gradient(ellipse at top, rgba(255,255,255,.9), rgba(255,255,255,0) 60%);
  filter: blur(2px);
  opacity: .9;
}

/* rocket motion path: JS sets --r-dx / --r-dy */
@keyframes rocket-rise {
  to { transform: translate(calc(-50% + var(--r-dx, 0px)), calc(-50% + var(--r-dy, -160px))); }
}

@keyframes rocket-fade {
  0%   { opacity: .95; }
  85%  { opacity: .95; }
  100% { opacity: 0; }
}

/* gentle glow pulse while rising */
@keyframes rocket-flicker {
  0%, 100% { box-shadow: 0 0 6px #fff, 0 0 14px var(--c), 0 0 26px var(--c); }
  50%      { box-shadow: 0 0 8px #fff, 0 0 20px var(--c), 0 0 36px var(--c); }
}