/* ==========================================================================
   Stackzilla fx overlays (public/fx.js)

   Effect layers are plain DOM elements appended to `.board-wrap` (already
   `position: relative` in style.css), stacked above the board canvas but
   below status overlays like `.fx-fog` / `.fx-shield` (z-index 5/6). Every
   element sets its placement (left/top/width/height/bottom) exactly once —
   only `transform` and `opacity` are ever animated, so effects stay
   GPU-composited and never trigger layout.

   Palette: reuses --accent (cyan), --accent-2 (purple), --ok, --danger only.
   ========================================================================== */

.sz-fx-layer {
  /* Geometry is set inline by makeLayer so the layer tracks the canvas box. */
  position: absolute;
  z-index: 4;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}

/* ---------------------------------------------------------------- line clear */
.sz-row-band {
  position: absolute;
  will-change: transform;
}

.sz-row-flash {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(34, 211, 238, 0.85), rgba(168, 85, 247, 0.85));
  mix-blend-mode: screen;
  opacity: 0;
  animation: szRowFlash 0.42s ease-out both;
}
.sz-row-flash-quad {
  background: linear-gradient(
    90deg,
    rgba(34, 211, 238, 0.95),
    rgba(168, 85, 247, 0.95),
    rgba(34, 211, 238, 0.95)
  );
}
.sz-row-flash.sz-reduced {
  animation: szRowFlashReduced 0.3s ease-out both;
  mix-blend-mode: normal;
}
@keyframes szRowFlash {
  0% { opacity: 0; transform: scaleY(0.4); }
  25% { opacity: 1; transform: scaleY(1.3); }
  60% { opacity: 0.85; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(1); }
}
@keyframes szRowFlashReduced {
  0% { opacity: 0; }
  40% { opacity: 0.9; }
  100% { opacity: 0; }
}

.sz-row-burst {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  animation: szRowBurst 0.5s ease-in both;
}
@keyframes szRowBurst {
  0% { opacity: 0.85; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(0.05); }
}

.sz-particle {
  position: absolute;
  border-radius: 2px;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent);
  will-change: transform, opacity;
}
.sz-particle-quad {
  background: var(--accent-2);
  box-shadow: 0 0 8px var(--accent-2);
}

/* ---------------------------------------------------------------- level up */
.sz-levelup-sweep {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(34, 211, 238, 0.35) 47%,
    rgba(168, 85, 247, 0.5) 50%,
    rgba(34, 211, 238, 0.35) 53%,
    transparent 70%
  );
  transform: translateX(-120%);
  animation: szLevelSweep 0.8s ease-in-out both;
}
.sz-levelup-sweep.sz-reduced {
  transform: none;
  opacity: 0;
  animation: szLevelSweepReduced 0.35s ease-out both;
  background: rgba(34, 211, 238, 0.3);
}
@keyframes szLevelSweep {
  0% { transform: translateX(-120%); opacity: 0; }
  15% { opacity: 1; }
  85% { opacity: 1; }
  100% { transform: translateX(120%); opacity: 0; }
}
@keyframes szLevelSweepReduced {
  0% { opacity: 0; }
  40% { opacity: 0.8; }
  100% { opacity: 0; }
}

.sz-levelup-border {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--accent), 0 0 26px rgba(34, 211, 238, 0.75);
  opacity: 0;
  animation: szLevelBorder 0.85s ease-out both;
}
.sz-levelup-border.sz-reduced { animation: szLevelBorderReduced 0.35s ease-out both; }
@keyframes szLevelBorder {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes szLevelBorderReduced {
  0% { opacity: 0; }
  40% { opacity: 0.8; }
  100% { opacity: 0; }
}

/* ---------------------------------------------------------------- garbage */
.sz-garbage-shove {
  position: absolute;
  background: linear-gradient(0deg, rgba(239, 68, 68, 0.55), rgba(239, 68, 68, 0));
  transform: translateY(100%);
  transform-origin: bottom center;
  animation: szGarbageShove 0.42s cubic-bezier(0.2, 0.8, 0.3, 1) both;
}
.sz-garbage-shove.sz-reduced { animation: szGarbageShoveReduced 0.3s ease-out both; }
@keyframes szGarbageShove {
  0% { transform: translateY(100%); opacity: 0; }
  35% { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(0); opacity: 0; }
}
@keyframes szGarbageShoveReduced {
  0% { opacity: 0; }
  40% { opacity: 0.8; }
  100% { opacity: 0; }
}

.sz-garbage-flash {
  position: absolute;
  inset: 0;
  background: var(--danger);
  opacity: 0;
  animation: szGarbageFlash 0.4s ease-out both;
}
.sz-garbage-flash.sz-reduced { animation: szGarbageFlashReduced 0.3s ease-out both; }
@keyframes szGarbageFlash {
  0% { opacity: 0; }
  15% { opacity: var(--sz-gflash-peak, 0.35); }
  100% { opacity: 0; }
}
@keyframes szGarbageFlashReduced {
  0% { opacity: 0; }
  30% { opacity: calc(var(--sz-gflash-peak, 0.35) * 0.7); }
  100% { opacity: 0; }
}

/* Extra safety net: if any effect ever bypasses the JS reduced-motion
   check, collapse every fx animation to a near-instant single frame. */
@media (prefers-reduced-motion: reduce) {
  .sz-fx-layer * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition: none !important;
  }
}
