/* =============================================================
   theme-light.css — light surface for the same instrument.
   Loaded after styles.css and probes.css; it never changes layout, DOM or
   behaviour, only the values the pixel/CRT language is built from.

   How it works
     1. Everything styles.css and probes.css hard-code as a literal colour is
        re-declared here once, in terms of a variable. Those rules are
        theme-neutral: in dark they resolve to exactly the values they
        replaced, so the dark surface is byte-for-byte what it was.
     2. The light values are then a single token payload, applied down two
        paths: the system preference (when the reader has made no explicit
        choice) and the explicit choice (which must win over the system in
        both directions). The payload is emitted twice, at the bottom of this
        file; the two blocks are generated from one source and must stay
        identical.

   Hard shadows are scaled by one factor, --depth, rather than re-picked one
   by one: "the same geometry, a quarter of the weight" is the whole rule for
   moving this surface onto paper, and having it in one number keeps it that
   way.
   ============================================================= */

:root {
  color-scheme: dark;

  /* --- dark defaults: identical to the literals they replace ------------ */
  --ink: 0 0 0;
  --depth: 1;

  --page-glow: rgba(63, 185, 80, 0.08);
  --lattice: rgba(150, 190, 255, 0.016);
  --scanline: rgba(255, 255, 255, 0.03);
  --vignette: rgba(0, 0, 0, 0.5);

  --topbar-bg: rgba(11, 14, 20, 0.86);
  --verdict-top: #121826;
  --verdict-bottom: #0d1119;
  --verdict-sweep: rgba(120, 160, 255, 0.09);
  --diagnose-bottom: #0e1219;
  --result-bg: rgba(31, 111, 235, 0.07);
  --btn-fg: #06110a;
  --border-hover: #2c3545;
  --active-border: #33507d;
  --active-bg: rgba(31, 111, 235, 0.16);
  --tip-bg: #1b2130;
  --tip-border: #2c3545;
  --control-line: var(--line);

  --chip-hi-soft: rgba(255, 255, 255, 0.05);
  --chip-hair: rgba(255, 255, 255, 0.035);
  --chip-mark: rgba(230, 233, 240, 0.6);
  --chip-mark-soft: rgba(230, 233, 240, 0.32);
  --probe-row-hi: rgba(255, 255, 255, 0.03);
  --inner-hairline: inset 0 1px 0 rgba(255, 255, 255, 0.045);
  --card-topline: rgba(190, 215, 255, 0.34);
  --card-sheen: rgba(120, 170, 255, 0.13);
  --card-glow: rgba(88, 166, 255, 0.22);
  --major-bloom: rgba(218, 54, 51, 0.45);
  --beat-ring: rgba(63, 185, 80, 0.45);

  --col-filter: brightness(1.68);
  --alarm-filter: brightness(1.35);
  --card-in-filter: brightness(2.1) saturate(0.25);

  --pill-bg-mix: 10%;
  --pill-bd-mix: 28%;
  --pill-bd-mix-warm: 30%;

  --fx-blend: screen;
  --theme-name: "dark";

  /* --- derived: one weight rule, applied everywhere -------------------- */
  --bevel-lo: rgb(var(--ink) / calc(0.42 * var(--depth)));
  --chip-lo-soft: rgb(var(--ink) / calc(0.06 * var(--depth)));
  --chip-flat-lo: rgb(var(--ink) / calc(0.18 * var(--depth)));
  --probe-row-lo: rgb(var(--ink) / calc(0.16 * var(--depth)));
  --probe-stripe: rgb(var(--ink) / calc(0.38 * var(--depth)));
  --col-shadow: rgb(var(--ink) / calc(0.55 * var(--depth)));
  --card-shadow: rgb(var(--ink) / calc(0.5 * var(--depth)));
  --card-shadow-strong: rgb(var(--ink) / calc(0.55 * var(--depth)));
  --card-shadow-soft: rgb(var(--ink) / calc(0.4 * var(--depth)));
  --btn-shadow: rgb(var(--ink) / calc(0.6 * var(--depth)));
  --tip-shadow: rgb(var(--ink) / calc(0.8 * var(--depth)));
  --verdict-led-gap: rgb(var(--ink) / calc(0.55 * var(--depth)));
  --ts-1: rgb(var(--ink) / calc(0.55 * var(--depth)));
  --ts-2: rgb(var(--ink) / calc(0.5 * var(--depth)));
  --ts-3: rgb(var(--ink) / calc(0.45 * var(--depth)));

  --fringe-r: color-mix(in srgb, var(--t-major) 28%, transparent);
  --fringe-b: color-mix(in srgb, var(--t-watch) 30%, transparent);
  --finding-bg: color-mix(in srgb, var(--t-partial) 7%, transparent);
  --finding-line: color-mix(in srgb, var(--t-partial) 22%, transparent);
  --finding-notch: color-mix(in srgb, var(--t-partial) 50%, transparent);
}

