/* ═══════════════════════════════════════════════════════════════════════════
   hetoimos — bg.css · the procedural background catalogue (CONTRACT_V2 §5, L7)

   FIFTEEN genuinely distinct fields, each a different TECHNIQUE — off-frame
   light pools · two-period lattice · radial ring interference · a printed dot
   screen · an ordered Bayer dither · moiré beat · thresholded turbulence
   veining · summed caustic ripples · a domain-warped flow field · risograph
   misregistration · conic glazing · mineral inclusions · conic god-rays ·
   percentage strata · and silence.

   ARCHITECTURE. One attribute on <html> (`data-bg`) selects the variant.
   Every variant is CSS keyed on that attribute over four span layers that
   already exist in the markup and are never created, moved or destroyed on a
   switch. That is the entire reason switching is free: no DOM work, no layout,
   one style recalculation. See bg.js for the engine.

   MARKUP CONTRACT — one of these inside every section that carries a field:
     <div class="bg" aria-hidden="true">
       <span class="bg-a"></span>   field      (light pools / base wash)
       <span class="bg-b"></span>   structure  (lattice / screen / veins)
       <span class="bg-c"></span>   second separation — only Ρ riso uses it
       <span class="bg-g"></span>   grain      (shared by all fifteen)
     </div>
   bg.js will create any missing span and will adopt legacy `.plate` markup, so
   a partially-wired page degrades to a quieter field, never to a broken one.

   COST, measured (see docs/AUTOLOOP/research/M1-backgrounds-note.md):
   this file is 15.6 KB raw / 3.7 KB gzip / 3.1 KB Brotli, all fifteen
   variants included. Thirteen of the fifteen do no per-frame work at all.

   CONTRAST. §G at the bottom of this file is not a comment — it is the
   guarantee, executed. Read it before changing any colour here.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ─── 1 · THE FOUR LAYERS ─────────────────────────────────────────────────── */

.bg{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none;
  contain:paint;border-radius:inherit;user-select:none}
/* `contain:paint` + `overflow:hidden` are load-bearing, not tidiness: without
   them the over-sized children grow the scroll area and produce horizontal
   overflow at 360 px. */
.bg>span{position:absolute;inset:-14% -7%;display:block;
  background-repeat:repeat;background-position:50% 50%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:100% 100%;mask-size:100% 100%}
/* the engine owns display so a stale legacy rule (`.plate-grid{display:none}`)
   can never silently switch a variant's structure layer off */
:root[data-bg] .bg>span{display:block}

/* GRACEFUL FLOOR. If a variant's field declaration is dropped whole — an
   engine without `in oklab`, without `conic-gradient()` — this still paints.
   A dropped declaration must cost a page its texture, never its background. */
