// Main page composition: hero, video grids, instructor, schedule, FAQ, footer. const { buildLots, computeLotState, fmtPrice, fmtDateBR, LotProgressBar, CTAProgress } = window; // ── Reusable bits ─────────────────────────────────────────────────────────── function AutoVideo({ src, poster, className = '', style, muted = true, loop = true, autoPlay = true, playsInline = true, controls = false, eager = false }) { const ref = React.useRef(null); const [inView, setInView] = React.useState(eager); // Observe viewport intersection — only load/play when near viewport React.useEffect(() => { if (eager) { setInView(true); return; } const el = ref.current; if (!el) return; const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { setInView(e.isIntersecting); }); }, { rootMargin: '200px 0px', threshold: 0.01 } ); io.observe(el); return () => io.disconnect(); }, [eager]); // Attach src + play only when in view; detach when out of view React.useEffect(() => { const video = ref.current; if (!video || !src) return; if (!inView) { // Free memory when not visible try { video.pause(); } catch(e) {} if (video.src) { video.removeAttribute('src'); try { video.load(); } catch(e) {} } return; } const tryPlay = () => { if (!autoPlay) return; const p = video.play(); if (p && p.catch) p.catch(() => {}); }; let cleanup; if (src.endsWith('.m3u8') || src.includes('.m3u8?')) { if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = src; video.addEventListener('loadedmetadata', tryPlay, { once: true }); } else if (window.Hls && window.Hls.isSupported()) { const hls = new window.Hls({ capLevelToPlayerSize: true, autoStartLoad: true }); hls.loadSource(src); hls.attachMedia(video); hls.on(window.Hls.Events.MANIFEST_PARSED, tryPlay); cleanup = () => hls.destroy(); } } else { video.src = src; video.addEventListener('loadedmetadata', tryPlay, { once: true }); } const delayed = setTimeout(tryPlay, 300); return () => { clearTimeout(delayed); if (cleanup) cleanup(); }; }, [src, autoPlay, inView]); return ( ); } // Stripe placeholder (for when we don't have a video yet) function VideoPlaceholder({ label, ratio = '16 / 9' }) { return (
2 dias inteiros criando um projeto do zero de um dos tipos de vídeo de IA mais valorizados do mercado. Animação a nível profissional.