/* =============================================================
   Theme-neutral rewiring. Every rule below reproduces what styles.css or
   probes.css already draws; the only change is that the literal became a
   token, so the light payload has something to move.
   ============================================================= */

/* ---------- Ground ---------- */

body {
  background-color: var(--bg);
  background-image:
    radial-gradient(1000px 420px at 50% -200px, var(--page-glow), transparent 70%),
    linear-gradient(var(--lattice) 1px, transparent 1px),
    linear-gradient(90deg, var(--lattice) 1px, transparent 1px);
}

body::after {
  background:
    repeating-linear-gradient(to bottom, var(--scanline) 0 1px, transparent 1px 3px),
    radial-gradient(130% 100% at 50% 50%, transparent 56%, var(--vignette) 100%);
}

html { scrollbar-color: var(--scroll-thumb) var(--scroll-track); }
.wall-scroll { scrollbar-color: var(--scroll-thumb) var(--scroll-track); }

/* ---------- Top bar ---------- */

.topbar { background: var(--topbar-bg); }
.brand-mark { box-shadow: 0 0 0 0 var(--beat-ring); }

@keyframes beat {
  0% { box-shadow: 0 0 0 0 var(--beat-ring); }
  70% { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ---------- Verdict ---------- */

.verdict { background: linear-gradient(180deg, var(--verdict-top), var(--verdict-bottom)); }

.verdict::before {
  background: repeating-linear-gradient(
    to bottom,
    var(--verdict-color, var(--t-op)) 0 7px,
    var(--verdict-led-gap) 7px 11px
  );
}

.verdict::after { background: linear-gradient(90deg, transparent, var(--verdict-sweep), transparent); }

.verdict h1 {
  text-shadow:
    var(--px) var(--px) 0 var(--ts-1),
    -1px 0 0 var(--fringe-r),
    1px 0 0 var(--fringe-b);
}

.verdict-counts b { text-shadow: var(--px) var(--px) 0 var(--ts-2); }
.section-heading h2 { text-shadow: var(--px) var(--px) 0 var(--ts-3); }

/* ---------- Wall cells ---------- */

/* Order matches styles.css: the state bloom, then the no-data hairline, then
   the column pop, then the "now" and "tracking start" markers. Those markers
   must keep winning over decoration in both themes. */

.cell {
  background-image: linear-gradient(
    180deg,
    var(--bevel-hi) 0,
    var(--chip-hi-soft) 38%,
    var(--chip-lo-soft) 62%,
    var(--bevel-lo) 100%
  );
}

.cell[data-state="unknown"],
.cell[data-state="no_data"] {
  background-image: linear-gradient(180deg, var(--chip-hi-soft), var(--chip-flat-lo));
}

.cell[data-state="major_outage"] { box-shadow: 0 0 6px var(--major-bloom); }
.cell[data-state="unknown"] { box-shadow: inset 0 0 0 1px var(--chip-hair); }
.cell.is-col { filter: var(--col-filter) drop-shadow(0 3px 4px var(--col-shadow)); }
.cell.is-now { box-shadow: inset 0 0 0 1px var(--chip-mark); }
.cell.is-start { box-shadow: inset 2px 0 0 var(--chip-mark-soft); }

@keyframes fx-alarm {
  0%, 100% { filter: brightness(1); }
  50% { filter: var(--alarm-filter); }
}

/* The legend's no-data swatch carries its hairline as an inline style, so the
   light version has to out-rank it. */
.wall-legend .swatch[style*="c-nodata"] {
  box-shadow: inset 0 0 0 1px var(--chip-hair) !important;
}

/* ---------- Controls ---------- */

#service-search,
.wall-vendor select,
.diagnose-form select,
.range-button,
.filter-button { border-color: var(--control-line); }

.range-button,
.filter-button,
.diagnose-form button { filter: drop-shadow(var(--px) var(--px) 0 var(--btn-shadow)); }

.range-button:active,
.filter-button:active,
.diagnose-form button:active { filter: none; }

.range-button:hover,
.filter-button:hover,
.wall-vendor select:hover,
.service-card:hover { border-color: var(--border-hover); }

.range-button.is-active,
.filter-button.is-active {
  border-color: var(--active-border);
  background: var(--active-bg);
}

/* ---------- Cards ---------- */

.service-card,
.probe-target { filter: drop-shadow(0 3px 0 var(--card-shadow)); }

.service-card:hover,
.probe-target:hover {
  filter:
    drop-shadow(0 6px 0 var(--card-shadow-strong))
    drop-shadow(0 14px 20px var(--card-shadow-soft))
    drop-shadow(0 0 9px var(--card-glow));
}

.service-card::before,
.probe-target::before { background: linear-gradient(90deg, transparent, var(--card-topline), transparent); }

.service-card::after,
.probe-target::after {
  background: radial-gradient(
    220px circle at var(--fx-mx, 50%) var(--fx-my, 50%),
    var(--card-sheen),
    transparent 62%
  );
}

.wall-card,
.insight-panel,
.service-card,
.probe-target { box-shadow: var(--inner-hairline); }

@keyframes fx-card-in {
  0% { opacity: 0; transform: perspective(780px) translateZ(-70px) scale(0.94); filter: var(--card-in-filter); }
  55% { opacity: 1; }
  100% { opacity: 1; transform: perspective(780px) translateZ(0) scale(1); filter: drop-shadow(0 3px 0 var(--card-shadow)); }
}

/* ---------- Pills ---------- */

/* Derived from the text scale so a re-graded --t-* carries its own tint with
   it. If color-mix is unavailable the declarations drop and the styles.css
   literals stand; the pill's meaning lives in its text colour either way. */
.pill.operational { background: color-mix(in srgb, var(--t-op) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-op) var(--pill-bd-mix), transparent); }
.pill.watch { background: color-mix(in srgb, var(--t-watch) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-watch) var(--pill-bd-mix), transparent); }
.pill.maintenance { background: color-mix(in srgb, var(--t-maint) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-maint) var(--pill-bd-mix), transparent); }
.pill.degraded { background: color-mix(in srgb, var(--t-degraded) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-degraded) var(--pill-bd-mix-warm), transparent); }
.pill.partial_outage { background: color-mix(in srgb, var(--t-partial) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-partial) var(--pill-bd-mix-warm), transparent); }
.pill.major_outage { background: color-mix(in srgb, var(--t-major) var(--pill-bg-mix), transparent); border-color: color-mix(in srgb, var(--t-major) var(--pill-bd-mix-warm), transparent); }

