/* ============================================================
   Mikewavs animated background
   Ported from site/exports/mikewavs-background-animation (React + CSS)
   to static markup — the site has no build step.

   Changes from the export:
   - The three colour props are wired to the site's design tokens rather
     than the component's hardcoded defaults, so the canvas matches the
     rest of the site (#080810, not the export's #09090b).
   - Plain colour fallbacks precede each color-mix() so the decorative
     rings degrade to a sensible border rather than currentColor on
     engines without it. (Unlike custom properties, regular properties
     DO support the two-declaration fallback.)

   Everything else — geometry, timings, reduced-motion — is unchanged.
   ============================================================ */

.mw-bg {
  --mw-bg-accent:    var(--mw-accent, #8141f7);
  --mw-bg-secondary: var(--mw-secondary, #b8956a);
  --mw-bg-canvas:    var(--mw-background, #080810);

  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--mw-bg-canvas);
}

.mw-bg--fixed    { position: fixed; }
.mw-bg--absolute { position: absolute; }

/* ---------- veil ----------

   A flat darkening pass over the canvas, tuned by --mw-veil in tokens.css.

   It has to sit ABOVE the blobs and BELOW the content, which is why it hangs
   off the two page roots rather than off .mw-bg. Both roots carry
   `isolation: isolate` and put their content at z-index 2, so z-index 1 is
   the gap this slots into. Putting it inside .mw-bg instead would darken the
   blobs but not the canvas fill behind them; putting it on <body> would
   escape the isolated context and dim the text too.

   Because it never covers the content, raising --mw-veil deepens the
   background without moving a single text contrast ratio. */

.lh::after,
.pg::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--mw-veil);
}

/* Interior variant. The hub and the interior pages used to share no visual
   canvas at all — one had drifting blobs and a waveform, the other a flat
   radial wash — which is a large part of why they read as two sites.

   This puts the same canvas under both, pulled back so it stays behind dense
   editorial content instead of competing with it. The waveform belongs to
   the hub and is not carried over; it is the front door's signature, and
   repeating it everywhere would spend it. */
.mw-bg--page .mw-bg__blob { opacity: 0.18; }
.mw-bg--page .mw-bg__blob--three { opacity: 0.08; }
.mw-bg--page::after { opacity: 0.55; }
.mw-bg--page::before { opacity: 0.6; }

.mw-bg::before,
.mw-bg::after {
  position: absolute;
  content: "";
}

/* drifting hairline pair */
.mw-bg::before {
  top: -18vh;
  left: 18vw;
  width: 64vw;
  height: 130vh;
  border-right: 1px solid rgb(230 228 221 / 4.5%);
  border-left: 1px solid rgb(230 228 221 / 4.5%);
  transform: rotate(12deg);
  animation: mw-line-drift 18s ease-in-out infinite alternate;
}

/* static outer ring */
.mw-bg::after {
  top: 12%;
  left: 50%;
  width: min(90vw, 900px);
  height: min(90vw, 900px);
  border: 1px solid rgba(129, 65, 247, 0.11);
  border: 1px solid color-mix(in srgb, var(--mw-bg-accent) 11%, transparent);
  border-radius: 50%;
  transform: translateX(-50%);
}

/* ---------- blobs ----------

   These were solid circles under `filter: blur(110px)`. That is a very large
   convolution kernel over a 420px+ layer, three times over, on a fixed layer
   that has to re-rasterize as the page scrolls — measured here as enough to
   stall a full-page paint outright, and the single most likely thing to jank
   a mid-range phone.

   A Gaussian blur is not the only way to draw a soft circular glow. A radial
   gradient produces the same result and is rasterized directly, with no
   convolution pass at all. The small residual blur is only there to smooth
   gradient banding on a near-black canvas, and at 24px it is cheap.

   Visually this is a wash; structurally it is roughly free. Each blob keeps
   an rgba fallback ahead of its color-mix() so the canvas still tracks
   --mw-accent on engines that support it, and degrades to the brand purple
   on engines that do not. (Regular properties DO support the two-declaration
   fallback — unlike custom properties, see the note in linkhub.css.) */

.mw-bg__blob {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(24px);
  opacity: 0.30;
  will-change: transform;
}

.mw-bg__blob--one {
  top: -18vw;
  left: -14vw;
  width: 46vw;
  min-width: 420px;
  height: 46vw;
  min-height: 420px;
  background: radial-gradient(circle closest-side,
              rgba(129, 65, 247, 0.85) 0%, rgba(129, 65, 247, 0.34) 48%, rgba(129, 65, 247, 0) 78%);
  background: radial-gradient(circle closest-side,
              color-mix(in srgb, var(--mw-bg-accent) 85%, transparent) 0%,
              color-mix(in srgb, var(--mw-bg-accent) 34%, transparent) 48%,
              transparent 78%);
  animation: mw-blob-one 18s ease-in-out infinite alternate;
}

