/* ============================================================
   background.css
   Three fixed layers under the page: particle canvas, monochrome
   bloom gradient, film grain. All pointer-transparent except the
   canvas, which reads pointer position from window level anyway.
   ============================================================ */

.field{
  position:fixed;
  inset:0;
  z-index:var(--z-field);
  display:block;
  width:100%;
  height:100%;
}

/* Black and white gradient. The white blooms stay under 15% alpha so
   every text colour keeps AA contrast wherever the bloom drifts. */
.bloom{
  position:fixed;
  inset:-25%;
  z-index:var(--z-bloom);
  pointer-events:none;
  background:
    radial-gradient(46% 40% at 22% 18%, rgba(255,255,255,.14), transparent 62%),
    radial-gradient(40% 44% at 82% 78%, rgba(255,255,255,.10), transparent 64%),
    linear-gradient(148deg,#131417 0%,#0a0b0d 46%,#08090a 100%);
  will-change:transform;
}

@media (prefers-reduced-motion: no-preference){
  .bloom{ animation:drift 34s var(--ease) infinite alternate }
}

@keyframes drift{
  from{ transform:translate3d(-2%,-1%,0) scale(1) }
  to  { transform:translate3d(3%,2%,0) scale(1.08) }
}

/* Grain lives on a fixed, non-scrolling, pointer-events-none layer.
   Never attach it to a scrolling container. */
.grain{
  position:fixed;
  inset:0;
  z-index:var(--z-grain);
  pointer-events:none;
  opacity:.24;
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
