/** * REBOUR — Main Script */ document.addEventListener('DOMContentLoaded', () => { // ---- CUSTOM CURSOR ---- const cursorDot = document.querySelector('.cursor-dot'); const cursorOutline = document.querySelector('.cursor-outline'); let mouseX = 0, mouseY = 0; let outlineX = 0, outlineY = 0; let rafId = null; window.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; // Première fois : initialise l'outline à la position courante et rend visible if (outlineX === 0 && outlineY === 0) { outlineX = mouseX; outlineY = mouseY; cursorDot.style.opacity = '1'; cursorOutline.style.opacity = '1'; } cursorDot.style.transform = `translate(calc(-50% + ${mouseX}px), calc(-50% + ${mouseY}px))`; if (!rafId) rafId = requestAnimationFrame(animateOutline); }, { once: false }); function animateOutline() { rafId = null; outlineX += (mouseX - outlineX) * 0.18; outlineY += (mouseY - outlineY) * 0.18; cursorOutline.style.transform = `translate(calc(-50% + ${outlineX}px), calc(-50% + ${outlineY}px))`; if (Math.abs(mouseX - outlineX) > 0.1 || Math.abs(mouseY - outlineY) > 0.1) { rafId = requestAnimationFrame(animateOutline); } } function attachCursorHover(elements) { elements.forEach(el => { el.addEventListener('mouseenter', () => { cursorOutline.style.width = '38px'; cursorOutline.style.height = '38px'; cursorDot.style.opacity = '0'; }); el.addEventListener('mouseleave', () => { cursorOutline.style.width = '26px'; cursorOutline.style.height = '26px'; cursorDot.style.opacity = '1'; }); }); } attachCursorHover(document.querySelectorAll('a, button, input, .product-card, summary, .panel-close')); // ---- INTERACTIVE GRID ---- const gridContainer = document.getElementById('interactive-grid'); const CELL = 60; const COLORS = [ 'rgba(160,160,155,0.3)', 'rgba(140,140,135,0.22)', 'rgba(120,120,115,0.18)', ]; function buildGrid() { if (!gridContainer) return; gridContainer.innerHTML = ''; const cols = Math.ceil(window.innerWidth / CELL); const rows = Math.ceil(window.innerHeight / CELL); gridContainer.style.display = 'grid'; gridContainer.style.gridTemplateColumns = `repeat(${cols}, ${CELL}px)`; gridContainer.style.gridTemplateRows = `repeat(${rows}, ${CELL}px)`; for (let i = 0; i < cols * rows; i++) { const cell = document.createElement('div'); cell.className = 'grid-cell'; cell.addEventListener('mouseenter', () => { cell.style.transition = 'none'; cell.style.backgroundColor = COLORS[Math.floor(Math.random() * COLORS.length)]; }); cell.addEventListener('mouseleave', () => { cell.style.transition = 'background-color 1.4s ease-out'; cell.style.backgroundColor = 'transparent'; }); gridContainer.appendChild(cell); } } buildGrid(); let rt; window.addEventListener('resize', () => { clearTimeout(rt); rt = setTimeout(buildGrid, 150); }); // ---- PRODUCT PANEL ---- const panel = document.getElementById('product-panel'); const panelClose = document.getElementById('panel-close'); const cards = document.querySelectorAll('.product-card'); // Champs du panel const fields = { img: document.getElementById('panel-img'), index: document.getElementById('panel-index'), name: document.getElementById('panel-name'), type: document.getElementById('panel-type'), mat: document.getElementById('panel-mat'), year: document.getElementById('panel-year'), status: document.getElementById('panel-status'), desc: document.getElementById('panel-desc'), specs: document.getElementById('panel-specs'), notes: document.getElementById('panel-notes'), }; // ---- CHECKOUT LOGIC ---- const checkoutSection = document.getElementById('checkout-section'); const checkoutToggleBtn = document.getElementById('checkout-toggle-btn'); const checkoutFormWrap = document.getElementById('checkout-form-wrap'); const checkoutForm = document.getElementById('checkout-form'); const checkoutSubmitBtn = document.getElementById('checkout-submit-btn'); // Toggle affichage du form checkoutToggleBtn.addEventListener('click', () => { const isOpen = checkoutFormWrap.style.display !== 'none'; checkoutFormWrap.style.display = isOpen ? 'none' : 'block'; checkoutToggleBtn.textContent = isOpen ? '[ COMMANDER CETTE PIÈCE ]' : '[ ANNULER ]'; }); // Submit → appel API Elysia → redirect Stripe checkoutForm.addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('checkout-email').value; checkoutSubmitBtn.disabled = true; checkoutSubmitBtn.textContent = 'CONNEXION STRIPE...'; try { const res = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product: 'lumiere_orbitale', email }), }); const data = await res.json(); if (data.url) { window.location.href = data.url; } else { throw new Error('No URL returned'); } } catch (err) { checkoutSubmitBtn.disabled = false; checkoutSubmitBtn.textContent = 'ERREUR — RÉESSAYER'; console.error(err); } }); function openPanel(card) { fields.img.src = card.dataset.img; fields.img.alt = card.dataset.name; fields.index.textContent = card.dataset.index; fields.name.textContent = card.dataset.name; fields.type.textContent = card.dataset.type; fields.mat.textContent = card.dataset.mat; fields.year.textContent = card.dataset.year; fields.status.textContent = card.dataset.status; fields.desc.textContent = card.dataset.desc; fields.specs.textContent = card.dataset.specs; fields.notes.textContent = card.dataset.notes; // Affiche le bouton de commande uniquement pour PROJET_001 const isOrderable = card.dataset.index === 'PROJET_001'; checkoutSection.style.display = isOrderable ? 'block' : 'none'; // Reset form state checkoutFormWrap.style.display = 'none'; checkoutToggleBtn.textContent = '[ COMMANDER CETTE PIÈCE ]'; checkoutSubmitBtn.disabled = false; checkoutSubmitBtn.textContent = 'PROCÉDER AU PAIEMENT →'; checkoutForm.reset(); // Ferme les accordéons panel.querySelectorAll('details').forEach(d => d.removeAttribute('open')); panel.classList.add('is-open'); panel.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; // Refresh cursor sur les nouveaux éléments attachCursorHover(panel.querySelectorAll('summary, .panel-close, .checkout-btn, .checkout-submit')); } function closePanel() { panel.classList.remove('is-open'); panel.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; } cards.forEach(card => { card.addEventListener('click', () => openPanel(card)); }); panelClose.addEventListener('click', closePanel); // Echap pour fermer document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePanel(); }); });