// SSR -> CSR hydration gate. Fades the WebP placeholder to the Three.js canvas // ONLY after BOTH the WebGL context is ready AND the model is loaded. // Firing on just one condition = empty-canvas flash (race). Idempotent. export interface HydrationGate { markWebGLReady(): void; markModelLoaded(): void; } export function createHydrationGate(preview: HTMLImageElement): HydrationGate { let webglReady = false; let modelLoaded = false; let fired = false; function maybeFire(): void { if (fired || !(webglReady && modelLoaded)) return; fired = true; preview.style.opacity = '0'; window.setTimeout(() => { preview.classList.add('hidden'); preview.style.display = 'none'; }, 500); } return { markWebGLReady() { webglReady = true; maybeFire(); }, markModelLoaded() { modelLoaded = true; maybeFire(); }, }; }