/* ======================================================= AION — 3D WebGL Chronographic Experience Engine ======================================================= */ let scrollWatchScene, scrollWatchCamera, scrollWatchRenderer; let watchGroup, caseMesh, bezelMesh, glassMesh, dialMesh, gearsGroup, powerCoil, backMesh, strapGroup; let gear1, gear2, gear3, hourHand, minuteHand, secondHand; let mouseX = 0; let mouseY = 0; document.addEventListener("DOMContentLoaded", () => { initEntranceScreen(); initLenisScroll(); initWatch3DScene(); initGearsThree(); // Caliber specs section canvas initHeroParallax(); initStackExplodedView(); initTechnicalHotspots(); initWatchConfigurator(); initContactFormSubmit(); // Handle cursor tracking for watch tilt window.addEventListener("mousemove", (e) => { mouseX = (e.clientX / window.innerWidth) - 0.5; mouseY = (e.clientY / window.innerHeight) - 0.5; }); }); /* ======================================================= 1. Entrance Screen Dismissal ======================================================= */ function initEntranceScreen() { const entryScreen = document.getElementById("entry-screen"); const btnEnter = document.getElementById("btn-enter-exp"); if (!entryScreen) return; if (btnEnter) { btnEnter.addEventListener("click", () => { if (typeof gsap !== "undefined") { gsap.to(entryScreen, { y: "-100%", duration: 1.4, ease: "power4.inOut", onComplete: () => { entryScreen.style.display = "none"; // Wake up hero text gsap.from("#hero .hero-title, #hero .hero-subheadline, #hero .hero-actions, #hero .hero-specs-row", { y: 45, opacity: 0, stagger: 0.15, duration: 1.2, ease: "power3.out" }); } }); } else { entryScreen.style.transition = "transform 0.8s ease-in-out"; entryScreen.style.transform = "translateY(-100%)"; setTimeout(() => { entryScreen.style.display = "none"; }, 800); } }); } } /* ======================================================= 2. Lenis Smooth Scroll ======================================================= */ function initLenisScroll() { if (typeof Lenis === "undefined" || typeof gsap === "undefined") return; const lenis = new Lenis({ duration: 1.4, easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), smoothWheel: true, }); lenis.on('scroll', ScrollTrigger.update); gsap.ticker.add((time) => { lenis.raf(time * 1000); }); gsap.ticker.lagSmoothing(0); } /* ======================================================= 3. True 3D Watch WebGL Scene (Central Pinned Scroll) ======================================================= */ function initWatch3DScene() { const canvas = document.getElementById("watch-3d-canvas"); if (!canvas) return; // Fallback if Three.js failed to load if (typeof THREE === "undefined") { const frame = document.querySelector(".watch-3d-canvas-frame"); if (frame) { frame.innerHTML = `AION Chrono`; } return; } // Init Scene scrollWatchScene = new THREE.Scene(); // Camera scrollWatchCamera = new THREE.PerspectiveCamera(40, canvas.clientWidth / canvas.clientHeight, 0.1, 100); scrollWatchCamera.position.set(0, 1.5, 12); // Renderer scrollWatchRenderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true, antialias: true }); scrollWatchRenderer.setSize(canvas.clientWidth, canvas.clientHeight); scrollWatchRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Lights const ambientLight = new THREE.AmbientLight(0xffffff, 0.2); scrollWatchScene.add(ambientLight); const dirLight1 = new THREE.DirectionalLight(0xffffff, 1.5); dirLight1.position.set(5, 10, 8); scrollWatchScene.add(dirLight1); const spotlight = new THREE.SpotLight(0xc9a86a, 2.5, 20, Math.PI / 6, 0.5, 1); spotlight.position.set(-6, 8, 6); scrollWatchScene.add(spotlight); const fillLight = new THREE.PointLight(0x1b2a38, 1.5, 15); fillLight.position.set(-4, -2, 4); scrollWatchScene.add(fillLight); // Group container watchGroup = new THREE.Group(); scrollWatchScene.add(watchGroup); // --- MODELING WATCH PARTS --- // Materials const steelMat = new THREE.MeshStandardMaterial({ color: 0xc9cdd2, roughness: 0.12, metalness: 0.95 }); const darkSteelMat = new THREE.MeshStandardMaterial({ color: 0x1a1c1e, roughness: 0.25, metalness: 0.92 }); const goldMat = new THREE.MeshStandardMaterial({ color: 0xc9a86a, roughness: 0.15, metalness: 0.88 }); const rubyMat = new THREE.MeshStandardMaterial({ color: 0xb33a31, roughness: 0.1, metalness: 0.3 }); const glassMat = new THREE.MeshPhysicalMaterial({ color: 0x44e5e7, transparent: true, opacity: 0.16, roughness: 0.05, transmission: 0.92, ior: 1.5, thickness: 0.15 }); // 1. Case Chassis const caseGeo = new THREE.CylinderGeometry(3.6, 3.6, 1.1, 64); caseMesh = new THREE.Mesh(caseGeo, darkSteelMat); watchGroup.add(caseMesh); // Crown const crownGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.6, 16); watchCrown = new THREE.Mesh(crownGeo, steelMat); watchCrown.rotation.z = Math.PI / 2; watchCrown.position.set(3.8, 0, 0); caseMesh.add(watchCrown); // Lugs / Strap connectors const lugGeo = new THREE.BoxGeometry(0.7, 0.4, 1.2); const lugTopL = new THREE.Mesh(lugGeo, darkSteelMat); lugTopL.position.set(-1.8, 0, -3.8); caseMesh.add(lugTopL); const lugTopR = new THREE.Mesh(lugGeo, darkSteelMat); lugTopR.position.set(1.8, 0, -3.8); caseMesh.add(lugTopR); const lugBotL = new THREE.Mesh(lugGeo, darkSteelMat); lugBotL.position.set(-1.8, 0, 3.8); caseMesh.add(lugBotL); const lugBotR = new THREE.Mesh(lugGeo, darkSteelMat); lugBotR.position.set(1.8, 0, 3.8); caseMesh.add(lugBotR); // 2. Bezel Ring const bezelGeo = new THREE.TorusGeometry(3.5, 0.16, 16, 64); bezelMesh = new THREE.Mesh(bezelGeo, steelMat); bezelMesh.rotation.x = Math.PI / 2; bezelMesh.position.y = 0.58; watchGroup.add(bezelMesh); // 3. Sapphire Crystal (Glass disc) const glassGeo = new THREE.CylinderGeometry(3.4, 3.4, 0.04, 32); glassMesh = new THREE.Mesh(glassGeo, glassMat); glassMesh.position.y = 0.68; watchGroup.add(glassMesh); // 4. Skeleton Dial dialMesh = new THREE.Mesh(new THREE.CylinderGeometry(3.3, 3.3, 0.05, 32), new THREE.MeshStandardMaterial({ color: 0x050505, roughness: 0.5 })); dialMesh.position.y = 0.38; watchGroup.add(dialMesh); // Dial golden index ticks const tickGeo = new THREE.BoxGeometry(0.08, 0.06, 0.32); for (let i = 0; i < 12; i++) { const tick = new THREE.Mesh(tickGeo, goldMat); const angle = (i / 12) * Math.PI * 2; tick.position.set(Math.cos(angle) * 2.9, 0.04, Math.sin(angle) * 2.9); tick.rotation.y = -angle + Math.PI / 2; dialMesh.add(tick); } // 5. Caliber Gears (Mechanical Caliber Cal. 01) gearsGroup = new THREE.Group(); gearsGroup.position.y = 0.08; watchGroup.add(gearsGroup); // Gear 1: Large Gold Wheel const g1Group = new THREE.Group(); g1Group.position.set(-0.9, 0, 0.8); gearsGroup.add(g1Group); gear1 = new THREE.Mesh(new THREE.CylinderGeometry(1.2, 1.2, 0.12, 32), goldMat); g1Group.add(gear1); for (let i = 0; i < 16; i++) { const t = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.15, 0.1), goldMat); const angle = (i / 16) * Math.PI * 2; t.position.set(Math.cos(angle) * 1.2, 0, Math.sin(angle) * 1.2); t.rotation.y = -angle; gear1.add(t); } // Gear 2: Steel Wheel const g2Group = new THREE.Group(); g2Group.position.set(0.9, 0, -0.6); gearsGroup.add(g2Group); gear2 = new THREE.Mesh(new THREE.CylinderGeometry(0.85, 0.85, 0.12, 32), steelMat); g2Group.add(gear2); for (let i = 0; i < 12; i++) { const t = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.15, 0.08), steelMat); const angle = (i / 12) * Math.PI * 2; t.position.set(Math.cos(angle) * 0.85, 0, Math.sin(angle) * 0.85); t.rotation.y = -angle; gear2.add(t); } // Gear 3: Red Escapement Wheel const g3Group = new THREE.Group(); g3Group.position.set(0.1, 0, -1.3); gearsGroup.add(g3Group); gear3 = new THREE.Mesh(new THREE.CylinderGeometry(0.5, 0.5, 0.1, 16), rubyMat); g3Group.add(gear3); // 6. Power Spiral Mainspring powerCoil = new THREE.Mesh(new THREE.TorusKnotGeometry(0.7, 0.04, 100, 4, 3, 4), rubyMat); powerCoil.rotation.x = Math.PI / 2; powerCoil.position.y = -0.2; watchGroup.add(powerCoil); // 7. Case Back Plate const backGeo = new THREE.CylinderGeometry(3.55, 3.55, 0.12, 64); backMesh = new THREE.Mesh(backGeo, darkSteelMat); backMesh.position.y = -0.62; watchGroup.add(backMesh); // 8. Strap Link Extensions strapGroup = new THREE.Group(); watchGroup.add(strapGroup); const strapGeo = new THREE.BoxGeometry(3.0, 0.15, 3.5); const strapTop = new THREE.Mesh(strapGeo, steelMat); strapTop.position.set(0, 0, -5.2); strapGroup.add(strapTop); const strapBot = new THREE.Mesh(strapGeo, steelMat); strapBot.position.set(0, 0, 5.2); strapGroup.add(strapBot); // Hands Group (ticking on dial) handsGroup = new THREE.Group(); handsGroup.position.y = 0.42; watchGroup.add(handsGroup); // Hour Hand (pivot at center) hourHand = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.04, 1.6), steelMat); hourHand.position.z = 0.8; handsGroup.add(hourHand); // Minute Hand minuteHand = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.04, 2.4), goldMat); minuteHand.position.z = 1.2; handsGroup.add(minuteHand); // Second Hand secondHand = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.02, 2.7), rubyMat); secondHand.position.z = 1.35; handsGroup.add(secondHand); // --- SCROLL ACTION TRIGGER --- const steps = document.querySelectorAll(".story-step"); if (typeof ScrollTrigger !== "undefined") { const tl3d = gsap.timeline({ scrollTrigger: { trigger: "#scroll-story", start: "top top", end: `+=${(steps.length - 1) * 115}%`, pin: true, scrub: 1.2, onUpdate: (self) => { const p = self.progress; // Target values let glassY = 0.68; let bezelY = 0.58; let dialY = 0.38; let handsY = 0.42; let gearsY = 0.08; let powerY = -0.2; let backY = -0.62; let strapZOffset = 0; let globalRotY = 0; let globalRotX = 0; if (p < 0.15) { // Step 1: Case rotation profile const ratio = p / 0.15; globalRotY = ratio * 1.3; globalRotX = ratio * 0.4; } else if (p < 0.82) { // Pushed states (Exploded watch) globalRotY = 1.3; globalRotX = 0.4; const sep = Math.min(1.0, (p - 0.15) / 0.5); // 0 to 1 glassY = 0.68 + sep * 5.2; bezelY = 0.58 + sep * 3.8; dialY = 0.38 + sep * 2.2; handsY = 0.42 + sep * 2.5; gearsY = 0.08 - sep * 1.4; powerY = -0.2 - sep * 3.0; backY = -0.62 - sep * 4.6; strapZOffset = sep * 1.8; } else { // Step 6: Collapse / Reassemble Watch const ratio = (1.0 - p) / 0.18; // 1 down to 0 globalRotY = ratio * 1.3; globalRotX = ratio * 0.4; glassY = 0.68 + ratio * 5.2; bezelY = 0.58 + ratio * 3.8; dialY = 0.38 + ratio * 2.2; handsY = 0.42 + ratio * 2.5; gearsY = 0.08 - ratio * 1.4; powerY = -0.2 - ratio * 3.0; backY = -0.62 - ratio * 4.6; strapZOffset = ratio * 1.8; } // Apply values with smooth GSAP mapping gsap.to(glassMesh.position, { y: glassY, duration: 0.1, overwrite: "auto" }); gsap.to(bezelMesh.position, { y: bezelY, duration: 0.1, overwrite: "auto" }); gsap.to(dialMesh.position, { y: dialY, duration: 0.1, overwrite: "auto" }); gsap.to(handsGroup.position, { y: handsY, duration: 0.1, overwrite: "auto" }); gsap.to(gearsGroup.position, { y: gearsY, duration: 0.1, overwrite: "auto" }); gsap.to(powerCoil.position, { y: powerY, duration: 0.1, overwrite: "auto" }); gsap.to(backMesh.position, { y: backY, duration: 0.1, overwrite: "auto" }); // Strap separation gsap.to(strapTop.position, { z: -5.2 - strapZOffset, duration: 0.1, overwrite: "auto" }); gsap.to(strapBot.position, { z: 5.2 + strapZOffset, duration: 0.1, overwrite: "auto" }); // Watch base rotation mapping gsap.to(watchGroup.rotation, { y: globalRotY, x: globalRotX, duration: 0.15, overwrite: "auto" }); } } }); // Update active text steps steps.forEach((step, i) => { ScrollTrigger.create({ trigger: "#scroll-story", start: () => `top+=${(i / steps.length) * window.innerHeight * 5 * 1.15} top`, end: () => `top+=${((i + 0.8) / steps.length) * window.innerHeight * 5 * 1.15} top`, onEnter: () => toggleStepActive(i), onEnterBack: () => toggleStepActive(i) }); }); } function toggleStepActive(idx) { steps.forEach((s, i) => s.classList.toggle("active", i === idx)); } // Animation ticks loop function tick() { requestAnimationFrame(tick); // Watch ticking (ticking gears) if (gear1) gear1.rotation.y += 0.005; if (gear2) gear2.rotation.y -= 0.007; if (gear3) gear3.rotation.y += 0.015; // Time ticking hands if (hourHand) hourHand.rotation.y -= 0.00015; if (minuteHand) minuteHand.rotation.y -= 0.0018; if (secondHand) secondHand.rotation.y -= 0.01; // Apply mouse parallax rotation if (watchGroup && (typeof ScrollTrigger !== "undefined" && !ScrollTrigger.isScrolling())) { watchGroup.rotation.y += (mouseX * 0.4 - watchGroup.rotation.y) * 0.05; watchGroup.rotation.x += (mouseY * 0.2 - watchGroup.rotation.x) * 0.05; } scrollWatchRenderer.render(scrollWatchScene, scrollWatchCamera); } tick(); window.addEventListener("resize", () => { if (!canvas.parentElement) return; scrollWatchCamera.aspect = canvas.clientWidth / canvas.clientHeight; scrollWatchCamera.updateProjectionMatrix(); scrollWatchRenderer.setSize(canvas.clientWidth, canvas.clientHeight); }); } /* ======================================================= 4. Three.js Caliber Specs Gear Section (Right-Side Visual) ======================================================= */ function initGearsThree() { if (typeof THREE === "undefined") return; const canvas = document.getElementById("gears-three-canvas"); if (!canvas) return; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 100); camera.position.z = 18; const renderer = new THREE.WebGLRenderer({ canvas: canvas, alpha: true, antialias: true }); renderer.setSize(canvas.clientWidth, canvas.clientHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Lights const ambientLight = new THREE.AmbientLight(0xffffff, 0.25); scene.add(ambientLight); const dirLight1 = new THREE.DirectionalLight(0xffffff, 0.85); dirLight1.position.set(5, 5, 8); scene.add(dirLight1); const dirLight2 = new THREE.DirectionalLight(0xc9a86a, 0.55); dirLight2.position.set(-5, -5, 5); scene.add(dirLight2); const steelMat = new THREE.MeshStandardMaterial({ color: 0x8e9399, roughness: 0.2, metalness: 0.95 }); const goldMat = new THREE.MeshStandardMaterial({ color: 0xc9a86a, roughness: 0.15, metalness: 0.9 }); const rubyMat = new THREE.MeshStandardMaterial({ color: 0xb33a31, roughness: 0.1, metalness: 0.2, transparent: true, opacity: 0.85 }); const gearsGroup = new THREE.Group(); scene.add(gearsGroup); function createCogWheel(radius, thickness, teethCount, material) { const cogGroup = new THREE.Group(); const coreGeo = new THREE.CylinderGeometry(radius * 0.9, radius * 0.9, thickness, 32); const coreMesh = new THREE.Mesh(coreGeo, material); coreMesh.rotation.x = Math.PI / 2; cogGroup.add(coreMesh); const toothGeo = new THREE.BoxGeometry(radius * 0.18, thickness * 1.05, radius * 0.15); for (let i = 0; i < teethCount; i++) { const angle = (i / teethCount) * Math.PI * 2; const tooth = new THREE.Mesh(toothGeo, material); tooth.position.set(Math.cos(angle) * radius, Math.sin(angle) * radius, 0); tooth.rotation.z = angle; cogGroup.add(tooth); } const rubyGeo = new THREE.CylinderGeometry(radius * 0.18, radius * 0.18, thickness * 1.15, 16); const ruby = new THREE.Mesh(rubyGeo, rubyMat); ruby.rotation.x = Math.PI / 2; cogGroup.add(ruby); return cogGroup; } const gearGold = createCogWheel(3.2, 0.4, 20, goldMat); gearGold.position.set(-2, 1, 0); gearsGroup.add(gearGold); const gearSteel = createCogWheel(2.2, 0.45, 14, steelMat); gearSteel.position.set(2.8, -0.2, 0); gearsGroup.add(gearSteel); const gearRuby = createCogWheel(1.4, 0.35, 10, goldMat); gearRuby.position.set(0.6, -2.6, 0); gearsGroup.add(gearRuby); function animate() { requestAnimationFrame(animate); gearGold.rotation.z += 0.006; gearSteel.rotation.z -= (20 / 14) * 0.006; gearRuby.rotation.z -= (20 / 10) * 0.006; gearsGroup.rotation.y = Math.sin(Date.now() * 0.0005) * 0.15; renderer.render(scene, camera); } animate(); window.addEventListener("resize", () => { if (!canvas.parentElement) return; camera.aspect = canvas.clientWidth / canvas.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.clientWidth, canvas.clientHeight); }); } /* ======================================================= 5. Hero Watch Mouse Parallax ======================================================= */ function initHeroParallax() { const heroSec = document.getElementById("hero"); const watchFrame = document.getElementById("hero-watch-frame"); if (!heroSec || !watchFrame) return; heroSec.addEventListener("mousemove", (e) => { const rect = heroSec.getBoundingClientRect(); const x = (e.clientX - rect.left) / rect.width - 0.5; const y = (e.clientY - rect.top) / rect.height - 0.5; if (typeof gsap !== "undefined") { gsap.to(watchFrame, { rotateY: x * 22, rotateX: -y * 22, translateX: x * 15, translateY: y * 15, duration: 0.6, ease: "power2.out" }); } else { watchFrame.style.transform = `rotateY(${x * 22}deg) rotateX(${-y * 22}deg) translate(${x * 15}px, ${y * 15}px)`; } }); heroSec.addEventListener("mouseleave", () => { if (typeof gsap !== "undefined") { gsap.to(watchFrame, { rotateX: 0, rotateY: 0, translateX: 0, translateY: 0, duration: 1, ease: "power3.out" }); } else { watchFrame.style.transform = "none"; } }); } /* ======================================================= 6. Exploded View Vertical Assembly Stack ======================================================= */ function initStackExplodedView() { const layers = document.querySelectorAll(".exp-layer"); const specsBox = document.getElementById("exploded-specs-box"); const titleEl = document.getElementById("exp-spec-title"); const descEl = document.getElementById("exp-spec-desc"); const btnReassemble = document.getElementById("btn-reassemble-stack"); if (!layers.length) return; layers.forEach(layer => { layer.addEventListener("mouseenter", () => { layers.forEach(l => l.classList.remove("active")); layer.classList.add("active"); const title = layer.dataset.title; const desc = layer.dataset.desc; if (typeof gsap !== "undefined" && specsBox) { gsap.to(specsBox, { opacity: 0, y: 8, duration: 0.2, onComplete: () => { titleEl.textContent = title; descEl.textContent = desc; gsap.to(specsBox, { opacity: 1, y: 0, duration: 0.3 }); } }); } else { titleEl.textContent = title; descEl.textContent = desc; } explodeStack(layer); }); }); function explodeStack(activeLayer) { const hoverIdx = Array.from(layers).indexOf(activeLayer); layers.forEach((layer, idx) => { let offset = 0; if (idx < hoverIdx) { offset = (idx - hoverIdx) * 35 - 30; } else if (idx > hoverIdx) { offset = (idx - hoverIdx) * 35 + 30; } else { offset = 0; } if (typeof gsap !== "undefined") { gsap.to(layer, { y: offset, scale: idx === hoverIdx ? 1.08 : 0.95, duration: 0.4, ease: "power2.out", overwrite: "auto" }); } else { layer.style.transform = `translateY(${offset}px) scale(${idx === hoverIdx ? 1.08 : 0.95})`; } }); } if (btnReassemble) { btnReassemble.addEventListener("click", () => { layers.forEach((layer, idx) => { const defaultOffset = idx * -10; layers.forEach(l => l.classList.remove("active")); if (typeof gsap !== "undefined") { gsap.to(layer, { y: defaultOffset, scale: 1, duration: 0.8, ease: "power3.out" }); } else { layer.style.transform = `translateY(${defaultOffset}px) scale(1)`; } }); titleEl.textContent = "Select Component..."; descEl.textContent = "Hover over any layer in the assembly stack to examine its metallurgical and mechanical properties."; }); } } /* ======================================================= 7. Luminescent Hotspots ======================================================= */ function initTechnicalHotspots() { const dots = document.querySelectorAll(".hotspot-dot"); const descCard = document.getElementById("hotspot-desc-card"); const titleEl = document.getElementById("hotspot-title"); const descEl = document.getElementById("hotspot-desc"); dots.forEach(dot => { dot.addEventListener("mouseenter", () => { const title = dot.dataset.title; const desc = dot.dataset.desc; if (typeof gsap !== "undefined" && descCard) { gsap.to(descCard, { opacity: 0, y: 8, duration: 0.25, onComplete: () => { titleEl.textContent = title; descEl.textContent = desc; gsap.to(descCard, { opacity: 1, y: 0, duration: 0.35 }); } }); } else { titleEl.textContent = title; descEl.textContent = desc; } }); }); } /* ======================================================= 8. Material Explorer & Configurator ======================================================= */ function initWatchConfigurator() { const buttons = document.querySelectorAll(".opt-btn"); const summaryTxt = document.getElementById("summary-text"); const saveBtn = document.getElementById("btn-save-config"); const watchStrapTop = document.getElementById("watch-strap-top"); const watchStrapBottom = document.getElementById("watch-strap-bottom"); const watchCase = document.getElementById("watch-case"); const watchCrown = document.getElementById("watch-crown"); const watchBezel = document.getElementById("watch-bezel"); const watchDial = document.getElementById("watch-dial"); const configState = { case: "Brushed Steel", dial: "Obsidian Black", strap: "Steel Link" }; buttons.forEach(btn => { btn.addEventListener("click", () => { const type = btn.dataset.type; const value = btn.dataset.value; btn.parentElement.querySelectorAll(".opt-btn").forEach(b => b.classList.remove("active")); btn.classList.add("active"); if (type === "case") { if (value === "brushed-steel") { configState.case = "Brushed Steel"; setSVGAttribute([watchCase, watchCrown, watchBezel], "fill", "url(#metal-steel)"); } else if (value === "black-titanium") { configState.case = "Black Titanium"; setSVGAttribute([watchCase, watchCrown, watchBezel], "fill", "url(#metal-titanium)"); } else if (value === "warm-bronze") { configState.case = "Warm Bronze"; setSVGAttribute([watchCase, watchCrown, watchBezel], "fill", "url(#metal-bronze)"); } } if (type === "dial") { if (value === "obsidian") { configState.dial = "Obsidian Black"; setSVGAttribute(watchDial, "fill", "url(#dial-obsidian)"); } else if (value === "royal-blue") { configState.dial = "Royal Blue"; setSVGAttribute(watchDial, "fill", "url(#dial-blue)"); } else if (value === "forest-green") { configState.dial = "Forest Green"; setSVGAttribute(watchDial, "fill", "url(#dial-green)"); } } if (type === "strap") { if (value === "steel-link") { configState.strap = "Steel Link"; setSVGAttribute([watchStrapTop, watchStrapBottom], "fill", "url(#strap-steel)"); } else if (value === "black-leather") { configState.strap = "Black Leather"; setSVGAttribute([watchStrapTop, watchStrapBottom], "fill", "url(#strap-black)"); } else if (value === "brown-leather") { configState.strap = "Brown Leather"; setSVGAttribute([watchStrapTop, watchStrapBottom], "fill", "url(#strap-brown)"); } } if (summaryTxt) { summaryTxt.textContent = `${configState.case} / ${configState.dial} / ${configState.strap}`; } }); }); function setSVGAttribute(elements, attr, value) { if (!elements) return; if (Array.isArray(elements)) { elements.forEach(el => { if (el) el.setAttribute(attr, value); }); } else { if (elements) elements.setAttribute(attr, value); } } if (saveBtn) { saveBtn.addEventListener("click", () => { saveBtn.disabled = true; saveBtn.textContent = "Saving Watch Configuration..."; setTimeout(() => { saveBtn.disabled = false; saveBtn.textContent = "✓ Configuration Saved"; const modal = document.createElement("div"); modal.className = "config-modal-overlay"; modal.innerHTML = `

Reloj Configurado

Tu diseño de reloj personalizado de AION ha sido archivado exitosamente:

Caja: ${configState.case}
Esfera: ${configState.dial}
Correa: ${configState.strap}

`; const style = document.createElement("style"); style.innerHTML = ` .config-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(5,5,5,0.96); display: flex; align-items: center; justify-content: center; z-index: 10000; opacity: 0; transition: opacity 0.3s ease; } .success-box { background: #111214; border: 1px solid var(--color-gold); padding: 48px; border-radius: 8px; text-align: center; max-width: 480px; box-shadow: 0 30px 60px rgba(0,0,0,0.8); color: #fff; } .success-icon { width: 50px; height: 50px; background: var(--color-gold); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; color: #000; margin: 0 auto 24px auto; } .success-box h2 { font-family: 'Cormorant Garamond', serif; font-size: 30px; margin-bottom: 16px; font-weight: 300; } .success-message { font-size: 13.5px; color: rgba(255,255,255,0.7); line-height: 1.6; margin-bottom: 20px; } .config-details { background: #050505; border: 1px solid var(--color-border); padding: 16px; font-family: monospace; font-size: 12px; text-align: left; margin-bottom: 32px; line-height: 1.8; } .config-details strong { color: var(--color-gold); } .close-config-btn { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,0.2); padding: 12px 30px; text-transform: uppercase; font-size: 11px; font-weight: bold; cursor: pointer; transition: all 0.3s ease; } .close-config-btn:hover { border-color: var(--color-gold); color: var(--color-gold); background: rgba(201, 168, 106, 0.05); } `; document.head.appendChild(style); document.body.appendChild(modal); setTimeout(() => { modal.style.opacity = "1"; }, 50); modal.querySelector(".close-config-btn").addEventListener("click", () => { modal.style.opacity = "0"; setTimeout(() => { modal.remove(); style.remove(); saveBtn.textContent = "Save Configuration"; }, 300); }); }, 1400); }); } } /* ======================================================= 9. Request Watch Configuration Form Submit ======================================================= */ function initContactFormSubmit() { const form = document.getElementById("contact-form"); const submitBtn = document.getElementById("submit-btn"); if (!form) return; form.addEventListener("submit", (e) => { e.preventDefault(); const name = document.getElementById("name").value; const email = document.getElementById("email").value; if (!name || !email) return; submitBtn.disabled = true; submitBtn.textContent = "Sending Request..."; setTimeout(() => { submitBtn.textContent = "✓ Request Sent"; const modal = document.createElement("div"); modal.className = "contact-success-overlay"; modal.innerHTML = `

¡Solicitud Recibida!

Hola ${name}, hemos guardado tus notas de personalización para la adquisición de AION.

Recibirás los detalles de fabricación numerada en ${email}.

`; const style = document.createElement("style"); style.innerHTML = ` .contact-success-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(5,5,5,0.96); display: flex; align-items: center; justify-content: center; z-index: 10000; opacity: 0; transition: opacity 0.3s ease; } .success-box { background: #111214; border: 1px solid var(--color-gold); padding: 48px; border-radius: 8px; text-align: center; max-width: 480px; box-shadow: 0 30px 60px rgba(0,0,0,0.8); color: #fff; } .success-icon { width: 50px; height: 50px; background: var(--color-gold); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; color: #000; margin: 0 auto 24px auto; } .success-box h2 { font-family: 'Cormorant Garamond', serif; font-size: 30px; margin-bottom: 16px; font-weight: 300; } .success-message { font-size: 13.5px; color: rgba(255,255,255,0.7); line-height: 1.6; margin-bottom: 12px; } .success-sub { font-size: 11.5px; color: rgba(255,255,255,0.4); margin-bottom: 32px; } .close-success-btn { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,0.2); padding: 12px 30px; text-transform: uppercase; font-size: 11px; font-weight: bold; cursor: pointer; transition: all 0.3s ease; } .close-success-btn:hover { border-color: var(--color-gold); color: var(--color-gold); background: rgba(201, 168, 106, 0.05); } `; document.head.appendChild(style); document.body.appendChild(modal); setTimeout(() => { modal.style.opacity = "1"; }, 50); modal.querySelector(".close-success-btn").addEventListener("click", () => { modal.style.opacity = "0"; setTimeout(() => { modal.remove(); style.remove(); form.reset(); submitBtn.disabled = false; submitBtn.textContent = "Request Watch Configuration"; }, 300); }); }, 1500); }); }