.bg-a{background:linear-gradient(180deg,#F1EFE9 0,#F4F2ED 100%)}
.bg-b,.bg-c{background:none}
.bg-b{opacity:var(--bg-k,1)}          /* per-section structure intensity */

/* grain · 288 B · shared by all fifteen · takes the shape of whatever it sits
   on. Kept on phones: a screen held 30 cm from the eye is exactly where paper
   tooth is visible. */
.bg-g{border-radius:inherit;opacity:var(--grain-a,.045);mix-blend-mode:soft-light;
  background-size:180px 180px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E")}

/* optional convenience for the sections that already carry these classes.
   Harmless if the class names disappear from the markup. */
/* M5-15 — «thirteen of fifteen do no per-frame work» describes each layer's own
   animation, not the compositing consequence: `.bg-g` carries mix-blend-mode in
   all fifteen variants and sits directly above `.bg-a`/`.bg-b`, which are
   transform-animated on a view() timeline in all nine sections — a blended layer
   whose backdrop moves must be re-composited every frame. The raster benchmark
   that was supposed to answer this froze the renderer twice and was abandoned, so
   the phone cost is UNMEASURED. Until it is measured, the blend is dropped where
   the grain is least visible and the GPU is weakest; the grain itself stays,
   which is what the paper tooth argument actually asks for. */
@media (max-width:719px){ .bg-g{mix-blend-mode:normal;opacity:calc(var(--grain-a,.045) * .8)} }

.pl-soft,.pl-quiet{--bg-k:.6}
.pl-none{--bg-k:0}


/* ─── 2 · DERIVED PAGE TOKENS — the «чуть-чуть стиль сайта» ───────────────── */
/* Declared BEFORE the variants so a variant's own token block always wins the
   tie. These read `--accent`, which each variant sets, so hairlines, the
   secondary surface and the selection colour drift with the field without one
   component knowing that backgrounds are switchable.
   `--paper` itself is deliberately NOT tinted: the whole contrast budget in §G
   is computed against it, and a moving floor is not a guarantee. */
:root[data-bg]{
  --grain-a:.045;
  --hair:#E3E0D8; --hair-strong:#D4D0C5; --paper-2:#EFECE5;
}
@supports (color:color-mix(in oklab,red,blue)){
  :root[data-bg]{
    --hair:color-mix(in oklab,var(--accent) 8%,#E3E0D8);
    --hair-strong:color-mix(in oklab,var(--accent) 13%,#D4D0C5);
    --paper-2:color-mix(in oklab,var(--accent) 4%,#EFECE5);
  }
}
:root[data-bg] ::selection{background:var(--accent);color:#F4F2ED}


/* ─── 3 · THE FIFTEEN ─────────────────────────────────────────────────────── */
/* Cycle order (bg.js owns it): Π Ω Γ Θ Σ Ζ Ξ Λ Δ Ψ Ρ Μ Φ Χ Ο — quiet to loud
   in the first six, the costed ones in the middle, the personality variants
   late, silence last.
   `--bg-lmin` on each is the variant's own WORST composite luminance: field →
   structure hairline alpha-composited → grain at its darkest sample. It is the
   single number §G3 gates on, and it lives next to the colours that produce it
   so it cannot drift away from them silently. */


/* Ⅰ · Π «Плита» — three off-frame light pools, three frequency bands ─────── */
:root[data-bg=pi]{--accent:#1C4E4A;--accent-hi:#123734;--accent-dk:#93C7BE;
  --bg-t1:#D9EAE4;--bg-t2:#F2E6D2;--bg-lmin:.707}
[data-bg=pi] .bg-a{background:
  radial-gradient(125% 95%  at   4% -18% in oklab,#D9EAE4 0,transparent 68%),
  radial-gradient(105% 85%  at 108%   2% in oklab,#F2E6D2 0,transparent 64%),
  radial-gradient(150% 115% at  58% 124% in oklab,#E2E9EE 0,transparent 70%)}
[data-bg=pi] .bg-b{--m:radial-gradient(82% 72% at 84% 12%,#000,transparent 70%);
  background-image:
  repeating-linear-gradient(90deg,rgb(20 23 28/.048) 0 1px,transparent 1px 38px),
  repeating-linear-gradient( 0deg,rgb(20 23 28/.032) 0 1px,transparent 1px 38px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
/* Phone. Two separate failures, two separate fixes: the same pool percentages
   cover a five-times-narrower box, so the warm pool would land on the
   headline; and a 38 px lattice at 390 px is ten lines across and reads as
   corduroy. The lattice is WIDENED, not deleted — deleting it would make Π and
   Γ the same variant on the majority device. */
@media (max-width:719px){
  [data-bg=pi] .bg-a{background:
    radial-gradient(150% 70% at  -8% -12% in oklab,#DEECE6 0,transparent 66%),
    radial-gradient(120% 55% at 118%  -6% in oklab,#F4EADA 0,transparent 58%),
    radial-gradient(170% 80% at  50% 122% in oklab,#E4EAEF 0,transparent 70%)}
  [data-bg=pi] .bg-b{background-image:
    repeating-linear-gradient(90deg,rgb(20 23 28/.040) 0 1px,transparent 1px 66px),
    repeating-linear-gradient( 0deg,rgb(20 23 28/.028) 0 1px,transparent 1px 66px)}
}

/* Ⅱ · Ω «Ізолінії» — two ring families, different origins and periods ────── */
/* The only structural variant that survives 390 px unaltered: a radial period
   has no lines-per-viewport count to go wrong against the viewport edge.
   Orthogonal structure dies on phones; radial structure does not. */
:root[data-bg=omega]{--accent:#3D4A2E;--accent-hi:#2A3420;--accent-dk:#BFCBA8;
  --bg-t1:#E4EAE0;--bg-t2:#EEEFE6;--bg-lmin:.732}
[data-bg=omega] .bg-a{background:
  radial-gradient(140% 110% at 22% -20% in oklab,#E4EAE0 0,transparent 70%),
  radial-gradient(120% 100% at 96% 110% in oklab,#EEEFE6 0,transparent 66%)}
[data-bg=omega] .bg-b{--m:radial-gradient(96% 84% at 78% 14%,#000,transparent 74%);
  background-image:
  repeating-radial-gradient(66% 92%  at 18%  8%,transparent 0 26px,rgb(20 23 28/.042) 26px 27px),
  repeating-radial-gradient(78% 104% at 88% 96%,transparent 0 34px,rgb(20 23 28/.030) 34px 35px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}

/* Ⅲ · Γ «Креслення» — two lattice periods. One grid is a background; two
   grids is a drawing. ───────────────────────────────────────────────────── */
:root[data-bg=gamma]{--accent:#2F4858;--accent-hi:#1E313D;--accent-dk:#A8C4D4;
  --bg-t1:#E8EDF0;--bg-t2:#DFE6EB;--bg-lmin:.741;--grain-a:.038}
[data-bg=gamma] .bg-a{background:
  radial-gradient(150% 120% at 50% -30% in oklab,#E8EDF0 0,transparent 72%)}
[data-bg=gamma] .bg-b{--m:linear-gradient(180deg,#000,rgb(0 0 0/.30) 55%,transparent);
  background-image:
  repeating-linear-gradient(90deg,rgb(20 23 28/.055) 0 1px,transparent 1px 96px),
  repeating-linear-gradient( 0deg,rgb(20 23 28/.055) 0 1px,transparent 1px 96px),
  repeating-linear-gradient(90deg,rgb(20 23 28/.022) 0 1px,transparent 1px 16px),
  repeating-linear-gradient( 0deg,rgb(20 23 28/.022) 0 1px,transparent 1px 16px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
/* a 1 px line in a repeating gradient lands on a fractional device pixel at
   DPR 1.75 and Chrome antialiases it, which shimmers during scroll. Periods
   stay in whole px, alpha stays ≤ .055, and the minor grid widens on phones
   where 16 px is 24 lines across and reads as fabric. */
@media (max-width:719px){
  [data-bg=gamma] .bg-b{background-image:
    repeating-linear-gradient(90deg,rgb(20 23 28/.050) 0 1px,transparent 1px 112px),
    repeating-linear-gradient( 0deg,rgb(20 23 28/.050) 0 1px,transparent 1px 112px),
    repeating-linear-gradient(90deg,rgb(20 23 28/.016) 0 1px,transparent 1px 28px),
    repeating-linear-gradient( 0deg,rgb(20 23 28/.016) 0 1px,transparent 1px 28px)}
}

/* Ⅳ · Θ «Растр» — a printed dot screen with a density ramp ───────────────── */
/* `space` forces an integer number of complete dots across the box, so no dot
   is ever clipped at an edge. Two masks composited with `intersect` shape the
   density, so the screen fades out rather than stopping. The famous
   `filter:contrast(16)` halftone is rejected: a filter on a full-viewport
   element forces an offscreen render pass on every invalidation. */
:root[data-bg=theta]{--accent:#5B3F1E;--accent-hi:#412C13;--accent-dk:#D8BE93;
  --bg-t1:#EDE6D8;--bg-t2:#E4DAC7;--bg-lmin:.650;--grain-a:.033}
[data-bg=theta] .bg-a{background:
  radial-gradient(150% 115% at 88% -22% in oklab,#EDE6D8 0,transparent 70%)}
[data-bg=theta] .bg-b{
  --m:radial-gradient(72% 66% at 86% 10%,#000,transparent 76%),
      linear-gradient(115deg,#000,rgb(0 0 0/.15) 62%,transparent);
  background:radial-gradient(closest-side,rgb(20 23 28/.30) 0 62%,transparent 63%) 0 0/9px 9px space;
  -webkit-mask-image:var(--m);mask-image:var(--m);
  -webkit-mask-composite:source-in;mask-composite:intersect}
/* if `mask-composite` is unsupported the masks stack additively: the field is
   LARGER, not broken — the right direction to fail. */
@media (max-width:719px){
  [data-bg=theta] .bg-b{background-size:12px 12px}
}

/* Ⅴ · Σ «Скло» — conic facets under a mullion lattice ───────────────────── */
/* The doubled line — a 1 px white highlight offset one pixel from each dark
   line — is what makes a grid read as a physical bar rather than graph paper.
   The conic origin is OUTSIDE the box: a conic gradient whose centre is on
   screen shows you its vertex, and a visible vertex is a visible shape. */
:root[data-bg=sigma]{--accent:#2A4A63;--accent-hi:#1B3345;--accent-dk:#A2C2DA;
  --bg-t1:#E2E8EE;--bg-t2:#EDF1F5;--bg-lmin:.699;--grain-a:.038}
[data-bg=sigma] .bg-a{background:linear-gradient(160deg,#EDF1F5,#E2E8EE)}
@supports (background:conic-gradient(in oklab,red,blue)){
  [data-bg=sigma] .bg-a{background:
    conic-gradient(from 210deg at 30% -10% in oklab,
      #E2E8EE,#EDF1F5 25%,#E2E8EE 52%,#EDF1F5 78%,#E2E8EE)}
}
[data-bg=sigma] .bg-b{--m:linear-gradient(190deg,#000,rgb(0 0 0/.25) 60%,transparent);
  background-image:
  repeating-linear-gradient(90deg,rgb(20 23 28/.060) 0 1px,transparent 1px 132px),
  repeating-linear-gradient( 0deg,rgb(20 23 28/.045) 0 1px,transparent 1px 132px),
  repeating-linear-gradient(90deg,transparent 0 1px,rgb(255 255 255/.55) 1px 2px,transparent 2px 132px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
/* survives 360 px unaltered — a 132 px cell is under three cells across, which
   reads as architecture. The corduroy failure is a function of
   lines-per-viewport, not of lattices as such. */

/* Ⅵ · Ζ «Шари» — geological banding at 179.4°, irregular by design ──────── */
/* Even spacing is a barcode; uneven spacing is sediment. The bands are in
   PERCENTAGES, so they scale with the section instead of tiling — the only
   structural variant that is automatically correct at 360 px. */
:root[data-bg=zeta]{--accent:#5A4326;--accent-hi:#3F2E17;--accent-dk:#D9C39C;
  --bg-t1:#F2EDE3;--bg-t2:#ECE5D9;--bg-lmin:.723;--grain-a:.042}
[data-bg=zeta] .bg-a{background:linear-gradient(180deg,#F2EDE3 0,#ECE5D9 46%,#F2EDE3 100%)}
@supports (background:linear-gradient(in oklab,red,blue)){
  [data-bg=zeta] .bg-a{background:
    linear-gradient(180deg in oklab,#F2EDE3 0,#ECE5D9 46%,#F2EDE3 100%)}
}
[data-bg=zeta] .bg-b{--m:linear-gradient(95deg,#000,rgb(0 0 0/.30) 58%,transparent);
  background-image:linear-gradient(179.4deg,
    transparent 0 6%,      rgb(20 23 28/.036) 6% 6.4%,     transparent 6.4% 17%,
    rgb(20 23 28/.022) 17% 17.3%,                          transparent 17.3% 31%,
    rgb(20 23 28/.030) 31% 31.6%,                          transparent 31.6% 49%,
    rgb(20 23 28/.018) 49% 49.2%,                          transparent 49.2% 68%,
    rgb(20 23 28/.028) 68% 68.5%,                          transparent 68.5% 100%);
  -webkit-mask-image:var(--m);mask-image:var(--m)}

/* Ⅶ · Ξ «Теразо» — sparse mineral inclusions IN the paper, not on it ────── */
:root[data-bg=xi]{--accent:#514434;--accent-hi:#392F23;--accent-dk:#D6C7AE;
  --bg-t1:#EFEBE2;--bg-t2:#F4F1EA;--bg-lmin:.817;--grain-a:.050}
[data-bg=xi] .bg-a{background:linear-gradient(170deg,#EFEBE2,#F4F1EA)}
@supports (background:linear-gradient(in oklab,red,blue)){
  [data-bg=xi] .bg-a{background:linear-gradient(170deg in oklab,#EFEBE2,#F4F1EA)}
}
[data-bg=xi] .bg-b{mix-blend-mode:multiply;opacity:calc(var(--bg-k,1) * .5);
  background-size:820px 820px;
  background-image:var(--bg-img,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='820' height='820'%3E%3Cfilter id='t'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.09' numOctaves='1' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='table' tableValues='1 1 1 0.55'/%3E%3CfeFuncG type='table' tableValues='1 1 1 0.52'/%3E%3CfeFuncB type='table' tableValues='1 1 1 0.46'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='820' height='820' filter='url(%23t)'/%3E%3C/svg%3E"))}
@media (max-width:719px){ [data-bg=xi] .bg-b{background-size:560px 560px} }

/* Ⅷ · Λ «Каустика» — three summed ripple families, then softened ────────── */
/* Where three ripple families overlap you get the bright filaments a real
   caustic has. The closest thing here to a shader, written without one.
   COST, stated: `filter:blur()` allocates a compositor layer expanded by ~3σ.
   At 390×844 DPR 3 that is ≈4.4 Mpx ≈ 17.6 MB of GPU texture — real, but
   rasterised ONCE, not per frame. This is the ceiling: no variant uses two
   blurs, none blurs above 6 px, and no blurred layer is ever animated. */
:root[data-bg=lambda]{--accent:#1D4A52;--accent-hi:#0F3138;--accent-dk:#96C6CE;
  --bg-t1:#DEEAE9;--bg-t2:#EAF2F1;--bg-lmin:.796}
[data-bg=lambda] .bg-a{background:
  radial-gradient(150% 118% at 62% -24% in oklab,#DEEAE9 0,transparent 72%)}
[data-bg=lambda] .bg-b{--m:radial-gradient(120% 90% at 50% 0,#000,transparent 78%);
  background-image:
  repeating-radial-gradient(50% 40% at 12% -6%,rgb(255 255 255/.55) 0 18px,transparent 18px 46px),
  repeating-radial-gradient(46% 44% at 92%  8%,rgb(255 255 255/.45) 0 21px,transparent 21px 53px),
  repeating-radial-gradient(60% 38% at 48% 112%,rgb(255 255 255/.40) 0 25px,transparent 25px 61px);
  filter:blur(6px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
@media (max-width:719px){ [data-bg=lambda] .bg-b{filter:blur(4px)} }

/* Ⅸ · Δ «Дифузія» — 1-bit ordered dithering, no shader ──────────────────── */
/* An 8×8 Bayer threshold matrix as a tiled mask, intersected with a soft ramp,
   over a flat ink wash: a genuine ordered dither, a gradient rendered in
   discrete dots rather than smooth tone. `image-rendering:pixelated` and a
   whole-pixel mask-size are both mandatory or the matrix resamples to mush at
   DPR 2.75. A strong retro commitment — in the set, never the default. */
:root[data-bg=delta]{--accent:#243B52;--accent-hi:#16283A;--accent-dk:#9DBBD4;
  --bg-t1:#E1E7EC;--bg-t2:#E9EDF1;--bg-lmin:.677;--grain-a:.030}
[data-bg=delta] .bg-a{background:linear-gradient(160deg,#E1E7EC 0,#E9EDF1 55%,transparent 100%)}
@supports (background:linear-gradient(in oklab,red,blue)){
  [data-bg=delta] .bg-a{background:
    linear-gradient(160deg in oklab,#E1E7EC 0,#E9EDF1 55%,transparent 100%)}
}
[data-bg=delta] .bg-b{
  --m:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' shape-rendering='crispEdges'%3E%3Cg fill='%23fff'%3E%3Crect x='0' y='0' width='1' height='1' opacity='.02'/%3E%3Crect x='4' y='0' width='1' height='1' opacity='.53'/%3E%3Crect x='2' y='0' width='1' height='1' opacity='.16'/%3E%3Crect x='6' y='0' width='1' height='1' opacity='.66'/%3E%3Crect x='1' y='1' width='1' height='1' opacity='.78'/%3E%3Crect x='5' y='1' width='1' height='1' opacity='.28'/%3E%3Crect x='3' y='1' width='1' height='1' opacity='.91'/%3E%3Crect x='7' y='1' width='1' height='1' opacity='.41'/%3E%3Crect x='0' y='2' width='1' height='1' opacity='.22'/%3E%3Crect x='4' y='2' width='1' height='1' opacity='.72'/%3E%3Crect x='2' y='2' width='1' height='1' opacity='.09'/%3E%3Crect x='6' y='2' width='1' height='1' opacity='.59'/%3E%3Crect x='1' y='3' width='1' height='1' opacity='.97'/%3E%3Crect x='5' y='3' width='1' height='1' opacity='.47'/%3E%3Crect x='3' y='3' width='1' height='1' opacity='.84'/%3E%3Crect x='7' y='3' width='1' height='1' opacity='.34'/%3E%3C/g%3E%3C/svg%3E"),
      linear-gradient(150deg,#000,transparent 70%);
  background:rgb(20 23 28/.075);image-rendering:pixelated;
  -webkit-mask-image:var(--m);mask-image:var(--m);
  -webkit-mask-size:8px 8px,100% 100%;mask-size:8px 8px,100% 100%;
  -webkit-mask-repeat:repeat,no-repeat;mask-repeat:repeat,no-repeat;
  -webkit-mask-composite:source-in;mask-composite:intersect}

/* Ⅹ · Ψ «Муар» — interference between two nearly-identical line fields ──── */
/* Two 7 px fields at 0.0° and 0.7°. The beat exists in NEITHER layer — it is
   emergent, which is why this cannot be faked with a single gradient. Under
   scroll the fields separate by ±3 px and the beat travels, with no timer. */
:root[data-bg=psi]{--accent:#3A4650;--accent-hi:#28313A;--accent-dk:#B2BEC8;
  --bg-t1:#E6E9EC;--bg-t2:#DDE1E5;--bg-lmin:.756;--grain-a:.030}
[data-bg=psi] .bg-a{background:
  radial-gradient(160% 120% at 40% -26% in oklab,#E6E9EC 0,transparent 72%)}
[data-bg=psi] .bg-b{--m:radial-gradient(88% 70% at 82% 16%,#000,transparent 72%);
  background-image:
  repeating-linear-gradient(0.0deg,rgb(20 23 28/.030) 0 1px,transparent 1px 7px),
  repeating-linear-gradient(0.7deg,rgb(20 23 28/.030) 0 1px,transparent 1px 7px);
  background-position:0 0,0 0;
  -webkit-mask-image:var(--m);mask-image:var(--m)}
/* a 7 px period at DPR 1.75 is the worst case for the antialiasing shimmer
   described under Γ. On phones the period widens and the beat stops moving:
   `background-position` is a PAINT property, not a composited one — the one
   place in this file where motion costs a repaint. */
@media (max-width:719px){
  [data-bg=psi] .bg-b{background-image:
    repeating-linear-gradient(0.0deg,rgb(20 23 28/.026) 0 1px,transparent 1px 10px),
    repeating-linear-gradient(0.8deg,rgb(20 23 28/.026) 0 1px,transparent 1px 10px);
    animation:none}
}

/* Ⅺ · Ρ «Ризограф» — two spot separations, deliberately misregistered ───── */
/* The signature of riso is not noise, it is MISREGISTRATION — two spot colours
   printed on separate passes that do not line up.
   THE MISTAKE THIS CODE EXISTS TO CORRECT, recorded because reasoning did not
   catch it and looking did: the first version offset two SOFT radial washes by
   2.5 px. On a 1568 px viewport those washes are ~1 800 px across, so the
   offset was 0.14 % of the feature — arithmetically invisible. It rendered as
   a plain lilac wash: precisely the "ordinary two-colour wash" this variant is
   defined against. Misregistration is only visible where there is an EDGE.
   So both separations are now coarse dot screens — which is also what riso
   actually is — offset by 4 px and 3 px against a 13 px period, i.e. by about
   a third of a period. The fringing is the variant.
   `position:absolute` (not fixed) is the performance decision: a fixed blended
   layer must re-blend against fresh content on every scroll frame, which is
   affordable for an Awwwards site and not for us. The offset lives in
   `background-position`, not in `transform`, so no compositor layer is
   promoted for a static effect and no global `transform:none` in a still-mode
   rule can silently erase the whole variant. */
:root[data-bg=rho]{--accent:#3E2F52;--accent-hi:#2A2039;--accent-dk:#BFAAD6;
  --bg-t1:#EAE5EF;--bg-t2:#F0ECF3;--bg-lmin:.672;--grain-a:.040}
[data-bg=rho] .bg-a{background:
  radial-gradient(120% 92% at 16% -14% in oklab,#EAE5EF 0,transparent 62%)}
[data-bg=rho] .bg-b{--m:radial-gradient(92% 76% at 80% 12%,#000,transparent 74%);
  mix-blend-mode:multiply;
  background:radial-gradient(closest-side,rgb(206 96 74/.12) 0 52%,transparent 54%) 0 0/13px 13px;
  -webkit-mask-image:var(--m);mask-image:var(--m)}
[data-bg=rho] .bg-c{--m:radial-gradient(92% 76% at 80% 12%,#000,transparent 74%);
  mix-blend-mode:multiply;
  background:radial-gradient(closest-side,rgb(58 96 154/.10) 0 52%,transparent 54%) 4px 3px/13px 13px;
  -webkit-mask-image:var(--m);mask-image:var(--m)}

/* Ⅻ · Μ «Мармур» — thresholded turbulence veining ──────────────────────── */
/* `type="turbulence"` (not fractalNoise) at a deliberately ANISOTROPIC base
   frequency — 0.008 across against 0.021 down — stretches the noise into
   directional veins rather than clouds; a step table thresholds it into hard
   edges. This is the only legitimate use of feTurbulence: run ONCE at raster
   time and bake. An feTurbulence animated at runtime is CPU-bound.
   `mask-size:cover` on a single non-repeating mask, NOT a 700 px tile:
   `stitchTiles` cannot hide a repeat when the feature scale (1/0.008 ≈ 125 px)
   is a large fraction of the tile, and the seam was legible. */
:root[data-bg=mu]{--accent:#4A4235;--accent-hi:#332D23;--accent-dk:#CFC4AE;
  --bg-t1:#EBE6DC;--bg-t2:#F1EDE4;--bg-lmin:.780;--grain-a:.050}
[data-bg=mu] .bg-a{background:linear-gradient(155deg,#EBE6DC,#F1EDE4)}
@supports (background:linear-gradient(in oklab,red,blue)){
  [data-bg=mu] .bg-a{background:linear-gradient(155deg in oklab,#EBE6DC,#F1EDE4)}
}
[data-bg=mu] .bg-b{
  --m:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.008 0.021' numOctaves='5' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='table' tableValues='0 0 1 1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='700' height='700' filter='url(%23m)'/%3E%3C/svg%3E");
  background:rgb(20 23 28/.022);
  -webkit-mask-image:var(--m);mask-image:var(--m);
  -webkit-mask-size:cover;mask-size:cover}

/* ⅩⅢ · Φ «Потік» — a domain-warped flow field ──────────────────────────── */
/* A line pattern displaced by fractal noise: the lines become streamlines and
   the field reads as flow, not as cloud. Executed ONCE when the mask image is
   rasterised, then it is a texture — the same discipline as Μ.
   `--bg-img` is the upgrade seam: a build-time bake (AVIF, two crops) drops in
   through that property with no change to this file or to bg.js. Until then
   the SVG below IS the shipped form, not a placeholder. */
/* M5-12 — Φ used to carry Π's EXACT accent triple, so one of the fifteen steps
   moved no tint at all and «чуть-чуть стиль сайта» was absent between them.
   Φ is the flow field; a cooler blue-green separates it from Π's plate. */
:root[data-bg=phi]{--accent:#17495C;--accent-hi:#0E3341;--accent-dk:#8FC4D6;
  --bg-t1:#DCE8E3;--bg-t2:#E9F0EC;--bg-lmin:.776;--grain-a:.040}
[data-bg=phi] .bg-a{background:
  radial-gradient(150% 120% at 30% -22% in oklab,#DCE8E3 0,transparent 70%)}
[data-bg=phi] .bg-b{
  --m:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='900'%3E%3Cdefs%3E%3Cpattern id='p' width='30' height='30' patternUnits='userSpaceOnUse'%3E%3Crect width='30' height='2.6' fill='%23000'/%3E%3C/pattern%3E%3Cfilter id='f' x='-20%25' y='-20%25' width='140%25' height='140%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.0032 0.0055' numOctaves='3' seed='9' result='n'/%3E%3CfeColorMatrix in='n' type='matrix' values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1' result='n2'/%3E%3CfeDisplacementMap in='SourceGraphic' in2='n2' scale='210' xChannelSelector='R' yChannelSelector='G'/%3E%3CfeGaussianBlur stdDeviation='0.7'/%3E%3C/filter%3E%3C/defs%3E%3Crect width='900' height='900' fill='url(%23p)' filter='url(%23f)'/%3E%3C/svg%3E");
  --mm:radial-gradient(130% 105% at 62% 4%,#000,transparent 80%);
  background:rgb(20 23 28/.030);
  -webkit-mask-image:var(--m),var(--mm);mask-image:var(--m),var(--mm);
  -webkit-mask-size:cover,100% 100%;mask-size:cover,100% 100%;
  -webkit-mask-composite:source-in;mask-composite:intersect}
/* dormant upgrade seam: only reachable once bg.js has DECODED a baked asset,
   so first paint is never gated on a network response and a failed fetch is
   invisible — the reader keeps a complete, slightly simpler field forever. */
[data-bg=phi].bg-ready .bg-b{background:var(--bg-img) 50% 0/cover no-repeat;
  -webkit-mask-image:var(--mm);mask-image:var(--mm);
  -webkit-mask-size:100% 100%;mask-size:100% 100%}

/* ⅩⅣ · Χ «Промені» — soft angular god-rays ─────────────────────────────── */
/* Irregular angular spacing is essential: evenly spaced spokes read as a pie
   chart. The first version of this variant did not exist on screen — 2–3°
   wedges, 9 px of blur and a 74 % mask falloff are three independent ways of
   erasing the same signal. These are the corrected numbers: wider wedges,
   higher alpha, less blur, a later falloff. Mechanism was never the problem. */
:root[data-bg=chi]{--accent:#6B4A15;--accent-hi:#4D340B;--accent-dk:#E0C489;
  --bg-t1:#F0E8D6;--bg-t2:#FFFCF0;--bg-lmin:.805;--grain-a:.045}
[data-bg=chi] .bg-a{background:
  radial-gradient(120% 90% at 84% -18% in oklab,#F0E8D6 0,transparent 68%)}
[data-bg=chi] .bg-b{--m:radial-gradient(120% 96% at 84% -10%,#000,transparent 88%);
  background:radial-gradient(90% 70% at 84% -14%,rgb(255 252 240/.55),transparent 70%);
  filter:blur(4px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
@supports (background:conic-gradient(red,blue)){
  [data-bg=chi] .bg-b{background:conic-gradient(from 168deg at 84% -14%,
    transparent 0 6deg,   rgb(255 252 240/.80)  6deg 14deg, transparent 14deg 23deg,
    rgb(255 252 240/.62) 23deg 29deg,                       transparent 29deg 41deg,
    rgb(255 252 240/.72) 41deg 52deg,                       transparent 52deg 360deg)}
}

/* ⅩⅤ · Ο «Порожнеча» — paper and grain, nothing else ───────────────────── */
/* The control. The honest answer for a visitor who finds texture distracting,
   the reference frame the other fourteen are judged against, and the state the
   page falls back to if the engine fails. It keeps L7 intact — the grain is
   still there, so the field is never REMOVED, only reduced to its quietest
   form. The accent collapses to ink and the page goes monochrome: a real
   stylistic position, not an absence of one. */
:root[data-bg=omicron]{--accent:#14171C;--accent-hi:#000;--accent-dk:#EDEAE3;
  --bg-t1:#F4F2ED;--bg-t2:#F4F2ED;--bg-lmin:.876;--grain-a:.055}
[data-bg=omicron] .bg-a,[data-bg=omicron] .bg-b,[data-bg=omicron] .bg-c{background:none}


/* ─── 4 · DARK SECTIONS ───────────────────────────────────────────────────── */
/* G5 inverts here: on dark the constraint is a CEILING, and it is much tighter
   than it looks. Derived, not chosen: the page's `--on-dark-3` at alpha .56
   composites to #8C8C8A (L 0.2617), which needs the field below **L 0.0193**
   to hold 4.5:1. Every dark pool below is therefore capped at alpha .26 — the
   largest value that keeps ALL FIFTEEN accents under L 0.0173, a 10 % margin.
   Overlay grain at .062 multiplies a dark backdrop by ≈1.06, which is inside
   that margin. §G raises `--on-dark-3` to .62 as well, so the real headroom is
   roughly double. Dark sections have the room; push texture there, not onto
   paper. */
/* A hard reset first. Variants set `filter`, `mix-blend-mode`, `margin`,
   `image-rendering`, `mask-composite` and tiled `background-size`; without
   this, a dark section inherits Θ's 9 px tile or Λ's blur and paints
   something nobody designed. `.dark .bg>span` is (0,2,1) and outranks every
   `[data-bg=x] .bg-y` (0,2,0), so this cannot be lost to source order. */
.dark .bg>span{filter:none;mix-blend-mode:normal;image-rendering:auto;margin:0;
  -webkit-mask-composite:add;mask-composite:add;
  -webkit-mask-size:100% 100%;mask-size:100% 100%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}

.dark .bg-a{background:
  radial-gradient(135% 105% at  6% -28%,rgb(28 78 74/.26) 0,transparent 64%),
  radial-gradient(115% 95%  at 98% 114%,rgb(60 72 88/.20) 0,transparent 68%)}
@supports (color:color-mix(in oklab,red,blue)){
  .dark .bg-a{background:
    radial-gradient(135% 105% at  6% -28% in oklab,color-mix(in oklab,var(--accent) 26%,transparent) 0,transparent 64%),
    radial-gradient(115% 95%  at 98% 114% in oklab,rgb(60 72 88/.20) 0,transparent 68%)}
}

/* Three dark structure families, so the dark sections change TECHNIQUE with
   the variant and not merely hue. Fifteen bespoke dark fields would be fifteen
   more chances to breach a ceiling that is already tight; three families keyed
   off the variant is the honest trade. */
.dark .bg-b{--m:linear-gradient(200deg,#000,rgb(0 0 0/.28) 62%,transparent);
  background:none;opacity:var(--bg-k,1);
  background-image:
  repeating-linear-gradient(90deg,rgb(237 234 227/.050) 0 1px,transparent 1px 132px),
  repeating-linear-gradient( 0deg,rgb(237 234 227/.034) 0 1px,transparent 1px 132px);
  -webkit-mask-image:var(--m);mask-image:var(--m)}
/* rings — Ω Λ Θ Φ */
[data-bg=omega] .dark .bg-b,[data-bg=lambda] .dark .bg-b,
[data-bg=theta] .dark .bg-b,[data-bg=phi]    .dark .bg-b{background-image:
  repeating-radial-gradient(70% 96% at 12% 6%,transparent 0 30px,rgb(237 234 227/.042) 30px 31px),
  repeating-radial-gradient(84% 108% at 92% 98%,transparent 0 40px,rgb(237 234 227/.028) 40px 41px)}
/* bands — Ζ Μ Ξ Ρ Χ */
[data-bg=zeta] .dark .bg-b,[data-bg=mu]  .dark .bg-b,[data-bg=xi] .dark .bg-b,
[data-bg=rho]  .dark .bg-b,[data-bg=chi] .dark .bg-b{background-image:linear-gradient(179.5deg,
  transparent 0 9%,   rgb(237 234 227/.040)  9% 9.4%,  transparent 9.4% 24%,
  rgb(237 234 227/.026) 24% 24.3%,                     transparent 24.3% 44%,
  rgb(237 234 227/.034) 44% 44.5%,                     transparent 44.5% 66%,
  rgb(237 234 227/.022) 66% 66.3%,                     transparent 66.3% 100%)}
/* Ο keeps its silence on dark too — L7 is satisfied by the grain alone. */
[data-bg=omicron] .dark .bg-b{background-image:none}
.dark .bg-c{background:none}
.dark .bg-g{opacity:.062;mix-blend-mode:overlay}


/* ─── 5 · MOTION — caused by the page moving, never by a clock ────────────── */
/* transform only ⇒ composited ⇒ no main thread, no requestAnimationFrame, no
   JavaScript, and it stops when the reader stops. Browsers without
   scroll-driven timelines get the AUTHORED state, which is the complete
   static field: nothing ever waits on this. */
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){
    .bg-a,.bg-b{animation:bgDriftA linear both;
      animation-timeline:view();animation-range:cover 0% cover 100%}
    .bg-b{animation-name:bgDriftB}
    [data-bg=psi] .bg-b{animation-name:bgMoire}
    /* the two blurred layers never move: a blurred layer is the one expensive
       surface in this file and re-compositing it per scroll frame is the only
       way to make this catalogue cost something. */
    [data-bg=lambda] .bg-b,[data-bg=chi] .bg-b{animation:none}
  }
}
@keyframes bgDriftA{from{transform:translate3d(0,-3.2%,0) scale(1.035)}
                    to  {transform:translate3d(0, 3.2%,0) scale(1)}}
@keyframes bgDriftB{from{transform:translate3d(0,-1.1%,0)}
                    to  {transform:translate3d(0, 1.1%,0)}}
@keyframes bgMoire {from{background-position:0 0,0 -3px}
                    to  {background-position:0 0,0  3px}}

/* L6 — `?still=1` freezes DETERMINISTICALLY, and that is a property of the
   catalogue, not of this rule: every variant's authored state IS its final
   visible state, so reverting to it is the whole implementation. A field with
   nothing to freeze cannot be frozen wrong. */
html.still .bg *,html.bg-still .bg *{animation:none!important;transition:none!important}
/* L4 — reduced motion. The page stays complete and correct: only movement is
   removed, never a layer. */
@media (prefers-reduced-motion:reduce){
  .bg *{animation:none!important;transition:none!important}
}
/* Work stops when nobody is looking. Thirteen of fifteen variants have nothing
   to pause — these two rules exist for Ψ, for the scroll drift, and so that any
   variant added later inherits the behaviour instead of re-inventing it. */
html.bg-idle .bg *,.bg-off *{animation-play-state:paused!important}


/* ─── 6 · THE SWITCHER ────────────────────────────────────────────────────── */
/* Top left, small, one Greek glyph, no border until focus or hover. Not an X,
   not a cog. The button shows the CURRENT variant's glyph, so it is both the
   control and the indicator at no extra cost. The glyph is a drawn PATH, never
   text: the site self-hosts Manrope with Latin and Cyrillic subsets only and
   there is no Greek subset, so a text glyph would fall through to a system
   font and render differently on every device. */
.bgsw{display:grid;place-items:center;width:44px;height:44px;padding:0;border:0;
  background:none;color:var(--ink-3,#4A5158);cursor:pointer;
  -webkit-tap-highlight-color:transparent;touch-action:manipulation}
.bgsw--float{position:fixed;left:max(4px,env(safe-area-inset-left));top:4px;z-index:60}
.bgsw svg{width:19px;height:19px;display:block;overflow:visible;
  fill:none;stroke:currentColor;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;
  opacity:.42;transition:opacity .2s ease,transform .2s ease}
.bgsw:hover svg{opacity:.92}
.bgsw:active svg{transform:scale(.9)}
.bgsw:focus-visible{outline:2px solid var(--accent,#1C4E4A);outline-offset:-5px;border-radius:10px}
.bgsw:focus-visible svg{opacity:.95}
/* 44×44 hit area, ~19 px of ink. Hit area and ink are different measurements
   and only one of them is a design decision. */
@media (prefers-reduced-motion:reduce){ .bgsw svg{transition:none} }

.bg-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}


/* ═══ §G · THE CONTRAST GUARANTEE ═════════════════════════════════════════════
   Not advice — the rules below EXECUTE the moment a variant is active, so the
   guarantee cannot be lost by someone forgetting to read this file.
   `bg.js` → hetoimosBG.check() recomputes the whole table from what is
   actually computed in the browser and returns the failures; `?bgcheck=1`
   prints it. A rule that must be read is a rule the next agent reasons its way
   around; a rule that executes is not.

   The arithmetic, so it can be re-run rather than trusted. WCAG 2.1 needs
   4.5:1 for body text. The darkest composite in this catalogue is Θ halftone
   at L 0.650, so any text over any field must satisfy
       L_text ≤ (0.650 + 0.05) / 4.5 − 0.05 = 0.1056.

   G1 — `--ink-3` (#5D656E, L 0.1276) FAILS at 3.94:1 and is retired here to
        #4A5158 (L 0.0805): 5.37:1 on the worst field, 7.19:1 on bare paper.
        Still visibly secondary, no longer unsafe. This was never a background
        problem — #5D656E was already at 5.28:1 on bare paper before any of
        this existed, with 0.78 of a ratio point of margin. The fields did not
        break the token, they exposed it. ONE token change, not fifteen
        weakened fields.
   G2 — `--ink-4` (#7C848D) is BANNED for text and cannot be rescued: at
        L 0.2271 it needs a backdrop of L 0.972 to reach 4.5:1, so it fails on
        pure white (3.79:1), never mind on a field (2.53:1). It is hardened
        here to #6A727B — the lightest value that clears the 3:1 bar for large
        text and non-text graphics on the darkest field (3.25:1) — and that is
        the most this file can honestly do. It is not a body-text colour.
   G3 — every variant declares `--bg-lmin`, its own worst composite luminance,
        and `--bg-lmin ≥ 0.62` is the ship gate. Below it the variant is
        rejected; the text is never darkened to accommodate a field.
   G4 — structure is MASKED away from the text column, never merely faded.
        Legibility is bought with geometry. Check the COMPOSITE, never the
        layers: the layers are individually safe and their product is what
        fails.
   G5 — on dark the constraint is a ceiling: field ≤ L 0.0193, implemented at
        ≤ 0.0173 (see §4), and `--on-dark-3` raised from .56 to .62 for margin.
   G6 — opaque content boxes (pricing tiers, the lead form, cards) are exempt
        and must stay exempt: the field never composites with their text.

   Verified accents, all fifteen, as link text on their OWN field: 6.44:1
   (Θ) to 15.96:1 (Ο). Hover states 8.78:1 to 18.66:1. `--accent-dk` on the
   capped dark field: 5.23:1 (Ρ) to 9.20:1 (Ο). No exceptions, no roundings.
   ═════════════════════════════════════════════════════════════════════════ */
:root[data-bg]{
  --ink-3:#4A5158;      /* G1 — retired from #5D656E */
  --ink-3s:#4A5158;
  --ink-4:#6A727B;      /* G2 — hardened; still not a text colour */
  --on-dark-3:rgba(237,234,227,.62);   /* G5 */
}
