/* Cursor sparkle trail. Only loaded when `sparkles.enabled` is true in
   site.yml — see static/js/sparkle.js. */

.sparkle-layer {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;   /* never intercept a click */
  z-index: 9999;
}

.sparkle {
  position: absolute;
  pointer-events: none;
  user-select: none;
  line-height: 1;
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.8);  /* keeps it legible over the tinted hero */
  will-change: transform, opacity;
  animation: sparkle-fall 950ms linear forwards;  /* must match LIFETIME in sparkle.js */
}

/* Drift down and sideways while shrinking and spinning out — the original
   effect fell straight down, but a little lateral drift reads as whimsical
   rather than as a loading indicator. */
@keyframes sparkle-fall {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1) rotate(0deg);
  }
  70% {
    opacity: 0.85;
  }
  100% {
    opacity: 0;
    transform:
      translate(calc(-50% + var(--drift, 0px)), calc(-50% + 38px))
      scale(0.25)
      rotate(var(--spin, 0deg));
  }
}

/* Belt and braces — sparkle.js already bails out on this, but if the script is
   ever loaded some other way the layer stays hidden.

   `.is-forced` is set when the visitor has explicitly opted in (sparkles.on()
   or ?sparkles=on). Without that exemption this rule would silently undo the
   override for exactly the people who asked for it. */
@media (prefers-reduced-motion: reduce) {
  .sparkle-layer:not(.is-forced) { display: none; }
}

/* Nothing on paper. */
@media print {
  .sparkle-layer { display: none; }
}