.mw-bg__blob--two {
  top: 24vh;
  right: -12vw;
  width: 38vw;
  min-width: 360px;
  height: 38vw;
  min-height: 360px;
  background: radial-gradient(circle closest-side,
              rgba(108, 52, 207, 0.85) 0%, rgba(108, 52, 207, 0.32) 48%, rgba(108, 52, 207, 0) 78%);
  background: radial-gradient(circle closest-side,
              color-mix(in srgb, var(--mw-bg-accent) 66%, #190046) 0%,
              color-mix(in srgb, var(--mw-bg-accent) 26%, transparent) 48%,
              transparent 78%);
  animation: mw-blob-two 21s ease-in-out infinite alternate;
}

.mw-bg__blob--three {
  bottom: -22vw;
  left: 30vw;
  width: 30vw;
  min-width: 300px;
  height: 30vw;
  min-height: 300px;
  background: radial-gradient(circle closest-side,
              rgba(184, 149, 106, 0.75) 0%, rgba(184, 149, 106, 0.28) 48%, rgba(184, 149, 106, 0) 78%);
  background: radial-gradient(circle closest-side,
              color-mix(in srgb, var(--mw-bg-secondary) 75%, transparent) 0%,
              color-mix(in srgb, var(--mw-bg-secondary) 28%, transparent) 48%,
              transparent 78%);
  opacity: 0.13;
  animation: mw-blob-three 17s ease-in-out infinite alternate;
}

/* ---------- orbits ---------- */

.mw-bg__orbit {
  position: absolute;
  display: block;
  border: 1px solid rgb(230 228 221 / 11%);
  border-radius: 50%;
}

.mw-bg__orbit::after {
  position: absolute;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mw-bg-accent);
  box-shadow: 0 0 22px rgba(129, 65, 247, 0.9);
  box-shadow: 0 0 22px color-mix(in srgb, var(--mw-bg-accent) 90%, transparent);
  content: "";
}

.mw-bg__orbit--one {
  top: 13%;
  left: 50%;
  width: min(72vw, 760px);
  height: min(72vw, 760px);
  transform: translateX(-50%);
  animation: mw-orbit-spin 28s linear infinite;
}
.mw-bg__orbit--one::after { top: 18%; left: 11%; }

.mw-bg__orbit--two {
  top: 24%;
  left: 50%;
  width: min(50vw, 520px);
  height: min(50vw, 520px);
  border-color: rgba(184, 149, 106, 0.18);
  border-color: color-mix(in srgb, var(--mw-bg-secondary) 18%, transparent);
  transform: translateX(-50%);
  animation: mw-orbit-spin-reverse 22s linear infinite;
}
.mw-bg__orbit--two::after {
  right: 4%;
  bottom: 29%;
  width: 5px;
  height: 5px;
  background: var(--mw-bg-secondary);
  box-shadow: none;
}

/* ---------- waveform ---------- */

/* Tight enough to read as one waveform rather than a field of loose vertical
   rules. It sits behind full-width display type now, so the bars have to
   group into a recognisable shape — spread wide they just look like stray
   lines crossing the headings. */
.mw-bg__wave {
  position: absolute;
  top: 34%;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(560px, 74vw);
  height: 190px;
  gap: clamp(4px, 0.55vw, 8px);
  opacity: 0.11;
  transform: translateX(-50%);
}

.mw-bg__wave span {
  width: clamp(3px, 0.38vw, 5px);
  height: var(--mw-bar-height);
  border-radius: 999px;
  background: var(--mw-bg-accent);
  transform-origin: center;
  animation: mw-wave-pulse 2.2s ease-in-out var(--mw-bar-delay) infinite;
}

/* scripts/wave.js sets this while the pointer is driving the bars, so the
   keyframe animation and the inline transform never contend for the same
   property. Dropping the class restores the loop with no cleanup. */
.mw-bg__wave.is-reactive span {
  animation: none;
  opacity: 1;
  transition: transform var(--mw-dur) var(--mw-ease);
}

/* ---------- keyframes ---------- */

@keyframes mw-wave-pulse {
  0%, 100% { opacity: 0.45; transform: scaleY(0.56); }
  50%      { opacity: 1;    transform: scaleY(1); }
}

@keyframes mw-line-drift {
  from { transform: translateX(-5vw) rotate(12deg); }
  to   { transform: translateX(5vw) rotate(12deg); }
}

@keyframes mw-orbit-spin {
  from { transform: translateX(-50%) rotate(0deg); }
  to   { transform: translateX(-50%) rotate(360deg); }
}

@keyframes mw-orbit-spin-reverse {
  from { transform: translateX(-50%) rotate(360deg); }
  to   { transform: translateX(-50%) rotate(0deg); }
}

@keyframes mw-blob-one {
  from { transform: translate3d(-4vw, -3vh, 0) scale(0.92); }
  to   { transform: translate3d(18vw, 14vh, 0) scale(1.12); }
}

@keyframes mw-blob-two {
  from { transform: translate3d(5vw, -8vh, 0) scale(1.04); }
  to   { transform: translate3d(-19vw, 18vh, 0) scale(0.88); }
}

@keyframes mw-blob-three {
  from { transform: translate3d(-8vw, 6vh, 0) scale(0.9); }
  to   { transform: translate3d(15vw, -18vh, 0) scale(1.14); }
}

/* ---------- responsive ---------- */

@media (max-width: 600px) {
  .mw-bg__wave { top: 26%; opacity: 0.09; }
  .mw-bg__orbit--one { top: 12%; width: 118vw; height: 118vw; }
  .mw-bg__orbit--two { top: 21%; width: 82vw;  height: 82vw; }
}

@media (prefers-reduced-motion: reduce) {
  .mw-bg,
  .mw-bg::before,
  .mw-bg::after,
  .mw-bg *,
  .mw-bg *::before,
  .mw-bg *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
