const { useEffect, useRef } = React; function FadingVideo({ src, className, style }) { const videoRef = useRef(null); const rafRef = useRef(null); const timeoutRef = useRef(null); const fadingOutRef = useRef(false); const FADE_MS = 500; const FADE_OUT_LEAD = 0.55; useEffect(() => { const video = videoRef.current; if (!video) return; const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const fadeTo = (target, duration = FADE_MS) => { if (rafRef.current) cancelAnimationFrame(rafRef.current); const startOpacity = Number.parseFloat(video.style.opacity || "0"); const start = performance.now(); const tick = (now) => { const progress = Math.min((now - start) / duration, 1); video.style.opacity = String(startOpacity + (target - startOpacity) * progress); if (progress < 1) rafRef.current = requestAnimationFrame(tick); }; rafRef.current = requestAnimationFrame(tick); }; const onLoadedData = () => { video.style.opacity = "0"; if (reducedMotion) { video.style.opacity = "1"; video.pause(); return; } video.play().catch(() => {}); fadeTo(1); }; const onTimeUpdate = () => { if (reducedMotion) return; const remaining = video.duration - video.currentTime; if (!fadingOutRef.current && remaining <= FADE_OUT_LEAD && remaining > 0) { fadingOutRef.current = true; fadeTo(0); } }; const onEnded = () => { if (reducedMotion) return; video.style.opacity = "0"; timeoutRef.current = setTimeout(() => { video.currentTime = 0; video.play().catch(() => {}); fadingOutRef.current = false; fadeTo(1); }, 100); }; video.addEventListener("loadeddata", onLoadedData); video.addEventListener("timeupdate", onTimeUpdate); video.addEventListener("ended", onEnded); return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); if (timeoutRef.current) clearTimeout(timeoutRef.current); video.removeEventListener("loadeddata", onLoadedData); video.removeEventListener("timeupdate", onTimeUpdate); video.removeEventListener("ended", onEnded); }; }, [src]); return