/* ---------- Diagnose, tooltip, footer ---------- */

.diagnose { background: linear-gradient(180deg, var(--panel), var(--diagnose-bottom)); }
.diagnose-form button { color: var(--btn-fg); }
.diagnose-result { background: var(--result-bg); }

#tip {
  background: var(--tip-bg);
  border-color: var(--tip-border);
  filter: drop-shadow(var(--px) var(--px) 0 var(--tip-shadow));
}

/* ---------- Probe surface (probes.css owns the layout, not the palette) --- */

.probe-signal-row { background-image: linear-gradient(180deg, var(--probe-row-hi), var(--probe-row-lo)); }

.probe-target > header span,
.probe-signal-row > strong,
.probe-latency-heading strong { text-shadow: var(--px) var(--px) 0 var(--ts-2); }

.probe-surface-heading h2,
.probe-empty strong { text-shadow: var(--px) var(--px) 0 var(--ts-3); }

.probe-finding {
  border-bottom-color: var(--finding-line);
  box-shadow: inset var(--px, 2px) 0 0 var(--finding-notch);
  background: var(--finding-bg);
}

.probe-region > i { background-image: linear-gradient(180deg, var(--chip-hi-soft), var(--chip-flat-lo)); }

.probe-region > i b {
  background-image:
    repeating-linear-gradient(90deg, var(--probe-stripe) 0 var(--px, 2px), transparent var(--px, 2px) calc(var(--px, 2px) * 2)),
    linear-gradient(180deg, var(--bevel-hi), transparent 45%, var(--bevel-lo));
}

