/** * Lumerel — 3D Homepage · main.js * * Responsibilities: * 1. Spline 3D scene pointer-events toggle + runtime loader * 2. Navbar scroll backdrop blur * 3. Background video autoplay fallback (iOS Safari) * 4. Waitlist button click hooks * 5. Public API exposed on window.Lumerel */ /* ── 1. Spline / 3D Layer Integration ───────────────────────────── * * When your Spline scene is ready, choose one of three methods: * * ── Method A (HTML — recommended) ───────────────────────────────── * Open index.html → find #spline-layer → delete .spline-placeholder * → paste in: * * * * ── Method B (JavaScript runtime) ───────────────────────────────── * Call from console or your code: * window.Lumerel.loadSplineScene('https://prod.spline.design/…') * * ── Method C (Interactive scenes) ───────────────────────────────── * After scene loads, enable pointer events: * window.Lumerel.enableSpline() * * The web component is already loaded via CDN * in the of index.html — no extra imports needed. * ──────────────────────────────────────────────────────────────────── */ const splineLayer = document.getElementById('spline-layer'); /** Allow pointer events on the 3D layer (for interactive Spline scenes). */ function enableSpline() { if (splineLayer) splineLayer.style.pointerEvents = 'auto'; } /** Block pointer events on the 3D layer (default — keeps UI clickable). */ function disableSpline() { if (splineLayer) splineLayer.style.pointerEvents = 'none'; } /** * Programmatically load a Spline scene URL at runtime. * Removes the placeholder orbs and inserts a . * @param {string} url - The full Spline scene URL * @param {boolean} [interactive=false] - Enable pointer events for interactive scenes */ function loadSplineScene(url, interactive = false) { if (!splineLayer) return; // Remove placeholder if present const placeholder = splineLayer.querySelector('.spline-placeholder'); if (placeholder) placeholder.remove(); // Create and configure the viewer const viewer = document.createElement('spline-viewer'); viewer.setAttribute('url', url); viewer.setAttribute('events-target', 'global'); viewer.style.cssText = 'width:100%;height:100%;display:block;'; viewer.addEventListener('load', () => { console.log('[Lumerel] ✓ Spline scene loaded:', url); if (interactive) enableSpline(); }); splineLayer.appendChild(viewer); console.log('[Lumerel] Loading Spline scene…'); } // Auto-detect if a was placed directly in index.html if (splineLayer) { const viewer = splineLayer.querySelector('spline-viewer'); if (viewer) { viewer.addEventListener('load', () => { console.log('[Lumerel] ✓ Spline scene loaded.'); // Uncomment to enable 3D mouse interaction after scene loads: // enableSpline(); }); } } /* ── 2. Navbar Scroll Backdrop ─────────────────────────────────── */ const navbar = document.getElementById('navbar'); function updateNavbar() { 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', updateNavbar, { passive: true }); updateNavbar(); // run once on load /* ── 3. Background Video Fallback ──────────────────────────────── */ const heroVideo = document.querySelector('.hero__video'); if (heroVideo) { // Gradient fallback if video fails to load heroVideo.addEventListener('error', () => { console.warn('[Lumerel] Background video failed — using gradient fallback.'); const wrap = heroVideo.closest('.hero__video-wrap'); if (wrap) { heroVideo.style.display = 'none'; wrap.style.background = 'radial-gradient(ellipse at 60% 40%, #1a0533 0%, #000000 70%)'; } }); // iOS Safari: force play on first user interaction if autoplay was blocked const forcePlay = () => { if (heroVideo.paused) { heroVideo.play().catch(() => {}); } document.removeEventListener('touchstart', forcePlay); document.removeEventListener('click', forcePlay); }; document.addEventListener('touchstart', forcePlay, { passive: true }); document.addEventListener('click', forcePlay, { once: true }); } /* ── 4. Waitlist Button Click Hooks ────────────────────────────── */ const waitlistBtns = document.querySelectorAll('.pill-btn'); waitlistBtns.forEach(btn => { btn.addEventListener('click', () => { // Hook your waitlist modal / form here. // Example: openWaitlistModal(); console.log('[Lumerel] Waitlist button clicked — connect your modal or form here.'); }); }); /* ── 5. Public API ─────────────────────────────────────────────── */ window.Lumerel = { /** * Load a Spline 3D scene at runtime. * @param {string} url - Spline scene URL * @param {boolean} [interactive=false] - Enable pointer events * @example window.Lumerel.loadSplineScene('https://prod.spline.design/…') */ loadSplineScene, /** * Enable pointer events on the 3D layer (for interactive Spline scenes). * @example window.Lumerel.enableSpline() */ enableSpline, /** * Disable pointer events on the 3D layer (restores default). * @example window.Lumerel.disableSpline() */ disableSpline, }; console.log('[Lumerel] ✓ Initialized. API available at window.Lumerel');