/* animations.css — transitions and keyframes */

@media (prefers-reduced-motion: no-preference) {
  .fade-in {
    animation: fade-in 300ms ease forwards;
  }

  .slide-up {
    animation: slide-up 300ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }

  .pop-in {
    animation: pop-in 250ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }

  .shake {
    animation: shake 400ms ease;
  }

  .pulse {
    animation: pulse 2s ease-in-out infinite;
  }

  .skeleton {
    background: linear-gradient(
      90deg,
      var(--bg-2) 25%,
      var(--bg-3) 50%,
      var(--bg-2) 75%
    );
    background-size: 200% 100%;
    animation: skeleton 1.2s ease-in-out infinite;
    border-radius: var(--radius-sm);
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* User-forced reduce-motion (Settings → Reduce motion), independent of the OS
   preference. Mirrors the media query so the toggle works even when the device
   itself asks for motion. */
:root[data-reduce-motion="1"] *,
:root[data-reduce-motion="1"] *::before,
:root[data-reduce-motion="1"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slide-up {
  from { opacity: 0; transform: translateY(1rem); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes pop-in {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

@keyframes skeleton {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(1rem) scale(0.95); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes toast-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(1rem) scale(0.95); }
}

@keyframes modal-backdrop-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes modal-content-in {
  from { opacity: 0; transform: scale(0.96) translateY(1rem); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

/* ------------------------------------------------------------------ *
 *  Interaction flourishes — leave/arrive animations and a success burst.
 *  Every one lives behind the reduced-motion guards above (the global
 *  rules zero out animation-duration), so they degrade to instant.
 * ------------------------------------------------------------------ */

/* "Destroy" — a row or card shrinks, fades and tips away when deleted. */
@keyframes destroy-out {
  0%   { opacity: 1; transform: scale(1) translateX(0); }
  35%  { opacity: 1; transform: scale(1.015); }
  100% { opacity: 0; transform: scale(0.86) translateX(1.5rem); }
}
@media (prefers-reduced-motion: no-preference) {
  .destroy-out {
    animation: destroy-out 340ms cubic-bezier(0.5, 0, 0.75, 0) forwards;
    transform-origin: center;
    pointer-events: none;
  }
  /* Table rows can't take transforms cleanly in every engine; animate their cells. */
  tr.destroy-out > td {
    animation: destroy-out 340ms cubic-bezier(0.5, 0, 0.75, 0) forwards;
  }
}

/* "Arrive" — a freshly added row gets a one-shot accent highlight sweep. */
@keyframes row-flash {
  0%   { background-color: color-mix(in srgb, var(--accent) 30%, transparent); }
  100% { background-color: transparent; }
}
@media (prefers-reduced-motion: no-preference) {
  .row-flash { animation: row-flash 1100ms ease-out; }
  tr.row-flash > td { animation: row-flash 1100ms ease-out; }
}

/* Success bounce for a submit button once its action lands. */
@keyframes success-pop {
  0%   { transform: scale(1); }
  30%  { transform: scale(0.95); }
  55%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: no-preference) {
  .success-pop { animation: success-pop 460ms cubic-bezier(0.16, 1, 0.3, 1); }
}

/* Celebratory sparkle burst (utils.burstAt): a ring of dots that fly out and fade. */
.burst-layer {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  width: 0;
  height: 0;
}
@keyframes burst-dot {
  0%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.4); }
}
.burst-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .burst-dot { animation: burst-dot 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards; }
}