/* ---------- Particle layer ---------- */

/* src/particles.js reads --fx-blend to decide whether it is painting light on
   dark or dark on light. The fallback keeps the dark surface correct even if
   this file never arrives. */
.fx-canvas { mix-blend-mode: var(--fx-blend, screen); }

/* =============================================================
   Theme toggle. Lives in the top bar, styled the same way as the other pixel
   controls; present and identical in both themes.
   ============================================================= */

.theme-toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Sized to sit inside the bar the brand already sets the height of, so
     adding it moves nothing below it. */
  padding: 4px 9px;
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  background: var(--panel-2);
  border: 1px solid var(--control-line);
  border-radius: 0;
  cursor: pointer;
  clip-path: polygon(
    0 3px, 3px 0, calc(100% - 3px) 0, 100% 3px,
    100% calc(100% - 3px), calc(100% - 3px) 100%, 3px 100%, 0 calc(100% - 3px)
  );
  filter: drop-shadow(var(--px) var(--px) 0 var(--btn-shadow));
  transition: transform 0.08s steps(2, end), filter 0.08s steps(2, end), color 0.15s, border-color 0.15s, background 0.15s;
}

/* Half-filled square: the same mark in both themes, drawn in currentColor so
   it tracks hover and focus without a second rule. */
.theme-toggle::before {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  background: linear-gradient(90deg, currentColor 0 50%, transparent 50%);
  box-shadow: inset 0 0 0 1px currentColor;
}

/* Reads the current theme out of CSS, so it is correct on the system path
   with no script and cannot flash the wrong word. */
.theme-toggle::after { content: var(--theme-name); }

.theme-toggle:hover { color: var(--fg); border-color: var(--border-hover); }
.theme-toggle:active { transform: translate(var(--px), var(--px)); filter: none; }

/* clip-path clips outline, so the focus ring has to be drawn inside the chip. */
.theme-toggle:focus-visible {
  outline: none;
  color: var(--fg);
  box-shadow: inset 0 0 0 var(--px) var(--t-watch);
}

/* Once the nav is gone nothing pushes the button right, so it takes over —
   and the top bar is down to brand, stamp and this, so the word goes and the
   mark carries it. The accessible name is on the button either way. */
@media (max-width: 620px) {
  .theme-toggle { margin-left: auto; padding: 5px; }
  .theme-toggle::after { display: none; }
}

