/** * REBOUR — Main Script */ document.addEventListener('DOMContentLoaded', () => { // ---- HEADER HEIGHT → CSS VAR ---- const setHeaderHeight = () => { const h = document.querySelector('.header')?.offsetHeight || 44; document.documentElement.style.setProperty('--header-h', h + 'px'); }; setHeaderHeight(); window.addEventListener('resize', setHeaderHeight); // ---- 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); } } let hoverCount = 0; function attachCursorHover(elements) { elements.forEach(el => { el.addEventListener('mouseenter', () => { cursorOutline.style.width = '38px'; cursorOutline.style.height = '38px'; }); el.addEventListener('mouseleave', () => { cursorOutline.style.width = '26px'; cursorOutline.style.height = '26px'; }); }); } 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(232,168,0,0.45)', 'rgba(232,168,0,0.32)', 'rgba(232,168,0,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); } }); // Slug à partir du nom du produit : "Solar_Altar" → "lumiere-orbitale" function toSlug(name) { return name .toLowerCase() .normalize('NFD').replace(/[\u0300-\u036f]/g, '') .replace(/_/g, '-') .replace(/[^a-z0-9-]/g, ''); } function openPanel(card, pushState = true) { 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')); // Mise à jour de l'URL if (pushState) { const slug = toSlug(card.dataset.name); history.pushState({ slug }, '', `/collection/${slug}`); } } function closePanel(pushState = true) { panel.classList.remove('is-open'); panel.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; if (pushState) { history.pushState({}, '', '/'); } } cards.forEach(card => { card.addEventListener('click', () => openPanel(card)); }); // Ouverture automatique si on arrive directement sur /collection/[slug] if (window.__OPEN_PANEL__) { const name = window.__OPEN_PANEL__; const card = [...cards].find(c => c.dataset.name === name); if (card) openPanel(card, false); } panelClose.addEventListener('click', () => closePanel()); // Echap pour fermer document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePanel(); }); // Bouton retour navigateur window.addEventListener('popstate', () => { if (panel.classList.contains('is-open')) { closePanel(false); } else { // Tente de rouvrir si on navigue vers un slug connu const match = location.pathname.match(/^\/collection\/(.+)$/); if (match) { const slug = match[1]; const card = [...cards].find(c => toSlug(c.dataset.name) === slug); if (card) openPanel(card, false); } } }); });