Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- 1. Chat Simulation Logic --- | |
| const chatContainer = document.getElementById('chat-display'); | |
| const chatData = { | |
| 'ru-RU': [ | |
| { sender: 'customer', text: 'Привет, есть iPhone 15 Pro Max в наличии?' }, | |
| { sender: 'bot', text: 'Привет! Да, iPhone 15 Pro Max в наличии. Есть варианты в цвете Natural Titanium и Blue Titanium с памятью 256GB и 512GB. Отправим сегодня.' }, | |
| { sender: 'customer', text: 'Отлично, давай оформим заказ.' }, | |
| { sender: 'bot', text: 'Принято. Подтверждаю заказ на iPhone 15 Pro Max. Напишите, какой цвет и объем памяти выбрать?' }, | |
| { sender: 'typing' } | |
| ], | |
| 'en-US': [ | |
| { sender: 'customer', text: 'Hi, is the iPhone 15 Pro Max available?' }, | |
| { sender: 'bot', text: 'Hi! Yes, it\'s in stock. We have Natural Titanium and Blue Titanium, 256GB or 512GB. Shipping today.' }, | |
| { sender: 'customer', text: 'Great, let\'s order it then.' }, | |
| { sender: 'bot', text: 'Perfect. Confirming the iPhone 15 Pro Max order. Which color and storage would you like?' }, | |
| { sender: 'typing' } | |
| ] | |
| }; | |
| // Detect Language | |
| let userLang = navigator.language || navigator.userLanguage; | |
| if (!chatData[userLang]) { | |
| userLang = 'ru-RU'; // Fallback | |
| } | |
| const conversation = chatData[userLang]; | |
| let msgIndex = 0; | |
| function addMessage(msg) { | |
| if (msg.sender === 'typing') { | |
| const typingDiv = document.createElement('div'); | |
| typingDiv.className = 'chat-bubble bot typing-indicator'; | |
| typingDiv.innerHTML = '<div class="dot"></div><div class="dot"></div><div class="dot"></div>'; | |
| chatContainer.appendChild(typingDiv); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| return; | |
| } | |
| const div = document.createElement('div'); | |
| div.className = `chat-bubble ${msg.sender}`; | |
| div.textContent = msg.text; | |
| chatContainer.appendChild(div); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| function playConversation() { | |
| if (msgIndex < conversation.length) { | |
| const msg = conversation[msgIndex]; | |
| addMessage(msg); | |
| msgIndex++; | |
| const delay = msg.sender === 'typing' ? 1500 : (msg.text.length * 50 + 800); | |
| setTimeout(playConversation, delay); | |
| } else { | |
| // Loop after a pause | |
| setTimeout(() => { | |
| chatContainer.innerHTML = ''; | |
| msgIndex = 0; | |
| playConversation(); | |
| }, 4000); | |
| } | |
| } | |
| // Start Chat | |
| setTimeout(playConversation, 1000); | |
| // --- 2. Three.js Scene (Floating Spheres) --- | |
| const canvasContainer = document.getElementById('canvas-container'); | |
| // Scene Setup | |
| const scene = new THREE.Scene(); | |
| // Light background handled by CSS, Three.js canvas is transparent | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| camera.position.z = 5; | |
| const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| renderer.setPixelRatio(window.devicePixelRatio); | |
| canvasContainer.appendChild(renderer.domElement); | |
| // Lighting | |
| const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); | |
| scene.add(ambientLight); | |
| const pointLight = new THREE.PointLight(0xffffff, 1); | |
| pointLight.position.set(10, 10, 10); | |
| scene.add(pointLight); | |
| const pointLight2 = new THREE.PointLight(0xD58A60, 0.8); // Copper tint light | |
| pointLight2.position.set(-10, -10, 5); | |
| scene.add(pointLight2); | |
| // Spheres | |
| const spheres = []; | |
| const sphereGeometry = new THREE.SphereGeometry(1, 32, 32); | |
| // Material: Polished Copper (using standard material with metalness/roughness for approximation) | |
| const sphereMaterial = new THREE.MeshStandardMaterial({ | |
| color: 0xD58A60, // Copper | |
| metalness: 0.7, | |
| roughness: 0.2, | |
| }); | |
| // Create multiple spheres | |
| const sphereConfigs = [ | |
| { x: -2, y: 0, z: -2, scale: 0.8, speedY: 0.002 }, | |
| { x: 2.5, y: 1, z: -3, scale: 1.2, speedY: 0.003 }, | |
| { x: 3, y: -1.5, z: -1, scale: 0.5, speedY: 0.001 }, | |
| { x: -1.5, y: -2, z: -4, scale: 0.6, speedY: 0.0025 }, | |
| { x: 0.5, y: 2, z: -5, scale: 1.0, speedY: 0.0015 }, | |
| ]; | |
| sphereConfigs.forEach(config => { | |
| const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); | |
| sphere.position.set(config.x, config.y, config.z); | |
| sphere.scale.setScalar(config.scale); | |
| sphere.userData = { | |
| baseY: config.y, | |
| speedY: config.speedY, | |
| offset: Math.random() * Math.PI * 2 | |
| }; | |
| scene.add(sphere); | |
| spheres.push(sphere); | |
| }); | |
| // Animation Loop | |
| let time = 0; | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| time += 0.01; | |
| spheres.forEach(sphere => { | |
| // Float animation | |
| sphere.position.y = sphere.userData.baseY + Math.sin(time + sphere.userData.offset) * 0.2; | |
| // Rotation | |
| sphere.rotation.x += 0.002; | |
| sphere.rotation.y += 0.003; | |
| }); | |
| renderer.render(scene, camera); | |
| } | |
| animate(); | |
| // Responsive Three.js | |
| window.addEventListener('resize', () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // --- 3. MacOS Window Tilt Interaction --- | |
| const windowEl = document.querySelector('.mac-window'); | |
| const container = document.querySelector('.perspective-container'); | |
| if(container && windowEl) { | |
| // Use requestAnimationFrame for smoother performance | |
| let currentX = -8; | |
| let currentY = 4; | |
| let targetX = -8; | |
| let targetY = 4; | |
| const animateTilt = () => { | |
| // Lerp (Linear Interpolation) for smooth movement | |
| currentX += (targetX - currentX) * 0.1; | |
| currentY += (targetY - currentY) * 0.1; | |
| windowEl.style.transform = `rotateX(${currentY}deg) rotateY(${currentX}deg)`; | |
| requestAnimationFrame(animateTilt); | |
| }; | |
| animateTilt(); | |
| container.addEventListener('mousemove', (e) => { | |
| const rect = container.getBoundingClientRect(); | |
| const x = e.clientX - rect.left; | |
| const y = e.clientY - rect.top; | |
| const centerX = rect.width / 2; | |
| const centerY = rect.height / 2; | |
| // Reduced intensity for a more premium feel | |
| targetX = ((x - centerX) / centerX) * -4; | |
| targetY = ((y - centerY) / centerY) * 4; | |
| }); | |
| container.addEventListener('mouseleave', () => { | |
| targetX = -8; | |
| targetY = 4; | |
| }); | |
| } | |
| // --- 4. FAQ Accordion Logic --- | |
| const faqData = [ | |
| { | |
| q: "Зачем мне это нужно?", | |
| a: "Твой бизнес ограничен только временем. Пока ты не отвечаешь, твои конкуренты забирают заказы. Nexus превращает твои чаты в автоматический конвейер продаж, увеличивая прибыль без необходимости нанимать персонал." | |
| }, | |
| { | |
| q: "Что делает вас уникальными и отличными от других?", | |
| a: "Мы не используем дешевые скрипты или нестабильные расширения. Nexus строится на официальной интеграции через API Avito, что гарантирует безопасность и скорость. Наш AI использует реальный контекст магазина, обеспечивая качество общения, недоступное обычным ботам." | |
| }, | |
| { | |
| q: "Какая гарантия того, что то, что вы говорите, — правда?", | |
| a: "Наша гарантия — это прозрачность технологий. Мы используем официальный API, что исключает риски блокировки аккаунта, а эффективность алгоритма можно оценить уже в первые дни использования по росту ответов и конверсии." | |
| } | |
| ]; | |
| const faqContainer = document.getElementById('faq-container'); | |
| faqData.forEach((item, index) => { | |
| const faqItem = document.createElement('div'); | |
| faqItem.className = 'border border-neutral-200 rounded-2xl bg-white overflow-hidden'; | |
| faqItem.innerHTML = ` | |
| <button class="w-full px-6 py-5 text-left flex justify-between items-center focus:outline-none group"> | |
| <span class="text-lg font-medium text-neutral-900 group-hover:text-primary transition-colors">${item.q}</span> | |
| <i data-feather="chevron-down" class="text-neutral-400 transform transition-transform duration-300"></i> | |
| </button> | |
| <div class="accordion-content bg-neutral-50"> | |
| <div class="px-6 pb-6 pt-2 text-neutral-600 leading-relaxed"> | |
| ${item.a} | |
| </div> | |
| </div> | |
| `; | |
| const btn = faqItem.querySelector('button'); | |
| const content = faqItem.querySelector('.accordion-content'); | |
| const icon = faqItem.querySelector('[data-feather="chevron-down"]'); | |
| btn.addEventListener('click', () => { | |
| const isOpen = content.style.maxHeight; | |
| // Close all others (optional, but cleaner) | |
| document.querySelectorAll('.accordion-content').forEach(c => { | |
| c.style.maxHeight = null; | |
| c.classList.remove('open'); | |
| }); | |
| document.querySelectorAll('.chevron-down').forEach(i => i.style.transform = 'rotate(0deg)'); | |
| if (!isOpen) { | |
| content.classList.add('open'); | |
| content.style.maxHeight = content.scrollHeight + "px"; | |
| icon.style.transform = 'rotate(180deg)'; | |
| } | |
| }); | |
| faqContainer.appendChild(faqItem); | |
| }); | |
| // Re-init icons for dynamic content | |
| feather.replace(); | |
| }); |