/* =============================================================
   The light payload, on both paths.

   Path 1 is the system preference, and it is deliberately declined by an
   explicit dark choice. Path 2 is the explicit light choice and, being later
   and no less specific, beats a dark system preference.

   The two blocks below are generated from one source. Keep them identical.
   ============================================================= */

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    /* Paper, not a negative of the panel: the ground is a hair cooler than the
       cards so a white chip still reads as raised against it. */
    --bg: #f4f5f8;
    --panel: #ffffff;
    --panel-2: #e9ecf3;
    --line: #ccd3e0;
    --line-soft: #e0e4ec;

    --fg: #10141c;
    --fg-dim: #4a5464;
    --fg-faint: #5f6878;

    /* Cell scale re-graded for a light ground. Same meanings, same order, but
       the ramp now runs quiet-to-loud downwards: healthy sits mid, anomalies go
       dark and saturated. Closest pair (partial/major) is dE76 25.4, i.e. the
       same separation the dark scale already ships. */
    --c-nodata: #dfe3ec;
    --c-op: #3f9a5c;
    --c-watch: #2f74dc;
    --c-maint: #7d55d0;
    --c-degraded: #b58108;
    --c-partial: #d9601a;
    --c-major: #c92f2b;

    /* Text weights: every one of these clears 4.5:1 on --panel, --panel-2 and
       on its own pill tint. The dark scale's greens and blues float on white,
       so they are pushed down, not inverted. */
    --t-op: #18783a;
    --t-watch: #0a58ca;
    --t-maint: #6639ba;
    --t-degraded: #8a5d00;
    --t-partial: #a54800;
    --t-major: #c02020;

    /* Controls need a boundary the fill cannot provide: --panel-2 is only
       1.08:1 against --bg here, so the border carries the affordance. */
    --control-line: #858fa4;

    /* Hard shadows keep their geometry and lose most of their weight: on white
       a 0.5-alpha black offset reads as dirt, not as depth. */
    --ink: 20 26 38;
    --depth: 0.28;
    --vignette: rgb(var(--ink) / 0.06);
    --col-shadow: rgb(var(--ink) / 0.26);
    --probe-stripe: rgb(var(--ink) / 0.26);

    /* Bevel flips its weighting rather than its direction: light still comes
       from above, but on a light chip the top highlight has to do the work the
       bottom shadow did, or every cell smears into its neighbour. */
    --bevel-hi: rgba(255, 255, 255, 0.55);
    --chip-hi-soft: rgba(255, 255, 255, 0.42);
    --chip-hair: rgb(var(--ink) / 0.16);
    --chip-mark: rgb(var(--ink) / 0.7);
    --chip-mark-soft: rgb(var(--ink) / 0.42);
    --probe-row-hi: rgba(255, 255, 255, 0.6);
    --inner-hairline: inset 0 -1px 0 rgb(var(--ink) / 0.07);

    /* Scanlines invert: dark ground gets bright lines, light ground gets dim
       ones. Both stay under 4% or the surface reads as smudged. */
    --scanline: rgb(var(--ink) / 0.03);
    --lattice: rgb(var(--ink) / 0.035);
    --page-glow: rgba(47, 116, 220, 0.06);

    --topbar-bg: rgba(255, 255, 255, 0.88);
    --verdict-top: #ffffff;
    --verdict-bottom: #eef1f7;
    --verdict-sweep: rgba(20, 40, 90, 0.06);
    --diagnose-bottom: #edf0f6;
    --result-bg: rgba(10, 88, 202, 0.06);
    --btn-fg: #ffffff;
    --border-hover: #aab3c4;
    --active-border: #7fa5e6;
    --active-bg: rgba(10, 88, 202, 0.09);
    --tip-bg: #ffffff;
    --tip-border: #c6cddc;

    --card-topline: rgb(var(--ink) / 0.13);
    --card-sheen: rgba(20, 60, 140, 0.07);
    --card-glow: rgba(10, 88, 202, 0.16);
    --major-bloom: rgba(201, 47, 43, 0.3);

    --pill-bg-mix: 13%;
    --pill-bd-mix: 46%;
    --pill-bd-mix-warm: 46%;

    /* Brightening a light chip washes it out, so "raised" and "alarming" are
       expressed by going down and more saturated instead of up. */
    --col-filter: brightness(0.92) saturate(1.3);
    --alarm-filter: brightness(0.84) saturate(1.2);
    --card-in-filter: brightness(0.72) saturate(0.3);
    --beat-ring: rgba(24, 120, 58, 0.4);

    /* screen on white is invisible; particles have to darken instead. */
    --fx-blend: multiply;

    --scroll-thumb: #b9c1d0;
    --scroll-track: #e9ecf3;

    --theme-name: "light";
  }
}

