/* ============================================================
   THEME ENGINE
   ------------------------------------------------------------
   This file defines the CONTRACT every theme pack must fill.
   It contains no theme colours of its own — only:
     1. @property registrations (so colours can be ANIMATED)
     2. the default/fallback values
     3. the transition + morph machinery
     4. atmosphere layers + particle shapes driven by theme vars

   To add a theme:  create src/themes/packs/<id>.css, link it in
   index.html, add an entry to src/themes/registry.js. Nothing else.
   To remove a theme: delete those three things. Nothing else breaks.
   ============================================================ */

/* ── 1. Animatable colour properties ─────────────────────────
   Custom properties normally snap. Registering them with a
   <color> syntax lets the browser interpolate them, which is
   what makes a theme switch tween instead of flash. */
@property --bg          { syntax: '<color>'; inherits: true; initial-value: #09080f; }
@property --surface     { syntax: '<color>'; inherits: true; initial-value: #110f1a; }
@property --surface-2   { syntax: '<color>'; inherits: true; initial-value: #1a1726; }
@property --surface-3   { syntax: '<color>'; inherits: true; initial-value: #221f30; }
@property --accent      { syntax: '<color>'; inherits: true; initial-value: #e81a2a; }
@property --accent-2    { syntax: '<color>'; inherits: true; initial-value: #ff3344; }
@property --accent-dim  { syntax: '<color>'; inherits: true; initial-value: #5a0a10; }
@property --support     { syntax: '<color>'; inherits: true; initial-value: #7c6fff; }
@property --support-2   { syntax: '<color>'; inherits: true; initial-value: #22c55e; }
@property --text        { syntax: '<color>'; inherits: true; initial-value: #eeeaf5; }
@property --text-dim    { syntax: '<color>'; inherits: true; initial-value: #9896ad; }
@property --text-dimmer { syntax: '<color>'; inherits: true; initial-value: #7a7890; }

/* Numeric properties that themes tween too */
@property --glow-alpha     { syntax: '<number>'; inherits: true; initial-value: 0.18; }
@property --noise-opacity  { syntax: '<number>'; inherits: true; initial-value: 0.025; }
@property --scanline-alpha { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --vignette-alpha { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --bloom-alpha    { syntax: '<number>'; inherits: true; initial-value: 0; }

/* ── 2. Default token values (theme "reaper" overrides these) ─ */
:root {
  /* surfaces */
  --bg: #09080f;
  --surface: #110f1a;
  --surface-2: #1a1726;
  --surface-3: #221f30;
  --rim: rgba(255, 255, 255, 0.07);
  --rim-strong: rgba(255, 255, 255, 0.12);

  /* accents */
  --accent: #e81a2a;
  --accent-2: #ff3344;
  --accent-dim: #5a0a10;
  --support: #7c6fff;
  --support-2: #22c55e;

  /* text */
  --text: #eeeaf5;
  --text-dim: #9896ad;
  --text-dimmer: #7a7890;
  --on-accent: #09080f;

  /* typography — every theme MUST set these */
  --font-display: 'Bebas Neue', sans-serif;
  --font-body: 'Rajdhani', sans-serif;
  --display-weight: 400;
  --display-tracking: 0.08em;
  --display-transform: uppercase;
  --display-line: 1;
  --display-scale: 1;
  --body-weight: 500;
  --body-weight-strong: 700;
  --body-tracking: 0;
  --body-line: 1.5;
  --body-scale: 1;

  /* shape */
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 20px;
  --r-pill: 999px;
  --border-w: 1px;

  /* vibe */
  --glow-alpha: 0.18;
  --glow-blur: 80px;
  --noise-opacity: 0.025;
  --panel-blur: 16px;
  --scanline-alpha: 0;
  --vignette-alpha: 0;
  --bloom-alpha: 0;
  --rim-light: 0;

  /* motion */
  --ease: cubic-bezier(0.22, 0.9, 0.3, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 140ms;
  --dur: 260ms;
  --dur-slow: 480ms;
  --float-amp: 6px;

  /* particles (read by src/core/atmosphere.js) */
  --particle-kind: ember;
  --particle-count: 14;
  --particle-min: 1.5;
  --particle-max: 4.5;
  --particle-speed: 9;
  --particle-drift: 30;
  --particle-opacity: 0.45;
  --particle-color: #e81a2a;

  /* how long a theme swap takes */
  --theme-swap: 620ms;
}

/* ── 3. The tween ────────────────────────────────────────────
   Everything that reads a themed token gets a transition on the
   token itself, so colour changes glide instead of jumping. */
:root {
  transition:
    --bg var(--theme-swap) var(--ease),
    --surface var(--theme-swap) var(--ease),
    --surface-2 var(--theme-swap) var(--ease),
    --surface-3 var(--theme-swap) var(--ease),
    --accent var(--theme-swap) var(--ease),
    --accent-2 var(--theme-swap) var(--ease),
    --accent-dim var(--theme-swap) var(--ease),
    --support var(--theme-swap) var(--ease),
    --support-2 var(--theme-swap) var(--ease),
    --text var(--theme-swap) var(--ease),
    --text-dim var(--theme-swap) var(--ease),
    --text-dimmer var(--theme-swap) var(--ease),
    --glow-alpha var(--theme-swap) var(--ease),
    --noise-opacity var(--theme-swap) var(--ease),
    --scanline-alpha var(--theme-swap) var(--ease),
    --vignette-alpha var(--theme-swap) var(--ease),
    --bloom-alpha var(--theme-swap) var(--ease);
}

/* Typography can't tween, so it gets a short settle instead:
   during a swap the type fades down, swaps family, fades back. */
html.theme-swapping .type-settle {
  animation: typeSettle var(--theme-swap) var(--ease) both;
}
@keyframes typeSettle {
  0%   { opacity: 1;    filter: blur(0);      letter-spacing: var(--display-tracking); }
  38%  { opacity: 0.35; filter: blur(2.5px);  letter-spacing: calc(var(--display-tracking) + 0.06em); }
  100% { opacity: 1;    filter: blur(0);      letter-spacing: var(--display-tracking); }
}

/* ── 4. Ink-wipe morph overlay (fallback + accent for the swap) ─ */
.theme-morph {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(circle at var(--morph-x, 50%) var(--morph-y, 50%),
      color-mix(in srgb, var(--morph-tint, #fff) 85%, transparent) 0%,
      color-mix(in srgb, var(--morph-tint, #fff) 35%, transparent) 42%,
      transparent 70%);
  clip-path: circle(0% at var(--morph-x, 50%) var(--morph-y, 50%));
  mix-blend-mode: screen;
}
.theme-morph.run {
  animation: inkWipe var(--theme-swap) var(--ease-out) forwards;
}
@keyframes inkWipe {
  0%   { opacity: 0.95; clip-path: circle(0% at var(--morph-x) var(--morph-y)); }
  55%  { opacity: 0.55; }
  100% { opacity: 0;    clip-path: circle(150% at var(--morph-x) var(--morph-y)); }
}

/* ── 5. View Transitions (Chrome/Edge/Safari) ────────────────
   When available the whole page cross-dissolves from the tapped
   swatch, which is smoother than any JS fallback. */
::view-transition-old(root) { animation: none; mix-blend-mode: normal; }
::view-transition-new(root) {
  animation: vtReveal var(--theme-swap) var(--ease-out) both;
  mix-blend-mode: normal;
}
@keyframes vtReveal {
  from { clip-path: circle(0% at var(--morph-x, 50%) var(--morph-y, 50%)); }
  to   { clip-path: circle(150% at var(--morph-x, 50%) var(--morph-y, 50%)); }
}

/* ── 6. Atmosphere layers ────────────────────────────────────
   All four exist always; each theme sets their alpha to 0 or
   something, so themes differ in TEXTURE and not just hue. */
.atmos {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.atmos-noise {
  position: absolute;
  inset: 0;
  opacity: var(--noise-opacity);
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
}

.atmos-scanlines {
  position: absolute;
  inset: 0;
  opacity: var(--scanline-alpha);
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.55) 0px,
    rgba(0, 0, 0, 0.55) 1px,
    transparent 1px,
    transparent 3px);
  animation: scanCrawl 8s linear infinite;
}
@keyframes scanCrawl {
  from { transform: translateY(0); }
  to   { transform: translateY(3px); }
}

.atmos-vignette {
  position: absolute;
  inset: 0;
  opacity: var(--vignette-alpha);
  background: radial-gradient(ellipse at 50% 45%, transparent 38%, #000 118%);
  animation: vignettePulse 6s ease-in-out infinite;
}
@keyframes vignettePulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}

.atmos-bloom {
  position: absolute;
  inset: 0;
  opacity: var(--bloom-alpha);
  background: radial-gradient(ellipse at 50% 0%,
    color-mix(in srgb, var(--accent) 55%, transparent) 0%,
    transparent 58%);
}

/* Drifting light pools — size/position fixed, colour + strength themed */
.glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(var(--glow-blur));
  pointer-events: none;
}
.glow-a {
  width: 360px; height: 360px; top: -100px; left: -120px;
  background: var(--support);
  opacity: var(--glow-alpha);
  animation: driftA 22s ease-in-out infinite;
}
.glow-b {
  width: 320px; height: 320px; bottom: 10%; right: -140px;
  background: var(--support-2);
  opacity: calc(var(--glow-alpha) * 0.5);
  animation: driftB 26s ease-in-out infinite;
}
.glow-c {
  width: 280px; height: 280px; top: 30%; right: -80px;
  background: var(--accent);
  opacity: calc(var(--glow-alpha) * 0.62);
  animation: driftB 30s ease-in-out infinite reverse;
}
@keyframes driftA { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(30px, 40px); } }
@keyframes driftB { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(-25px, -30px); } }

/* ── 7. Particles ────────────────────────────────────────────
   One element, five personalities. atmosphere.js reads
   --particle-* and stamps the right class on each particle. */
.particles { position: absolute; inset: 0; }
.p {
  position: absolute;
  opacity: 0;
  will-change: transform, opacity;
}

/* ember — rises and burns out (reaper, inferno) */
.p-ember {
  border-radius: 50%;
  background: var(--p-color);
  box-shadow: 0 0 6px var(--p-color);
  animation: pRise var(--p-dur) ease-in var(--p-delay) infinite;
}
@keyframes pRise {
  0%   { opacity: 0; transform: translate(0, 0) scale(1); }
  15%  { opacity: var(--p-opacity); }
  85%  { opacity: calc(var(--p-opacity) * 0.3); }
  100% { opacity: 0; transform: translate(var(--p-dx), -190px) scale(0.3); }
}

/* star — hangs in space and twinkles (void) */
.p-star {
  border-radius: 50%;
  background: var(--p-color);
  box-shadow: 0 0 10px var(--p-color), 0 0 20px var(--p-color);
  animation: pTwinkle var(--p-dur) ease-in-out var(--p-delay) infinite;
}
@keyframes pTwinkle {
  0%, 100% { opacity: 0.05; transform: translate(0, 0) scale(0.7); }
  50%      { opacity: var(--p-opacity); transform: translate(var(--p-dx), -28px) scale(1.25); }
}

/* fleck — sharp, fast, jittery (toxic) */
.p-fleck {
  border-radius: 1px;
  background: var(--p-color);
  box-shadow: 0 0 4px var(--p-color);
  animation: pJitter var(--p-dur) steps(9, end) var(--p-delay) infinite;
}
@keyframes pJitter {
  0%   { opacity: 0; transform: translate(0, 0); }
  10%  { opacity: var(--p-opacity); }
  50%  { transform: translate(calc(var(--p-dx) * -1), -90px); }
  90%  { opacity: calc(var(--p-opacity) * 0.4); }
  100% { opacity: 0; transform: translate(var(--p-dx), -200px); }
}

/* mote — big, slow, warm dust (solar) */
.p-mote {
  border-radius: 50%;
  background: radial-gradient(circle, var(--p-color) 0%, transparent 72%);
  animation: pFloat var(--p-dur) ease-in-out var(--p-delay) infinite;
}
@keyframes pFloat {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.8); }
  25%  { opacity: var(--p-opacity); }
  75%  { opacity: calc(var(--p-opacity) * 0.6); }
  100% { opacity: 0; transform: translate(var(--p-dx), -150px) scale(1.4); }
}

/* shard — falls straight down, crisp (frost) */
.p-shard {
  border-radius: 0;
  background: var(--p-color);
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  animation: pFall var(--p-dur) linear var(--p-delay) infinite;
}
@keyframes pFall {
  0%   { opacity: 0; transform: translate(0, -20vh) rotate(0deg); }
  12%  { opacity: var(--p-opacity); }
  88%  { opacity: var(--p-opacity); }
  100% { opacity: 0; transform: translate(var(--p-dx), 110vh) rotate(180deg); }
}

/* ── 8. Reduced motion ───────────────────────────────────────
   Themes still change instantly and fully — only the motion goes. */
@media (prefers-reduced-motion: reduce) {
  :root { --theme-swap: 1ms; }
  .glow,
  .p,
  .atmos-scanlines,
  .atmos-vignette { animation: none !important; }
  .p { opacity: var(--p-opacity); }
  .theme-morph.run { animation: none !important; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }
  html.theme-swapping .type-settle { animation: none !important; }
}
