/* styles/grain.css — Pass A of the soul package (analog-film veil).
 *
 * A single fixed pseudo-element painting fractal SVG noise over the whole
 * viewport. Sits above the UI stack (z=999) with pointer-events: none so it
 * never intercepts input. The blend mode flips per theme:
 *   light → multiply (warm tooth, darkens noise pixels into the surface)
 *   dark  → overlay  (lifts mid-tones, kills flat-black banding)
 *
 * GPU cost is near-zero: one rasterised SVG tile + a 22s linear background-
 * position animation. No filter, no blur, no compositor-thrashing properties.
 *
 * Tier control via data-grain on <html>:
 *   data-grain="on"     → --grain-alpha: 0.04 (default)
 *   data-grain="subtle" → --grain-alpha: 0.02
 *   data-grain="off"    → display: none (no paint, no animation)
 *
 * Reduced-motion (system pref OR data-reduce-motion="true") freezes the drift
 * to a static texture. The grain itself stays — only the motion stops.
 *
 * Authored 2026-05-08 for the soul package. Brief: .agents/SESSION-DOSSIER-20260508.md §11.
 */

:root {
	--grain-alpha: 0.04;
}

html[data-grain="subtle"] {
	--grain-alpha: 0.02;
}

@keyframes grain-drift {
	0%   { background-position:   0px   0px; }
	25%  { background-position:  12px  -8px; }
	50%  { background-position: -16px  16px; }
	75%  { background-position:   8px -12px; }
	100% { background-position:   0px   0px; }
}

html::after {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 999;
	pointer-events: none;
	opacity: var(--grain-alpha);
	/* Inline SVG fractal noise — same baseFrequency/numOctaves as
	   ports/_shared/delight.css .delight-paper, kept stitched so adjacent
	   tiles align across the drift. 256x256 tile. Pure greyscale (alpha-
	   weighted black) so accent stays single-accent — no colour bleed. */
	background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch' seed='7'/%3E%3CfeColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.6 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
	background-size: 256px 256px;
	background-repeat: repeat;
	mix-blend-mode: multiply;
	animation: grain-drift 22s linear infinite;
	will-change: background-position;
}

/* Dark theme — overlay blend reads as "lifted film grain" instead of
   making the already-dark surface darker. The shell's pre-paint script
   always stamps data-theme; the prefers-color-scheme fallback below covers
   the rare case where the attribute is missing (e.g. SSR, head-not-yet-run). */
html[data-theme="dark"]::after {
	mix-blend-mode: overlay;
}
@media (prefers-color-scheme: dark) {
	html:not([data-theme])::after { mix-blend-mode: overlay; }
}

/* Off tier — fully removed, no animation, no compositor cost. */
html[data-grain="off"]::after {
	display: none;
}

/* Reduced motion — freeze the drift, keep the texture. Honoured both via
   the system media query and the operator-set data-reduce-motion attribute. */
@media (prefers-reduced-motion: reduce) {
	html::after { animation: none; }
}
html[data-reduce-motion="true"]::after {
	animation: none;
}