:root[data-theme="light"] {
  color-scheme: light;

  /* Paper, not a negative of the panel: the ground is a hair cooler than the
     cards so a white chip still reads as raised against it. */
  --bg: #f4f5f8;
  --panel: #ffffff;
  --panel-2: #e9ecf3;
  --line: #ccd3e0;
  --line-soft: #e0e4ec;

  --fg: #10141c;
  --fg-dim: #4a5464;
  --fg-faint: #5f6878;

  /* Cell scale re-graded for a light ground. Same meanings, same order, but
     the ramp now runs quiet-to-loud downwards: healthy sits mid, anomalies go
     dark and saturated. Closest pair (partial/major) is dE76 25.4, i.e. the
     same separation the dark scale already ships. */
  --c-nodata: #dfe3ec;
  --c-op: #3f9a5c;
  --c-watch: #2f74dc;
  --c-maint: #7d55d0;
  --c-degraded: #b58108;
  --c-partial: #d9601a;
  --c-major: #c92f2b;

  /* Text weights: every one of these clears 4.5:1 on --panel, --panel-2 and
     on its own pill tint. The dark scale's greens and blues float on white,
     so they are pushed down, not inverted. */
  --t-op: #18783a;
  --t-watch: #0a58ca;
  --t-maint: #6639ba;
  --t-degraded: #8a5d00;
  --t-partial: #a54800;
  --t-major: #c02020;

  /* Controls need a boundary the fill cannot provide: --panel-2 is only
     1.08:1 against --bg here, so the border carries the affordance. */
  --control-line: #858fa4;

  /* Hard shadows keep their geometry and lose most of their weight: on white
     a 0.5-alpha black offset reads as dirt, not as depth. */
  --ink: 20 26 38;
  --depth: 0.28;
  --vignette: rgb(var(--ink) / 0.06);
  --col-shadow: rgb(var(--ink) / 0.26);
  --probe-stripe: rgb(var(--ink) / 0.26);

  /* Bevel flips its weighting rather than its direction: light still comes
     from above, but on a light chip the top highlight has to do the work the
     bottom shadow did, or every cell smears into its neighbour. */
  --bevel-hi: rgba(255, 255, 255, 0.55);
  --chip-hi-soft: rgba(255, 255, 255, 0.42);
  --chip-hair: rgb(var(--ink) / 0.16);
  --chip-mark: rgb(var(--ink) / 0.7);
  --chip-mark-soft: rgb(var(--ink) / 0.42);
  --probe-row-hi: rgba(255, 255, 255, 0.6);
  --inner-hairline: inset 0 -1px 0 rgb(var(--ink) / 0.07);

  /* Scanlines invert: dark ground gets bright lines, light ground gets dim
     ones. Both stay under 4% or the surface reads as smudged. */
  --scanline: rgb(var(--ink) / 0.03);
  --lattice: rgb(var(--ink) / 0.035);
  --page-glow: rgba(47, 116, 220, 0.06);

  --topbar-bg: rgba(255, 255, 255, 0.88);
  --verdict-top: #ffffff;
  --verdict-bottom: #eef1f7;
  --verdict-sweep: rgba(20, 40, 90, 0.06);
  --diagnose-bottom: #edf0f6;
  --result-bg: rgba(10, 88, 202, 0.06);
  --btn-fg: #ffffff;
  --border-hover: #aab3c4;
  --active-border: #7fa5e6;
  --active-bg: rgba(10, 88, 202, 0.09);
  --tip-bg: #ffffff;
  --tip-border: #c6cddc;

  --card-topline: rgb(var(--ink) / 0.13);
  --card-sheen: rgba(20, 60, 140, 0.07);
  --card-glow: rgba(10, 88, 202, 0.16);
  --major-bloom: rgba(201, 47, 43, 0.3);

  --pill-bg-mix: 13%;
  --pill-bd-mix: 46%;
  --pill-bd-mix-warm: 46%;

  /* Brightening a light chip washes it out, so "raised" and "alarming" are
     expressed by going down and more saturated instead of up. */
  --col-filter: brightness(0.92) saturate(1.3);
  --alarm-filter: brightness(0.84) saturate(1.2);
  --card-in-filter: brightness(0.72) saturate(0.3);
  --beat-ring: rgba(24, 120, 58, 0.4);

  /* screen on white is invisible; particles have to darken instead. */
  --fx-blend: multiply;

  --scroll-thumb: #b9c1d0;
  --scroll-track: #e9ecf3;

  --theme-name: "light";
}

/* Restated after the rules above, which are later in the cascade than the
   reduced-motion block in styles.css and would otherwise revive the surface
   effects it switches off. */
@media (prefers-reduced-motion: reduce) {
  body::after { background: none; }
  .fx-canvas { display: none; }
}
