/** * Lumerel — 3D Homepage * main.js * * Handles: * - Spline 3D scene loading & pointer-events toggle * - Navbar scroll behaviour (frosted glass on scroll) * - Video autoplay fallback (iOS / autoplay-blocked browsers) * - Waitlist button click handlers * - Global helpers exposed on window.Lumerel */ /* ── Spline Integration ──────────────────────────────────────────────── * * WHEN YOUR SPLINE SCENE IS READY: * * Option A — HTML (simplest): * Open index.html, find #spline-layer, delete .spline-placeholder, * and paste in: * * * * Option B — JavaScript: * Call window.Lumerel.loadSplineScene('https://prod.spline.design/…'); * This removes the placeholder and injects for you. * * Option C — @splinetool/runtime (advanced): * import { Application } from '@splinetool/runtime'; * const app = new Application(canvas); * app.load('https://prod.spline.design/YOUR_SCENE_ID/scene.splinecode'); * * For interactive scenes (mouse/touch on the 3D layer), call: * window.Lumerel.enableSpline() * * ────────────────────────────────────────────────────────────────────── */ const splineLayer = document.getElementById('spline-layer'); /** * Enable pointer events on the 3D layer so Spline scenes * can receive mouse/touch input. * Call this AFTER your Spline scene has loaded. */ function enableSpline() { if (splineLayer) { splineLayer.style.pointerEvents = 'auto'; console.log('[Lumerel] Spline pointer events ENABLED.'); } } /** * Disable pointer events on the 3D layer (default state). * Ensures all UI elements (buttons, links) remain fully clickable. */ function disableSpline() { if (splineLayer) { splineLayer.style.pointerEvents = 'none'; console.log('[Lumerel] Spline pointer events DISABLED.'); } } /** * Programmatically load a Spline scene by URL. * Removes the placeholder orbs and injects a . * @param {string} sceneUrl - The Spline scene URL (.splinecode) * @param {boolean} [enableInteraction=false] - Auto-enable pointer events */ function loadSplineScene(sceneUrl, enableInteraction = false) { if (!splineLayer) return; // Remove placeholder const placeholder = splineLayer.querySelector('.spline-placeholder'); if (placeholder) placeholder.remove(); // Inject spline-viewer const viewer = document.createElement('spline-viewer'); viewer.setAttribute('url', sceneUrl); viewer.setAttribute('events-target', 'global'); viewer.style.width = '100%'; viewer.style.height = '100%'; viewer.style.display = 'block'; viewer.addEventListener('load', () => { console.log('[Lumerel] Spline scene loaded:', sceneUrl); if (enableInteraction) enableSpline(); }); splineLayer.appendChild(viewer); console.log('[Lumerel] Spline scene injected:', sceneUrl); } // Listen for spline-viewer ready event if already in HTML if (splineLayer) { const existingViewer = splineLayer.querySelector('spline-viewer'); if (existingViewer) { existingViewer.addEventListener('load', () => { console.log('[Lumerel] Spline scene loaded.'); // Uncomment to auto-enable 3D interactivity: // enableSpline(); }); } } /* ── Navbar Scroll Effect ─────────────────────────────────────────────── Adds a frosted-glass background when the user scrolls past 24px. ─────────────────────────────────────────────────────────────────────── */ const navbar = document.getElementById('navbar'); function handleNavbarScroll() { if (!navbar) return; if (window.scrollY > 24) { navbar.style.background = 'rgba(0, 0, 0, 0.45)'; navbar.style.backdropFilter = 'blur(16px)'; navbar.style.webkitBackdropFilter = 'blur(16px)'; } else { navbar.style.background = 'transparent'; navbar.style.backdropFilter = 'none'; navbar.style.webkitBackdropFilter = 'none'; } } window.addEventListener('scroll', handleNavbarScroll, { passive: true }); handleNavbarScroll(); // Run on load /* ── Video Autoplay Fallback ──────────────────────────────────────────── Some browsers block autoplay even with muted. This retries on user interaction if the video fails to start automatically. ─────────────────────────────────────────────────────────────────────── */ const heroVideo = document.querySelector('.hero__video'); if (heroVideo) { const playVideo = () => { heroVideo.play().catch(() => { // Autoplay still blocked — video stays paused, page still works }); }; heroVideo.addEventListener('canplaythrough', playVideo, { once: true }); // Retry on first user gesture (for strict autoplay policies) const retryOnInteraction = () => { playVideo(); document.removeEventListener('click', retryOnInteraction); document.removeEventListener('touchstart', retryOnInteraction); }; if (heroVideo.paused) { document.addEventListener('click', retryOnInteraction, { once: true }); document.addEventListener('touchstart', retryOnInteraction, { once: true }); } } /* ── Waitlist Button Handlers ─────────────────────────────────────────── Replace the console.log with your modal, form, or redirect logic. ─────────────────────────────────────────────────────────────────────── */ function handleWaitlistClick(source) { console.log(`[Lumerel] Waitlist CTA clicked — source: ${source}`); // TODO: Open modal, redirect, or trigger waitlist form // Example: window.location.href = 'https://waitlist.lumerel.com'; // Example: document.getElementById('waitlist-modal').showModal(); } const navbarCta = document.getElementById('navbar-cta'); const heroCta = document.getElementById('hero-cta'); if (navbarCta) navbarCta.addEventListener('click', () => handleWaitlistClick('navbar')); if (heroCta) heroCta.addEventListener('click', () => handleWaitlistClick('hero')); /* ── Global API ───────────────────────────────────────────────────────── Expose helpers so you can call them from the browser console or from other scripts after the page loads. ─────────────────────────────────────────────────────────────────────── */ window.Lumerel = { loadSplineScene, enableSpline, disableSpline, }; console.log( '%c LUMEREL ', 'background:#000;color:#fff;font-weight:700;font-size:13px;padding:4px 8px;border:1px solid rgba(255,255,255,0.3);border-radius:4px;', '— 3D Homepage ready. window.Lumerel API available.' );