/* ============================================================
   Grainfield — a soft blurred colour field under film grain.

   Drop the class on any element that should carry a textured
   gradient background. Colours and shape are passed in as custom
   properties, so one component serves every palette:

     <div class="grainfield"
          style="--gf-base:#0b0b0f;
                 --gf-1:rgba(97,95,255,.55);
                 --gf-2:rgba(143,92,255,.35);
                 --gf-3:rgba(43,43,255,.30);">

   INPUTS (all optional, sensible defaults below)
     --gf-base        base colour behind everything
     --gf-1..5        blob colours (use rgba to control weight). 4 and 5 are
                      transparent by default, so three-colour usage is
                      unchanged; add them when you want an organic, less
                      obviously-circular field.
     --gf-at-1..5     blob positions, e.g. "26% 12%"
     --gf-size-1..5   blob radii, e.g. "46% 52%". Uneven radii read as
                      organic; matching ones read as a circle.
     --gf-blur        how soft the field is            (default 90px)
     --gf-sat         saturation of the field          (default 115%)
     --gf-strength    overall field opacity            (default 1)
     --gf-grain       grain opacity                    (default .18)
     --gf-grain-size  grain tile size                  (default 200px)
     --gf-bleed       how far the field oversizes its
                      box so blur never shows an edge  (default 25%)

   The grain is generated by feTurbulence in a data URI: no image
   asset, no network request, and it stays sharp at any density.
   ============================================================ */

.grainfield {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background-color: var(--gf-base, #0c0c10);

    --gf-grain-img: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* the colour field */
.grainfield::before {
    content: '';
    position: absolute;
    inset: calc(-1 * var(--gf-bleed, 25%));
    z-index: -2;
    background-image:
        radial-gradient(var(--gf-size-1, 46% 52%) at var(--gf-at-1, 26% 10%),
            var(--gf-1, rgba(97, 95, 255, 0.55)) 0%, transparent 66%),
        radial-gradient(var(--gf-size-2, 52% 46%) at var(--gf-at-2, 80% 4%),
            var(--gf-2, rgba(143, 92, 255, 0.34)) 0%, transparent 68%),
        radial-gradient(var(--gf-size-3, 62% 56%) at var(--gf-at-3, 54% 96%),
            var(--gf-3, rgba(43, 43, 255, 0.28)) 0%, transparent 70%),
        radial-gradient(var(--gf-size-4, 40% 34%) at var(--gf-at-4, 36% 42%),
            var(--gf-4, transparent) 0%, transparent 68%),
        radial-gradient(var(--gf-size-5, 34% 46%) at var(--gf-at-5, 70% 60%),
            var(--gf-5, transparent) 0%, transparent 68%);
    filter: blur(var(--gf-blur, 90px)) saturate(var(--gf-sat, 115%));
    opacity: var(--gf-strength, 1);
}

/* the grain */
.grainfield::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: var(--gf-grain-img);
    background-size: var(--gf-grain-size, 200px) var(--gf-grain-size, 200px);
    opacity: var(--gf-grain, 0.18);
}

/* Coarser, more photographic grain — closer to pushed film. */
.grainfield--coarse {
    --gf-grain-img: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.45' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23g)'/%3E%3C/svg%3E");
    --gf-grain-size: 260px;
}

/* Softer field, for large areas where the blobs should barely register. */
.grainfield--wide { --gf-blur: 140px; --gf-bleed: 35%; }

/* A hairline that fades out at both ends, for panel edges. */
.grainfield--edged::after { box-shadow: inset 0 1px 0 var(--gf-edge, rgba(255, 255, 255, 0.09)); }

@media (prefers-reduced-transparency: reduce) {
    .grainfield::after { opacity: calc(var(--gf-grain, 0.18) * 0.5); }
}
