/* Grain shimmer. Measured from melius.com (every dark section), rewritten; the noise tile is ours (SVG feTurbulence).
   A tiled grey noise layer laid over a section in overlay blend at 30% opacity, whose background-position JUMPS
   instead of sliding: the 3 s cycle has five legs (0 0 -> 96,-64 -> -80,112 -> 128,48 -> -48,-96 -> 0 0) and the
   steps(5, end) timing applies to EACH leg, so every 120 ms the tile hops a fifth of the way along the current leg:
   25 still frames per cycle, like film grain re-rolling, never a visible drift.

   <section class="dark-thing" style="position: relative">     any positioned box
     <div class="grain" aria-hidden="true"></div>              the overlay (fills it, ignores the pointer)
     ...content (give it position: relative so it paints above the grain, or put the grain last)
   </section>

   Optional part.js pauses grains that are off screen (the source does the same). Reduced motion = still grain. */
.grain {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  mix-blend-mode: var(--grain-blend, overlay);
  opacity: var(--grain-opacity, .3);
  /* our tile: fractal noise, baseFrequency .9, 3 octaves, stitched; grey mapped to .43 + .85 x noise, which lands on
     the source tile's statistics (mean ~217/255, spread ~25 levels at 2x) */
  --grain-tile-default: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.85 0 0 0 .43 .85 0 0 0 .43 .85 0 0 0 .43 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
}
.grain::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--grain-tile, var(--grain-tile-default));
  background-size: var(--grain-size, 192px) var(--grain-size, 192px);
  background-repeat: repeat;
  background-position: 0 0;
  /* the steps() here is per keyframe leg (CSS applies the timing function between each pair of stops) */
  animation: grain-shimmer var(--grain-dur, 3s) steps(var(--grain-steps, 5), end) infinite;
  animation-play-state: var(--grain-state, running);
}
.grain.is-paused::before { animation-play-state: paused; }

/* distances scale with --grain-travel (1 = the measured px) */
@keyframes grain-shimmer {
  0%, 100% { background-position: 0 0; }
  20% { background-position: calc(96px * var(--grain-travel, 1)) calc(-64px * var(--grain-travel, 1)); }
  40% { background-position: calc(-80px * var(--grain-travel, 1)) calc(112px * var(--grain-travel, 1)); }
  60% { background-position: calc(128px * var(--grain-travel, 1)) calc(48px * var(--grain-travel, 1)); }
  80% { background-position: calc(-48px * var(--grain-travel, 1)) calc(-96px * var(--grain-travel, 1)); }
}

@media (prefers-reduced-motion: reduce) {
  .grain::before { animation: none; }
}
