import React, { useMemo } from 'react'; import { SeasonalOverlayProps } from '../types'; import { animMoonPulse, animFogDrift, animBatGlide, animWingFlap, animEmberFloat, animEmberTwinkle, } from './Halloween.css'; // ─── Palette (oklch) ────────────────────────────────────────────────────────── // Deep haunted indigo, sickly toxic-green moon glow, warm ember orange. const PURPLE_DEEP = 'oklch(0.20 0.12 300)'; const PURPLE_FAINT = 'oklch(0.28 0.10 300 / 0.45)'; const TOXIC_GREEN = 'oklch(0.80 0.18 150)'; const TOXIC_GREEN_SOFT = 'oklch(0.72 0.16 150 / 0.35)'; const EMBER_ORANGE = 'oklch(0.70 0.18 50)'; const FOG_TINT = 'oklch(0.45 0.06 280 / 0.32)'; // A corner cobweb, drawn once as an inline SVG data-URI (CSP-safe, no assets). // strokeWidth kept hairline so it reads as gossamer thread, not a cage. const cobwebUri = (() => { const svg = `` + `` + // radial threads `` + `` + `` + `` + `` + // concentric catch-threads (gentle sag via quadratic curves) `` + `` + `` + `` + `` + ``; return `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}")`; })(); // A single silhouetted bat, inline SVG. Wings are separate so the wrapper can // glide while an inner element flaps independently — we re-use one body shape. function BatSilhouette() { return ( ); } export function HalloweenOverlay({ reduced }: SeasonalOverlayProps) { // Deterministic per-mount generation — never per-frame React state. const embers = useMemo( () => Array.from({ length: 12 }, (_, i) => { const green = i % 3 === 0; // ~1/3 toxic-green wisps, rest warm embers return { left: (i * 6151 + 113) % 100, bottom: (i * 3137 + 47) % 28, // start near floor size: 3 + (i % 4), duration: 11 + (i % 6) * 2.2, delay: (i * 0.83) % 11, twinkle: 2.4 + (i % 5) * 0.6, color: green ? TOXIC_GREEN : EMBER_ORANGE, }; }), [], ); const bats = useMemo( () => Array.from({ length: 3 }, (_, i) => ({ top: 8 + i * 13, duration: 22 + i * 7, delay: i * 6.5, flap: 0.5 + i * 0.12, scale: 0.7 + i * 0.18, })), [], ); const fogBands = useMemo( () => Array.from({ length: 3 }, (_, i) => ({ bottom: -6 + i * 9, duration: 26 + i * 8, delay: i * 5, height: 130 + i * 30, })), [], ); return ( <> {/* ── Sky: layered indigo→black gradient with toxic-green moon vignette ── */}