From e09fb66b25f8d097615fac72a153557f93ade97d Mon Sep 17 00:00:00 2001 From: temp Date: Mon, 17 Aug 2026 11:38:39 +0000 Subject: [PATCH] Kazakhtelecom: Modern Liquid Glass website with all services, tariffs, bundles --- app.js | 689 ++++++++++------------- data.json | 290 +++++++++- index.html | 332 ++++++----- package-lock.json | 4 +- package.json | 5 +- server.js | 362 +----------- style.css | 1331 ++++++++++++++++++++++++++++++--------------- 7 files changed, 1669 insertions(+), 1344 deletions(-) diff --git a/app.js b/app.js index 3e3abf8..aab46bd 100644 --- a/app.js +++ b/app.js @@ -1,473 +1,332 @@ -// VibeChat Messenger App +// Kazakhtelecom Website — JavaScript -const API = { - me: '/api/auth/me', - logout: '/api/auth/logout', - users: '/api/users', - chats: '/api/chats', - updateProfile: '/api/users/me' -}; +const API = '/api/data'; +let siteData = null; +let currentTariffCategory = 'internet'; -let currentUser = null; -let currentChat = null; -let allChats = []; -let allUsers = []; - -// Initialize app +// Initialize window.addEventListener('DOMContentLoaded', async () => { - const token = localStorage.getItem('vibechat_token'); - if (!token) { - window.location.href = 'index.html'; + await loadData(); + renderHero(); + renderServices(); + renderTariffs('internet'); + renderBundles(); + renderNews(); + renderFAQ(); + setupAnimations(); + setupTariffTabs(); + setupContactForm(); +}); + +// Load data from JSON +async function loadData() { + try { + const res = await fetch(API); + const json = await res.json(); + if (json.ok) { + siteData = json.data; + } + } catch (e) { + console.error('Failed to load data:', e); + } +} + +// Render Hero +function renderHero() { + const { heroTitle, heroSubtitle, heroDescription } = siteData.siteSettings; + document.getElementById('heroTitle').textContent = heroTitle; + document.getElementById('heroSubtitle').textContent = heroSubtitle; + document.getElementById('heroDescription').textContent = heroDescription; +} + +// Render Services +function renderServices() { + const grid = document.getElementById('servicesGrid'); + grid.innerHTML = siteData.services.map(service => ` +
+ ${service.icon} +

${service.name}

+

${service.description}

+
+ ${service.features.map(f => `
${f}
`).join('')} +
+
+ `).join(''); +} + +// Render Tariffs +function renderTariffs(category) { + currentTariffCategory = category; + const grid = document.getElementById('tariffsGrid'); + const tariffs = siteData.tariffs[category] || []; + + if (tariffs.length === 0) { + grid.innerHTML = '

Тарифы загружаются...

'; return; } - await loadCurrentUser(); - await loadChats(); - await loadUsers(); - - // Inject emoji picker HTML - const emojis = ['😀','😂','😍','🥰','😎','🤔','👍','❤️','🔥','🎉','👋','🙏','💯','✨','🚀','💪','🎯','✅','📌','🔔']; - document.getElementById('emojiPicker').innerHTML = ` -
- ${emojis.map(e => `${e}`).join('')} -
- `; -}); - -// Load current user -async function loadCurrentUser() { - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.me, { - headers: { 'Authorization': `Bearer ${token}` } - }); - const json = await res.json(); + grid.innerHTML = tariffs.map(tariff => { + const speedOrChannels = tariff.speed || tariff.channels; + const priceLabel = category === 'smart' ? '/мес' : ''; - if (!json.ok) { - localStorage.removeItem('vibechat_token'); - window.location.href = 'index.html'; - return; - } - - currentUser = json.user; - localStorage.setItem('vibechat_user', JSON.stringify(currentUser)); - - // Update UI - document.getElementById('currentUserName').textContent = currentUser.username; - document.getElementById('currentUserAvatar').src = currentUser.avatar; - } catch (e) { - console.error('Failed to load user:', e); - } -} - -// Load chats -async function loadChats() { - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.chats, { - headers: { 'Authorization': `Bearer ${token}` } - }); - const json = await res.json(); - - if (json.ok) { - allChats = json.chats; - renderChatList(); - } - } catch (e) { - console.error('Failed to load chats:', e); - } -} - -// Render chat list -function renderChatList() { - const chatList = document.getElementById('chatList'); - - if (allChats.length === 0) { - chatList.innerHTML = ` -
-

Нет чатов

-

Нажмите + чтобы создать новый

+ return ` +
+

${tariff.name}

+
${speedOrChannels}
+
${tariff.price.toLocaleString()} ${tariff.currency}${priceLabel}
+
+ ${tariff.features.map(f => `
${f}
`).join('')} +
+
`; - return; - } + }).join(''); +} - chatList.innerHTML = allChats.map(chat => ` -
- -
-
- ${chat.name} - ${chat.type !== 'private' ? `${chat.type === 'group' ? 'Группа' : 'Канал'}` : ''} -
-
- ${chat.lastMessage ? chat.lastMessage.text : 'Нет сообщений'} +// Setup Tariff Tabs +function setupTariffTabs() { + const tabs = document.querySelectorAll('.tab'); + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + renderTariffs(tab.dataset.category); + }); + }); +} + +// Render Bundles +function renderBundles() { + const grid = document.getElementById('bundlesGrid'); + grid.innerHTML = siteData.bundles.map(bundle => ` +
+

${bundle.name}

+

${bundle.description}

+
+
${bundle.originalPrice.toLocaleString()} ₸
+
+ ${bundle.bundlePrice.toLocaleString()} ₸ + -${bundle.discount}%
- ${chat.lastMessage ? `${formatTime(chat.lastMessage.createdAt)}` : ''} +
+ ${bundle.includes.map(item => `
${item}
`).join('')} +
+
`).join(''); } -// Load users for new chat -async function loadUsers() { - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.users, { - headers: { 'Authorization': `Bearer ${token}` } - }); - const json = await res.json(); - - if (json.ok) { - allUsers = json.users; - renderUserList(); - } - } catch (e) { - console.error('Failed to load users:', e); - } -} - -// Render user list -function renderUserList() { - const userList = document.getElementById('userList'); - - if (allUsers.length === 0) { - userList.innerHTML = '

Нет пользователей

'; - return; - } - - userList.innerHTML = allUsers.map(user => ` -
- - ${user.username} -
- `).join(''); -} - -// Select chat -async function selectChat(chatId) { - currentChat = allChats.find(c => c.id === chatId); - if (!currentChat) return; - - // Update UI - document.getElementById('emptyChat').style.display = 'none'; - document.getElementById('messagesList').style.display = 'flex'; - document.getElementById('messageInputContainer').style.display = 'block'; - - // Update header - document.getElementById('chatHeaderName').textContent = currentChat.name; - document.getElementById('chatHeaderAvatar').src = currentChat.avatar || 'https://api.dicebear.com/7.x/identicon/svg?seed=' + currentChat.id; - document.getElementById('chatHeaderStatus').textContent = currentChat.type === 'channel' ? 'Канал' : (currentChat.type === 'group' ? `${currentChat.participants?.length || 0} участников` : 'Онлайн'); - - // Render chat list again to update active state - renderChatList(); - - // Load messages - await loadMessages(chatId); -} - -// Load messages -async function loadMessages(chatId) { - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(`${API.chats}/${chatId}/messages`, { - headers: { 'Authorization': `Bearer ${token}` } - }); - const json = await res.json(); - - if (json.ok) { - renderMessages(json.messages); - } - } catch (e) { - console.error('Failed to load messages:', e); - } -} - -// Render messages -function renderMessages(messages) { - const messagesList = document.getElementById('messagesList'); - - if (messages.length === 0) { - messagesList.innerHTML = '

Сообщений пока нет

'; - return; - } - - messagesList.innerHTML = messages.map(msg => ` -
- -
- ${msg.senderId !== currentUser.id ? `
${msg.senderName}
` : ''} -
${escapeHtml(msg.text)}
-
${formatTime(msg.createdAt)}
+// Render News +function renderNews() { + const grid = document.getElementById('newsGrid'); + grid.innerHTML = siteData.news.map(news => ` +
+ ${news.title} +
+
+ ${news.category} + ${formatDate(news.date)} +
+

${news.title}

+

${news.excerpt}

`).join(''); - - // Scroll to bottom - const container = document.getElementById('messagesContainer'); - container.scrollTop = container.scrollHeight; } -// Send message -async function sendMessage() { - const input = document.getElementById('messageInput'); - const text = input.value.trim(); - - if (!text || !currentChat) return; +// Render FAQ +function renderFAQ() { + const container = document.getElementById('faqContainer'); + container.innerHTML = siteData.faq.map((item, index) => ` +
+ +
+

${item.answer}

+
+
+ `).join(''); +} - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(`${API.chats}/${currentChat.id}/messages`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ text }) - }); - const json = await res.json(); - - if (json.ok) { - input.value = ''; - await loadMessages(currentChat.id); - await loadChats(); // Update last message +// Toggle FAQ +function toggleFaq(index) { + const items = document.querySelectorAll('.faq-item'); + items.forEach((item, i) => { + if (i === index) { + item.classList.toggle('active'); } else { - alert('Ошибка отправки: ' + json.error); + item.classList.remove('active'); } - } catch (e) { - alert('Ошибка: ' + e.message); - } + }); } -// Handle enter key -function handleKeyPress(event) { - if (event.key === 'Enter') { - sendMessage(); - } -} - -// Emoji picker -function toggleEmojiPicker() { - const picker = document.getElementById('emojiPicker'); - picker.classList.toggle('active'); -} - -function insertEmoji(emoji) { - const input = document.getElementById('messageInput'); - input.value += emoji; - input.focus(); - document.getElementById('emojiPicker').classList.remove('active'); -} - -// Close emoji picker on outside click -document.addEventListener('click', (e) => { - const picker = document.getElementById('emojiPicker'); - const btn = document.querySelector('.btn-emoji'); - if (!picker.contains(e.target) && !btn.contains(e.target)) { - picker.classList.remove('active'); - } -}); - -// New chat modal -function showNewChatModal() { - document.getElementById('newChatModal').classList.add('active'); - loadUsers(); -} - -function closeNewChatModal() { - document.getElementById('newChatModal').classList.remove('active'); -} - -function switchTab(tab) { - document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); - event.target.classList.add('active'); - - document.getElementById('chatTab').style.display = tab === 'chat' ? 'block' : 'none'; - document.getElementById('groupTab').style.display = tab === 'group' ? 'block' : 'none'; - document.getElementById('channelTab').style.display = tab === 'channel' ? 'block' : 'none'; -} - -// Create private chat -async function createPrivateChat(userId) { - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.chats, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ type: 'private', participantId: userId }) - }); - const json = await res.json(); +// Contact Form +function setupContactForm() { + // Phone mask + const phoneInput = document.getElementById('contactPhone'); + phoneInput.addEventListener('input', (e) => { + let value = e.target.value.replace(/\D/g, ''); + if (value.length > 11) value = value.slice(0, 11); - if (json.ok) { - closeNewChatModal(); - await loadChats(); - selectChat(json.chat.id); + if (value.length > 0) { + value = '+7 (' + value.slice(1, 4); + } + if (value.length > 6) { + value = value.slice(0, 6) + ') ' + value.slice(6, 9); + } + if (value.length > 11) { + value = value.slice(0, 11) + '-' + value.slice(11, 13); + } + if (value.length > 14) { + value = value.slice(0, 14) + '-' + value.slice(14, 16); } - } catch (e) { - alert('Ошибка: ' + e.message); - } -} - -// Create group -async function createGroup() { - const name = document.getElementById('groupName').value.trim(); - if (!name) return alert('Введите название группы'); - - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.chats, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ type: 'group', name }) - }); - const json = await res.json(); - if (json.ok) { - closeNewChatModal(); - document.getElementById('groupName').value = ''; - await loadChats(); - selectChat(json.chat.id); - } - } catch (e) { - alert('Ошибка: ' + e.message); - } + e.target.value = value; + }); } -// Create channel -async function createChannel() { - const name = document.getElementById('channelName').value.trim(); - if (!name) return alert('Введите название канала'); - - try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.chats, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ type: 'channel', name }) - }); - const json = await res.json(); - - if (json.ok) { - closeNewChatModal(); - document.getElementById('channelName').value = ''; - await loadChats(); - selectChat(json.chat.id); - } - } catch (e) { - alert('Ошибка: ' + e.message); - } -} - -// Settings -function openSettings() { - const user = JSON.parse(localStorage.getItem('vibechat_user')); - document.getElementById('settingsAvatar').src = user.avatar; - document.getElementById('settingsUsername').value = user.username; - document.getElementById('settingsBio').value = user.bio || ''; - document.getElementById('settingsModal').classList.add('active'); -} - -function closeSettings() { - document.getElementById('settingsModal').classList.remove('active'); -} - -async function updateProfile(event) { +async function submitContact(event) { event.preventDefault(); - const username = document.getElementById('settingsUsername').value; - const bio = document.getElementById('settingsBio').value; + const name = document.getElementById('contactName').value; + const phone = document.getElementById('contactPhone').value; + const service = document.getElementById('contactService').value; try { - const token = localStorage.getItem('vibechat_token'); - const res = await fetch(API.updateProfile, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ username, bio }) + const res = await fetch('/api/contact', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, phone, service }) }); const json = await res.json(); if (json.ok) { - localStorage.setItem('vibechat_user', JSON.stringify(json.user)); - currentUser = json.user; - document.getElementById('currentUserName').textContent = currentUser.username; - document.getElementById('currentUserAvatar').src = currentUser.avatar; - closeSettings(); - alert('Профиль обновлён! ✅'); + alert('✅ ' + json.message); + document.getElementById('contactForm').reset(); + } else { + alert('Ошибка: ' + json.error); } } catch (e) { - alert('Ошибка: ' + e.message); + alert('Ошибка отправки: ' + e.message); } } -function changeAvatar() { - const url = prompt('Введите URL аватара:', currentUser.avatar); - if (url) { - updateProfile({ preventDefault: () => {} }); +// Select tariff +function selectTariff(tariffId) { + const tariff = siteData.tariffs[currentTariffCategory].find(t => t.id === tariffId); + if (!tariff) return; + + const service = currentTariffCategory === 'tv' ? 'Цифровое ТВ' : + currentTariffCategory === 'mobile' ? 'Мобильная связь' : + currentTariffCategory === 'smart' ? 'Умный дом' : 'Домашний интернет'; + + document.getElementById('contactService').value = currentTariffCategory; + document.getElementById('contactName').focus(); + + alert(`Вы выбрали тариф "${tariff.name}" за ${tariff.price} ₸\n\nЗаполните форму ниже, и мы свяжемся с вами для подключения!`); + + scrollToSection('contacts'); +} + +// Select bundle +function selectBundle(bundleId) { + const bundle = siteData.bundles.find(b => b.id === bundleId); + if (!bundle) return; + + document.getElementById('contactService').value = 'bundle'; + document.getElementById('contactName').focus(); + + alert(`Вы выбрали пакет "${bundle.name}" за ${bundle.bundlePrice} ₸ (экономия ${bundle.discount}%)\n\nЗаполните форму ниже для подключения!`); + + scrollToSection('contacts'); +} + +// Scroll to section +function scrollToSection(id) { + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } -// Logout -async function logout() { - if (!confirm('Вы уверены, что хотите выйти?')) return; +// Setup animations +function setupAnimations() { + // Nav scroll effect + window.addEventListener('scroll', () => { + const nav = document.getElementById('nav'); + if (window.scrollY > 50) { + nav.classList.add('scrolled'); + } else { + nav.classList.remove('scrolled'); + } + }); - try { - const token = localStorage.getItem('vibechat_token'); - await fetch(API.logout, { - method: 'POST', - headers: { 'Authorization': `Bearer ${token}` } + // Animate stats + const stats = document.querySelectorAll('.stat-number'); + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const target = parseInt(entry.target.dataset.target); + animateNumber(entry.target, target); + observer.unobserve(entry.target); + } }); - } catch (e) {} + }, { threshold: 0.5 }); - localStorage.removeItem('vibechat_token'); - localStorage.removeItem('vibechat_user'); - window.location.href = 'index.html'; + stats.forEach(stat => observer.observe(stat)); + + // Reveal on scroll + const reveals = document.querySelectorAll('.service-card, .tariff-card, .bundle-card, .news-card'); + const revealObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + }); + }, { threshold: 0.1 }); + + reveals.forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(30px)'; + el.style.transition = 'all 0.6s cubic-bezier(0.25, 1, 0.5, 1)'; + revealObserver.observe(el); + }); } -// Filter chats -function filterChats() { - const query = document.getElementById('searchInput').value.toLowerCase(); - const filtered = allChats.filter(c => c.name.toLowerCase().includes(query)); - - const chatList = document.getElementById('chatList'); - chatList.innerHTML = filtered.map(chat => ` -
- -
-
${chat.name}
-
${chat.lastMessage ? chat.lastMessage.text : 'Нет сообщений'}
-
-
- `).join(''); +// Animate number +function animateNumber(element, target) { + const duration = 2000; + const start = 0; + const increment = target / (duration / 16); + let current = start; + + const timer = setInterval(() => { + current += increment; + if (current >= target) { + element.textContent = formatNumber(target); + clearInterval(timer); + } else { + element.textContent = formatNumber(Math.floor(current)); + } + }, 16); } -// Helper functions -function formatTime(isoString) { +// Format number with spaces +function formatNumber(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); +} + +// Format date +function formatDate(isoString) { const date = new Date(isoString); - const now = new Date(); - const diff = now - date; - - if (diff < 60000) return 'Только что'; - if (diff < 3600000) return Math.floor(diff / 60000) + ' мин назад'; - if (diff < 86400000) return Math.floor(diff / 3600000) + ' ч назад'; - if (diff < 604800000) return Math.floor(diff / 86400000) + ' дн назад'; - - return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' }); + return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }); } -function escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; +// Open personal cabinet (placeholder) +function openPersonal() { + alert('Личный кабинет в разработке 🔜\n\nВы можете войти через:\n• Мобильное приложение\n• my.kazakhtelecom.kz'); } diff --git a/data.json b/data.json index 111e1c8..12fe48f 100644 --- a/data.json +++ b/data.json @@ -1,8 +1,286 @@ { - "users": [], - "chats": [], - "messages": [], - "groups": [], - "channels": [], - "sessions": [] + "siteSettings": { + "heroTitle": "Kazakhtelecom", + "heroSubtitle": "Цифровое будущее Казахстана", + "heroDescription": "Высокоскоростной интернет, цифровое ТВ и инновационные технологии для вашего дома и бизнеса", + "primaryColor": "#0066CC", + "accentColor": "#00C9FF" + }, + + "services": [ + { + "id": "internet", + "name": "Домашний интернет", + "icon": "🌐", + "description": "Высокоскоростной оптоволоконный интернет", + "features": ["Скорость до 1 Гбит/с", "Безлимитный трафик", "Стабильное соединение", "Поддержка 24/7"], + "color": "#0066CC" + }, + { + "id": "tv", + "name": "Цифровое TV", + "icon": "📺", + "description": "Более 200 каналов в высоком качестве", + "features": ["4K Ultra HD", "Интерактивное ТВ", "Архив передач", "Родительский контроль"], + "color": "#7B2CBF" + }, + { + "id": "mobile", + "name": "Мобильная связь", + "icon": "📱", + "description": "Современные тарифы для всей семьи", + "features": ["Безлимитные звонки", "5G интернет", "Семейные тарифы", "Роуминг по миру"], + "color": "#FF006E" + }, + { + "id": "smart", + "name": "Умный дом", + "icon": "🏠", + "description": "Автоматизация и безопасность вашего дома", + "features": ["Видеонаблюдение", "Умные датчики", "Управление со смартфона", "Система безопасности"], + "color": "#00B4D8" + }, + { + "id": "cloud", + "name": "Облачные сервисы", + "icon": "☁️", + "description": "Хранение данных и онлайн-сервисы", + "features": ["Облачное хранилище", "Онлайн-офис", "Резервное копирование", "Доступ отовсюду"], + "color": "#3A86FF" + }, + { + "id": "business", + "name": "Бизнесу", + "icon": "💼", + "description": "Решения для корпоративных клиентов", + "features": ["Выделенные линии", "IP-телефония", "ЦОД услуги", "Техническая поддержка"], + "color": "#8338EC" + } + ], + + "tariffs": { + "internet": [ + { + "id": "net-start", + "name": "Старт", + "speed": "100 Мбит/с", + "price": 3990, + "currency": "₸", + "popular": false, + "features": ["Безлимитный трафик", "Wi-Fi роутер в аренду", "Антивирус в подарок", "Техподдержка 24/7"] + }, + { + "id": "net-optimal", + "name": "Оптимальный", + "speed": "300 Мбит/с", + "price": 5990, + "currency": "₸", + "popular": true, + "features": ["Безлимитный трафик", "Двухдиапазонный роутер", "Антивирус + Родительский контроль", "Приоритетная поддержка"] + }, + { + "id": "net-max", + "name": "Максимальный", + "speed": "1 Гбит/с", + "price": 8990, + "currency": "₸", + "popular": false, + "features": ["Гигабитный доступ", "Mesh-система", "Премиум защита", "Персональный менеджер"] + }, + { + "id": "net-gamer", + "name": "Геймер", + "speed": "500 Мбит/с", + "price": 7490, + "currency": "₸", + "popular": false, + "features": ["Низкий пинг", "Игровой приоритет", "Защита от DDoS", "Бонусы от партнеров"] + } + ], + + "tv": [ + { + "id": "tv-light", + "name": "Лёгкий", + "channels": "100+ каналов", + "price": 2990, + "currency": "₸", + "popular": false, + "features": ["HD качество", "Архив 3 дня", "Базовые пакеты", "Пульт в комплекте"] + }, + { + "id": "tv-standard", + "name": "Стандарт", + "channels": "150+ каналов", + "price": 4490, + "currency": "₸", + "popular": true, + "features": ["Full HD", "Архив 7 дней", "Премьеры и сериалы", "4K контент"] + }, + { + "id": "tv-premium", + "name": "Премиум", + "channels": "200+ каналов", + "price": 6990, + "currency": "₸", + "popular": false, + "features": ["4K Ultra HD", "Архив 14 дней", "Все премиум пакеты", "Онлайн-кинотеатр"] + } + ], + + "mobile": [ + { + "id": "mobile-light", + "name": "Лёгкий", + "price": 2500, + "currency": "₸", + "popular": false, + "features": ["Безлимитные звонки по КЗ", "10 ГБ интернета", "SMS безлимит", "Соцсети без трафика"] + }, + { + "id": "mobile-standard", + "name": "Стандарт", + "price": 4500, + "currency": "₸", + "popular": true, + "features": ["Безлимитные звонки", "30 ГБ интернета", "Безлимит на мессенджеры", "5G доступ"] + }, + { + "id": "mobile-unlimited", + "name": "Безлимит", + "price": 7500, + "currency": "₸", + "popular": false, + "features": ["Всё безлимитно", "50 ГБ на высокой скорости", "Роуминг СНГ включён", "Приоритет в сети"] + }, + { + "id": "mobile-family", + "name": "Семейный", + "price": 12000, + "currency": "₸", + "popular": false, + "features": ["4 сим-карты", "Общий пакет 100 ГБ", "Безлимит внутри семьи", "Детский контроль"] + } + ], + + "smart": [ + { + "id": "smart-basic", + "name": "Базовый", + "price": 4990, + "currency": "₸/мес", + "popular": false, + "features": ["2 умные лампочки", "1 датчик движения", "Управление со смартфона", "Базовые сценарии"] + }, + { + "id": "smart-comfort", + "name": "Комфорт", + "price": 8990, + "currency": "₸/мес", + "popular": true, + "features": ["5 умных устройств", "Видеодомофон", "Датчики протечки", "Голосовое управление"] + }, + { + "id": "smart-security", + "name": "Безопасность", + "price": 12990, + "currency": "₸/мес", + "popular": false, + "features": ["Видеонаблюдение 4 камеры", "Датчики открытия", "Сигнализация", "Мониторинг 24/7"] + } + ] + }, + + "bundles": [ + { + "id": "bundle-family", + "name": "Семейный", + "description": "Интернет + ТВ + Мобильная связь для всей семьи", + "originalPrice": 15470, + "bundlePrice": 11990, + "discount": 23, + "includes": ["Интернет 300 Мбит/с", "Цифровое ТВ 150+ каналов", "2 мобильных тарифа Стандарт"], + "popular": true + }, + { + "id": "bundle-max", + "name": "Максимальный", + "description": "Все услуги в одном пакете", + "originalPrice": 22970, + "bundlePrice": 17990, + "discount": 22, + "includes": ["Интернет 1 Гбит/с", "Цифровое ТВ Премиум", "Мобильный тариф Безлимит", "Умный дом Комфорт"], + "popular": false + }, + { + "id": "bundle-gamer", + "name": "Геймер", + "description": "Для настоящих геймеров и стримеров", + "originalPrice": 14480, + "bundlePrice": 11490, + "discount": 21, + "includes": ["Интернет Геймер 500 Мбит/с", "ТВ Стандарт", "Мобильный тариф Стандарт", "Игровые бонусы"], + "popular": false + } + ], + + "news": [ + { + "id": 1, + "title": "Запущен 5G в Алматы", + "date": "2026-08-15", + "category": "technology", + "image": "https://picsum.photos/600/400?random=1", + "excerpt": "Теперь абоненты могут пользоваться сверхбыстрым интернетом пятого поколения" + }, + { + "id": 2, + "title": "Новые тарифы на домашний интернет", + "date": "2026-08-10", + "category": "tariffs", + "image": "https://picsum.photos/600/400?random=2", + "excerpt": "Увеличили скорости без изменения стоимости для всех тарифов" + }, + { + "id": 3, + "title": "Умный дом стал доступнее", + "date": "2026-08-05", + "category": "products", + "image": "https://picsum.photos/600/400?random=3", + "excerpt": "Скидки до 30% на оборудование для системы умный дом" + } + ], + + "contacts": { + "phone": "123", + "phoneFormatted": "123 (бесплатно с городских)", + "mobile": "+7 (777) 123-45-67", + "email": "info@kazakhtelecom.kz", + "address": "г. Алматы, ул. Байтурсынова, 112Б", + "social": { + "instagram": "https://instagram.com/kazakhtelecom", + "facebook": "https://facebook.com/kazakhtelecom", + "telegram": "https://t.me/kazakhtelecom", + "youtube": "https://youtube.com/kazakhtelecom" + } + }, + + "faq": [ + { + "question": "Как подключить домашний интернет?", + "answer": "Оставьте заявку на сайте или позвоните по номеру 123. Наш специалист свяжется с вами в течение 15 минут для уточнения деталей и назначения времени подключения." + }, + { + "question": "Какая скорость интернета мне подойдет?", + "answer": "Для просмотра сайтов и соцсетей достаточно 100 Мбит/с. Для стриминга 4K и онлайн-игр рекомендуем от 300 Мбит/с. Для умного дома и множества устройств — 1 Гбит/с." + }, + { + "question": "Можно ли заморозить услуги на время отъезда?", + "answer": "Да, вы можете приостановить действие тарифа на срок от 14 до 90 дней через личный кабинет или по звонку в контактный центр." + }, + { + "question": "Предоставляете ли вы оборудование в аренду?", + "answer": "Да, мы предоставляем современные Wi-Fi роутеры и TV-приставки в аренду за символическую плату. Также возможна покупка оборудования в рассрочку." + } + ] } diff --git a/index.html b/index.html index 845dc42..391fd1d 100644 --- a/index.html +++ b/index.html @@ -3,136 +3,224 @@ - VibeChat — Мессенджер будущего - + Kazakhtelecom — Цифровое будущее Казахстана + - -
- -
-
-

Общение без границ

-

Быстрый. Безопасный. С End-to-End шифрованием.

+ +
+
+
+
+
+
+
+
+

+

+

- - + +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-

Почему VibeChat?

-
-
-
🔒
-

E2E Шифрование

-

Ваши сообщения видят только отправитель и получатель

-
-
-
⚡
-

Молниеносно

-

Мгновенная доставка сообщений в любую точку мира

-
-
-
👥
-

Группы и каналы

-

Создавайте группы до 1000 человек и публичные каналы

-
-
-
🎨
-

Эмодзи и стикеры

-

Тысячи эмодзи и стикеров для выражения эмоций

-
-
-
📱
-

Кроссплатформенно

-

Работает на любом устройстве через браузер

-
-
-
🔐
-

Безопасность

-

Двухфакторная аутентификация и секретные чаты

-
-
-
- -
-

Готовы начать?

-

Присоединяйтесь к тысячам пользователей уже сегодня

- -
- -
-

© 2026 VibeChat. Все права защищены.

-
-
- - - +
+
+
+
+
+
- + +
+
+

Наши услуги

+

Полный спектр цифровых услуг для вашего комфорта

+
+
+ +
+
+ + +
+
+

Тарифные планы

+

Выберите подходящий тариф

+
+ + + + +
+
+
+ +
+
+ + +
+
+

Выгодные пакеты

+

Экономьте до 30% при подключении нескольких услуг

+
+
+ +
+
+ + +
+
+
+
0
+
Абонентов по всему Казахстану
+
+
+
0
+
Лет на рынке
+
+
+
0
+
Городов покрытия
+
+
+
0
+
Поддержка 24/7
+
+
+
+ + +
+
+

Новости и события

+

Будьте в курсе последних событий

+
+
+ +
+
+ + +
+
+

Частые вопросы

+

Ответы на популярные вопросы

+
+
+ +
+
+ + +
+
+

Оставить заявку

+

Мы перезвоним вам в течение 15 минут

+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + diff --git a/package-lock.json b/package-lock.json index e98e49b..9004032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,10 @@ { - "name": "apple-style-app", + "name": "vibechat-messenger", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "apple-style-app" + "name": "vibechat-messenger" } } } diff --git a/package.json b/package.json index 2874e00..929fc3a 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { - "name": "vibechat-messenger", + "name": "kazakhtelecom-website", "private": true, "type": "commonjs", "scripts": { "start": "node server.js" - }, - "dependencies": {} + } } diff --git a/server.js b/server.js index 349473f..1e3fdcd 100644 --- a/server.js +++ b/server.js @@ -1,18 +1,12 @@ const http = require("http"); const fs = require("fs"); const path = require("path"); -const crypto = require("crypto"); const PORT = process.env.PORT || 3000; const DATA = path.join(__dirname, "data.json"); -const SECRET_KEY = crypto.randomBytes(32).toString("hex"); function readData() { - try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return { users: [], chats: [], messages: [], groups: [], channels: [], sessions: [] }; } -} - -function writeData(data) { - fs.writeFileSync(DATA, JSON.stringify(data, null, 2)); + try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return {}; } } function parseBody(req) { @@ -28,326 +22,18 @@ function json(res, data, status = 200) { res.end(JSON.stringify(data)); } -function notFound(res) { - res.writeHead(404, { "Content-Type": "text/plain" }); - res.end("Not Found"); -} - -// Генерация токена сессии -function generateToken(userId) { - return crypto.createHmac("sha256", SECRET_KEY).update(userId + Date.now()).digest("hex"); -} - -// Проверка токена -function verifyToken(token) { - const data = readData(); - const session = data.sessions.find(s => s.token === token && new Date(s.expires) > new Date()); - if (!session) return null; - return data.users.find(u => u.id === session.userId); -} - -// Хеширование пароля -function hashPassword(password) { - return crypto.createHash("sha256").update(password + "vibechat_salt").digest("hex"); -} - -// E2E шифрование (упрощённое AES-GCM) -function encryptMessage(text, key) { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv("aes-256-gcm", Buffer.from(key.slice(0, 32).padEnd(32, "0")), iv); - let encrypted = cipher.update(text, "utf8", "hex"); - encrypted += cipher.final("hex"); - const authTag = cipher.getAuthTag().toString("hex"); - return { iv: iv.toString("hex"), encrypted, authTag }; -} - -function decryptMessage(encryptedData, key) { - try { - const decipher = crypto.createDecipheriv("aes-256-gcm", Buffer.from(key.slice(0, 32).padEnd(32, "0")), Buffer.from(encryptedData.iv, "hex")); - decipher.setAuthTag(Buffer.from(encryptedData.authTag, "hex")); - let decrypted = decipher.update(encryptedData.encrypted, "hex", "utf8"); - decrypted += decipher.final("utf8"); - return decrypted; - } catch (e) { - return "[Ошибка расшифровки]"; - } -} - const routes = { - // ========== AUTH ========== - "POST /api/auth/register": async (req, res) => { - const { username, email, password } = await parseBody(req); - if (!username || !email || !password) return json(res, { ok: false, error: "Все поля обязательны" }, 400); - + "GET /api/data": (req, res) => { const data = readData(); - if (data.users.find(u => u.email === email)) return json(res, { ok: false, error: "Email уже зарегистрирован" }, 400); - if (data.users.find(u => u.username === username)) return json(res, { ok: false, error: "Имя пользователя занято" }, 400); - - const user = { - id: Date.now(), - username, - email, - passwordHash: hashPassword(password), - avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${username}`, - bio: "", - createdAt: new Date().toISOString() - }; - - data.users.push(user); - writeData(data); - - const token = generateToken(user.id); - data.sessions.push({ token, userId: user.id, expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() }); - writeData(data); - - json(res, { ok: true, token, user: { id: user.id, username: user.username, email: user.email, avatar: user.avatar } }); + json(res, { ok: true, data }); }, - "POST /api/auth/login": async (req, res) => { - const { email, password } = await parseBody(req); - if (!email || !password) return json(res, { ok: false, error: "Email и пароль обязательны" }, 400); - - const data = readData(); - const user = data.users.find(u => u.email === email && u.passwordHash === hashPassword(password)); - if (!user) return json(res, { ok: false, error: "Неверный email или пароль" }, 401); - - const token = generateToken(user.id); - data.sessions.push({ token, userId: user.id, expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() }); - writeData(data); - - json(res, { ok: true, token, user: { id: user.id, username: user.username, email: user.email, avatar: user.avatar } }); - }, - - "POST /api/auth/logout": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Токен не предоставлен" }, 401); - - const data = readData(); - data.sessions = data.sessions.filter(s => s.token !== token); - writeData(data); - json(res, { ok: true }); - }, - - "GET /api/auth/me": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - - const user = verifyToken(token); - if (!user) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - json(res, { ok: true, user: { id: user.id, username: user.username, email: user.email, avatar: user.avatar, bio: user.bio } }); - }, - - // ========== USERS ========== - "GET /api/users": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const data = readData(); - const users = data.users.filter(u => u.id !== currentUser.id).map(u => ({ - id: u.id, username: u.username, avatar: u.avatar - })); - json(res, { ok: true, users }); - }, - - "PUT /api/users/me": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const { username, bio, avatar } = await parseBody(req); - const data = readData(); - const idx = data.users.findIndex(u => u.id === currentUser.id); - if (idx === -1) return json(res, { ok: false, error: "Пользователь не найден" }, 404); - - if (username) data.users[idx].username = username; - if (bio !== undefined) data.users[idx].bio = bio; - if (avatar) data.users[idx].avatar = avatar; - writeData(data); - - json(res, { ok: true, user: data.users[idx] }); - }, - - // ========== CHATS ========== - "GET /api/chats": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const data = readData(); - const userChats = data.chats.filter(c => c.participants.includes(currentUser.id)); + "POST /api/contact": async (req, res) => { + const { name, phone, service } = await parseBody(req); + if (!name || !phone) return json(res, { ok: false, error: "Имя и телефон обязательны" }, 400); - const chatsWithDetails = userChats.map(chat => { - const otherUserId = chat.type === "private" ? chat.participants.find(p => p !== currentUser.id) : null; - const otherUser = otherUserId ? data.users.find(u => u.id === otherUserId) : null; - const lastMessage = data.messages.filter(m => m.chatId === chat.id).sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))[0]; - - return { - ...chat, - name: chat.type === "private" ? otherUser?.username : chat.name, - avatar: chat.type === "private" ? otherUser?.avatar : chat.avatar, - lastMessage: lastMessage ? { text: lastMessage.text, createdAt: lastMessage.createdAt } : null - }; - }); - - json(res, { ok: true, chats: chatsWithDetails }); - }, - - "POST /api/chats": async (req, res) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const { type, participantId, name } = await parseBody(req); - const data = readData(); - - if (type === "private") { - const existingChat = data.chats.find(c => c.type === "private" && c.participants.includes(currentUser.id) && c.participants.includes(participantId)); - if (existingChat) return json(res, { ok: true, chat: existingChat }); - - const chat = { - id: Date.now(), - type: "private", - participants: [currentUser.id, participantId], - createdAt: new Date().toISOString() - }; - data.chats.push(chat); - writeData(data); - json(res, { ok: true, chat }); - } else if (type === "group") { - const chat = { - id: Date.now(), - type: "group", - name: name || "Группа", - participants: [currentUser.id], - avatar: `https://api.dicebear.com/7.x/identicon/svg?seed=${Date.now()}`, - createdAt: new Date().toISOString() - }; - data.chats.push(chat); - writeData(data); - json(res, { ok: true, chat }); - } else if (type === "channel") { - const channel = { - id: Date.now(), - type: "channel", - name: name || "Канал", - ownerId: currentUser.id, - subscribers: [currentUser.id], - avatar: `https://api.dicebear.com/7.x/identicon/svg?seed=${Date.now()}`, - createdAt: new Date().toISOString() - }; - data.channels.push(channel); - data.chats.push({ id: channel.id, type: "channel", name: channel.name, ownerId: channel.id, subscribers: [currentUser.id], avatar: channel.avatar, createdAt: channel.createdAt }); - writeData(data); - json(res, { ok: true, chat: channel }); - } - }, - - "GET /api/chats/:id/messages": async (req, res, params) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const chatId = parseInt(params.id); - const data = readData(); - const chat = data.chats.find(c => c.id === chatId); - if (!chat || !chat.participants.includes(currentUser.id)) return json(res, { ok: false, error: "Чат не найден" }, 404); - - const messages = data.messages.filter(m => m.chatId === chatId).sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)); - - // E2E расшифровка - const userKey = hashPassword(currentUser.email + currentUser.id); - const decryptedMessages = messages.map(m => ({ - ...m, - text: decryptMessage(m.encrypted, userKey) - })); - - json(res, { ok: true, messages: decryptedMessages }); - }, - - "POST /api/chats/:id/messages": async (req, res, params) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const chatId = parseInt(params.id); - const { text, type = "text" } = await parseBody(req); - const data = readData(); - - const chat = data.chats.find(c => c.id === chatId); - if (!chat || !chat.participants.includes(currentUser.id)) return json(res, { ok: false, error: "Чат не найден" }, 404); - - // E2E шифрование - const userKey = hashPassword(currentUser.email + currentUser.id); - const encrypted = encryptMessage(text, userKey); - - const message = { - id: Date.now(), - chatId, - senderId: currentUser.id, - senderName: currentUser.username, - senderAvatar: currentUser.avatar, - text, - encrypted, - type, - createdAt: new Date().toISOString() - }; - - data.messages.push(message); - writeData(data); - json(res, { ok: true, message: { ...message, text } }); - }, - - // ========== GROUPS ========== - "POST /api/groups/:id/members": async (req, res, params) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const groupId = parseInt(params.id); - const { userId } = await parseBody(req); - const data = readData(); - - const group = data.chats.find(c => c.id === groupId && c.type === "group"); - if (!group || !group.participants.includes(currentUser.id)) return json(res, { ok: false, error: "Группа не найдена" }, 404); - - if (!group.participants.includes(userId)) { - group.participants.push(userId); - writeData(data); - } - json(res, { ok: true, group }); - }, - - // ========== CHANNELS ========== - "POST /api/channels/:id/subscribe": async (req, res, params) => { - const token = req.headers.authorization?.split(" ")[1]; - if (!token) return json(res, { ok: false, error: "Не авторизован" }, 401); - const currentUser = verifyToken(token); - if (!currentUser) return json(res, { ok: false, error: "Сессия истекла" }, 401); - - const channelId = parseInt(params.id); - const data = readData(); - - const channel = data.channels.find(c => c.id === channelId); - if (!channel) return json(res, { ok: false, error: "Канал не найден" }, 404); - - if (!channel.subscribers.includes(currentUser.id)) { - channel.subscribers.push(currentUser.id); - const chatIdx = data.chats.findIndex(c => c.id === channelId); - if (chatIdx !== -1 && !data.chats[chatIdx].subscribers.includes(currentUser.id)) { - data.chats[chatIdx].subscribers.push(currentUser.id); - } - writeData(data); - } - json(res, { ok: true, channel }); + console.log(`📞 Заявка: ${name}, ${phone}, ${service || 'Не указано'}`); + json(res, { ok: true, message: "Заявка отправлена! Мы свяжемся с вами в течение 15 минут." }); } }; @@ -363,41 +49,20 @@ http.createServer(async (req, res) => { const method = req.method; res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); - res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); if (method === "OPTIONS") { res.writeHead(204); return res.end(); } - // API ROUTING for (const [route, handler] of Object.entries(routes)) { const [m, p] = route.split(" "); if (method !== m) continue; - - const routeParts = p.split("/"); - const urlParts = url.split("/"); - - if (routeParts.length !== urlParts.length) continue; - - const params = {}; - let match = true; - for (let i = 0; i < routeParts.length; i++) { - if (routeParts[i].startsWith(":")) { - params[routeParts[i].slice(1)] = urlParts[i]; - } else if (routeParts[i] !== urlParts[i]) { - match = false; - break; - } - } - - if (match) { - return handler(req, res, params); - } + if (p === url) return handler(req, res); } - // STATIC FILES let file = url === "/" ? "/index.html" : url; const full = path.join(__dirname, file); @@ -408,5 +73,6 @@ http.createServer(async (req, res) => { return res.end(fs.readFileSync(full)); } - notFound(res); -}).listen(PORT, () => console.log("VibeChat server running on port " + PORT)); + res.writeHead(404, { "Content-Type": "text/plain" }); + res.end("Not Found"); +}).listen(PORT, () => console.log("Kazakhtelecom site running on port " + PORT)); diff --git a/style.css b/style.css index 07a9683..fade6c2 100644 --- a/style.css +++ b/style.css @@ -1,5 +1,5 @@ /* =================================== - APPLE-STYLE DESIGN SYSTEM + KAZAKHTELECOM — LIQUID GLASS DESIGN =================================== */ * { @@ -9,54 +9,86 @@ } :root { - --bg-primary: #000000; - --bg-secondary: #1d1d1f; - --bg-tertiary: #2d2d30; - --text-primary: #f5f5f7; - --text-secondary: #86868b; - --text-tertiary: #6e6e73; - --accent: #2997ff; - --accent-hover: #147ce5; - --gradient-1: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --gradient-2: linear-gradient(180deg, #1a1a2e 0%, #000000 100%); - --nav-height: 48px; - --transition-smooth: cubic-bezier(0.25, 1, 0.5, 1); - --transition-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); + --primary: #0066CC; + --primary-dark: #004C99; + --accent: #00C9FF; + --accent-purple: #7B2CBF; + --accent-pink: #FF006E; + --bg-dark: #0A0E1A; + --bg-card: rgba(255, 255, 255, 0.05); + --glass-bg: rgba(255, 255, 255, 0.08); + --glass-border: rgba(255, 255, 255, 0.1); + --text-primary: #FFFFFF; + --text-secondary: #B0B8C8; + --text-muted: #6B7280; + --gradient-1: linear-gradient(135deg, #0066CC 0%, #00C9FF 100%); + --gradient-2: linear-gradient(135deg, #7B2CBF 0%, #FF006E 100%); + --gradient-3: linear-gradient(180deg, #0A0E1A 0%, #1A1F2E 100%); + --blur: blur(20px); + --shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.3); + --transition: all 0.4s cubic-bezier(0.25, 1, 0.5, 1); } body { - font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", sans-serif; - background: var(--bg-primary); + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Inter", sans-serif; + background: var(--bg-dark); color: var(--text-primary); - line-height: 1.5; + line-height: 1.6; overflow-x: hidden; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } -/* Announcement Bar */ -.announcement-bar { - background: var(--bg-secondary); - padding: 12px 24px; - text-align: center; - font-size: 14px; - color: var(--text-secondary); - border-bottom: 1px solid rgba(255,255,255,0.1); - transition: all 0.4s var(--transition-smooth); +/* Liquid Background */ +.liquid-bg { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + background: var(--bg-dark); + overflow: hidden; } -.announcement-bar.hidden { - transform: translateY(-100%); - opacity: 0; +.liquid-orb { + position: absolute; + border-radius: 50%; + filter: blur(80px); + opacity: 0.5; + animation: float 20s infinite ease-in-out; } -.announcement-bar a { - color: var(--accent); - text-decoration: none; +.orb-1 { + width: 600px; + height: 600px; + background: var(--primary); + top: -200px; + right: -100px; + animation-delay: 0s; } -.announcement-bar a:hover { - text-decoration: underline; +.orb-2 { + width: 500px; + height: 500px; + background: var(--accent-purple); + bottom: -150px; + left: -100px; + animation-delay: -7s; +} + +.orb-3 { + width: 400px; + height: 400px; + background: var(--accent-pink); + top: 50%; + left: 50%; + animation-delay: -14s; +} + +@keyframes float { + 0%, 100% { transform: translate(0, 0) scale(1); } + 25% { transform: translate(50px, -50px) scale(1.1); } + 50% { transform: translate(-30px, 30px) scale(0.9); } + 75% { transform: translate(-50px, -30px) scale(1.05); } } /* Navigation */ @@ -65,38 +97,44 @@ body { top: 0; left: 0; right: 0; - height: var(--nav-height); - background: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); z-index: 1000; - border-bottom: 1px solid rgba(255,255,255,0.08); - transition: all 0.3s var(--transition-smooth); + padding: 20px 0; + background: rgba(10, 14, 26, 0.8); + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + border-bottom: 1px solid var(--glass-border); + transition: var(--transition); } .nav.scrolled { - background: rgba(0, 0, 0, 0.95); + padding: 12px 0; + background: rgba(10, 14, 26, 0.95); } .nav-container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; - padding: 0 24px; - height: 100%; + padding: 0 40px; display: flex; justify-content: space-between; align-items: center; } .logo { - font-size: 22px; - color: var(--text-primary); - text-decoration: none; - transition: opacity 0.3s; + display: flex; + align-items: center; + gap: 12px; + font-size: 24px; + font-weight: 700; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -.logo:hover { - opacity: 0.8; +.logo-icon { + font-size: 32px; + -webkit-text-fill-color: initial; } .nav-links { @@ -107,27 +145,58 @@ body { .nav-links a { color: var(--text-secondary); text-decoration: none; - font-size: 13px; - font-weight: 400; - transition: color 0.3s; - letter-spacing: -0.01em; + font-size: 15px; + font-weight: 500; + transition: var(--transition); + position: relative; +} + +.nav-links a::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 2px; + background: var(--gradient-1); + transition: var(--transition); } .nav-links a:hover { color: var(--text-primary); } -.nav-links .admin-link { - color: var(--accent); +.nav-links a:hover::after { + width: 100%; +} + +.btn-personal { + padding: 12px 24px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 980px; + color: var(--text-primary); + font-size: 15px; + font-weight: 600; + cursor: pointer; + backdrop-filter: blur(10px); + transition: var(--transition); +} + +.btn-personal:hover { + background: var(--primary); + border-color: var(--primary); + transform: translateY(-2px); } /* Hero Section */ .hero { - height: 100vh; - position: relative; + min-height: 100vh; display: flex; align-items: center; justify-content: center; + padding: 120px 40px 60px; + position: relative; overflow: hidden; } @@ -136,128 +205,160 @@ body { top: 0; left: 0; width: 100%; - height: 120%; - background: radial-gradient(ellipse at center, #2a2a3e 0%, #0a0a0f 50%, #000000 100%); - z-index: 1; - will-change: transform; + height: 100%; + z-index: 0; } -.hero-overlay { +.gradient-orb { position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: radial-gradient(ellipse at center, transparent 0%, rgba(0,0,0,0.6) 100%); - z-index: 2; + border-radius: 50%; + filter: blur(100px); + opacity: 0.4; + animation: pulse 15s infinite ease-in-out; +} + +.orb-1 { + width: 800px; + height: 800px; + background: var(--primary); + top: -300px; + right: -200px; +} + +.orb-2 { + width: 600px; + height: 600px; + background: var(--accent-purple); + bottom: -200px; + left: -100px; + animation-delay: -5s; +} + +.orb-3 { + width: 500px; + height: 500px; + background: var(--accent); + top: 50%; + left: 50%; + animation-delay: -10s; +} + +@keyframes pulse { + 0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.4; } + 50% { transform: translate(30px, -30px) scale(1.2); opacity: 0.6; } } .hero-content { position: relative; - z-index: 3; - text-align: center; + z-index: 2; max-width: 900px; - padding: 0 24px; + text-align: center; } -.hero-title { - font-size: clamp(56px, 10vw, 112px); - font-weight: 700; - letter-spacing: -0.03em; +.glass-card { + background: var(--glass-bg); + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 32px; + padding: 60px 40px; + box-shadow: var(--shadow-glass); +} + +.hero-card h1 { + font-size: clamp(48px, 8vw, 96px); + font-weight: 800; line-height: 1.05; - margin-bottom: 16px; - background: linear-gradient(180deg, #ffffff 0%, #b0b0b5 100%); + margin-bottom: 24px; + background: var(--gradient-1); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; - opacity: 0; - transform: translateY(40px); - animation: fadeUp 1s var(--transition-smooth) forwards; - animation-delay: 0.3s; + letter-spacing: -2px; } .hero-subtitle { - font-size: clamp(24px, 4vw, 32px); - color: var(--text-secondary); + font-size: clamp(24px, 4vw, 36px); + color: var(--text-primary); + margin-bottom: 16px; font-weight: 400; - margin-bottom: 40px; - opacity: 0; - transform: translateY(40px); - animation: fadeUp 1s var(--transition-smooth) forwards; - animation-delay: 0.5s; } -.hero-cta { +.hero-description { + font-size: clamp(18px, 2.5vw, 22px); + color: var(--text-secondary); + margin-bottom: 40px; + line-height: 1.7; +} + +.hero-buttons { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; - opacity: 0; - transform: translateY(40px); - animation: fadeUp 1s var(--transition-smooth) forwards; - animation-delay: 0.7s; } -.btn { - display: inline-block; - padding: 14px 28px; +.btn-primary, +.btn-secondary { + padding: 18px 36px; border-radius: 980px; font-size: 17px; - font-weight: 500; - text-decoration: none; - transition: all 0.3s var(--transition-smooth); + font-weight: 600; cursor: pointer; + transition: var(--transition); border: none; } .btn-primary { - background: var(--accent); + background: var(--gradient-1); color: white; + box-shadow: 0 10px 40px rgba(0, 102, 204, 0.4); } .btn-primary:hover { - background: var(--accent-hover); - transform: scale(1.02); + transform: translateY(-4px) scale(1.02); + box-shadow: 0 20px 60px rgba(0, 102, 204, 0.6); } .btn-secondary { background: transparent; - color: var(--accent); - border: 2px solid var(--accent); + color: var(--text-primary); + border: 2px solid var(--glass-border); + backdrop-filter: blur(10px); } .btn-secondary:hover { - background: var(--accent); - color: white; + border-color: var(--primary); + background: rgba(0, 102, 204, 0.1); + transform: translateY(-4px); } -/* Scroll Indicator */ -.scroll-indicator { +.hero-scroll { position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%); - z-index: 3; + z-index: 2; opacity: 0; animation: fadeIn 1s ease forwards; - animation-delay: 1.2s; + animation-delay: 1s; } .mouse { - width: 26px; - height: 40px; + width: 28px; + height: 44px; border: 2px solid var(--text-secondary); - border-radius: 13px; + border-radius: 16px; position: relative; } .wheel { width: 4px; - height: 8px; + height: 10px; background: var(--text-secondary); border-radius: 2px; position: absolute; - top: 8px; + top: 10px; left: 50%; transform: translateX(-50%); animation: scroll 2s infinite; @@ -265,399 +366,733 @@ body { @keyframes scroll { 0%, 100% { opacity: 1; transform: translateX(-50%) translateY(0); } - 50% { opacity: 0.5; transform: translateX(-50%) translateY(12px); } -} - -@keyframes fadeUp { - to { - opacity: 1; - transform: translateY(0); - } + 50% { opacity: 0.5; transform: translateX(-50%) translateY(14px); } } @keyframes fadeIn { to { opacity: 1; } } -/* Product Showcase */ -.product-showcase { - padding: 140px 24px; - background: var(--bg-primary); +/* Sections */ +section { + padding: 120px 40px; + max-width: 1400px; + margin: 0 auto; } -.showcase-intro { +.section-header { text-align: center; margin-bottom: 80px; } -.showcase-title { - font-size: clamp(40px, 6vw, 64px); +.section-title { + font-size: clamp(36px, 5vw, 64px); font-weight: 700; - letter-spacing: -0.02em; - margin-bottom: 12px; + letter-spacing: -1.5px; + margin-bottom: 16px; + background: linear-gradient(180deg, #FFFFFF 0%, #B0B8C8 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -.showcase-subtitle { - font-size: clamp(20px, 3vw, 28px); +.section-subtitle { + font-size: clamp(18px, 2.5vw, 22px); color: var(--text-secondary); } -.products-container { - max-width: 1400px; - margin: 0 auto; +/* Services */ +.services-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 32px; +} + +.service-card { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 24px; + padding: 40px 32px; + transition: var(--transition); + cursor: pointer; + position: relative; + overflow: hidden; +} + +.service-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--gradient-1); + opacity: 0; + transition: var(--transition); +} + +.service-card:hover { + transform: translateY(-12px); + border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4); +} + +.service-card:hover::before { + opacity: 1; +} + +.service-icon { + font-size: 56px; + margin-bottom: 24px; + display: block; +} + +.service-card h3 { + font-size: 24px; + margin-bottom: 12px; + font-weight: 600; +} + +.service-card p { + color: var(--text-secondary); + margin-bottom: 24px; + font-size: 16px; +} + +.service-features { + display: flex; + flex-direction: column; + gap: 10px; +} + +.service-feature { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + color: var(--text-secondary); +} + +.service-feature::before { + content: '✓'; + color: var(--accent); + font-weight: 700; +} + +/* Tariffs */ +.tariff-tabs { + display: flex; + gap: 12px; + justify-content: center; + margin-top: 40px; + flex-wrap: wrap; +} + +.tab { + padding: 14px 28px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 980px; + color: var(--text-secondary); + font-size: 15px; + font-weight: 600; + cursor: pointer; + backdrop-filter: blur(10px); + transition: var(--transition); +} + +.tab.active, +.tab:hover { + background: var(--gradient-1); + border-color: transparent; + color: white; +} + +.tariffs-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 32px; +} + +.tariff-card { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 24px; + padding: 40px 32px; + position: relative; + transition: var(--transition); + display: flex; + flex-direction: column; +} + +.tariff-card.popular { + border-color: var(--primary); + box-shadow: 0 0 40px rgba(0, 102, 204, 0.3); +} + +.tariff-card.popular::before { + content: 'Популярный'; + position: absolute; + top: 16px; + right: 16px; + padding: 6px 14px; + background: var(--gradient-1); + border-radius: 980px; + font-size: 12px; + font-weight: 600; +} + +.tariff-card:hover { + transform: translateY(-8px); + border-color: rgba(255, 255, 255, 0.2); +} + +.tariff-name { + font-size: 24px; + font-weight: 700; + margin-bottom: 8px; +} + +.tariff-speed, +.tariff-channels { + font-size: 18px; + color: var(--accent); + margin-bottom: 20px; + font-weight: 500; +} + +.tariff-price { + font-size: 42px; + font-weight: 800; + margin-bottom: 24px; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.tariff-price span { + font-size: 18px; + -webkit-text-fill-color: var(--text-secondary); +} + +.tariff-features { + flex: 1; + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 32px; +} + +.tariff-feature { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + color: var(--text-secondary); +} + +.tariff-feature::before { + content: '✓'; + color: var(--accent); + font-weight: 700; + font-size: 16px; +} + +.btn-tariff { + width: 100%; + padding: 16px; + background: var(--gradient-1); + border: none; + border-radius: 12px; + color: white; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: var(--transition); +} + +.btn-tariff:hover { + transform: translateY(-2px); + box-shadow: 0 10px 30px rgba(0, 102, 204, 0.4); +} + +/* Bundles */ +.bundles-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 32px; } -.product-card { - background: var(--bg-secondary); +.bundle-card { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); border-radius: 24px; - overflow: hidden; - transition: all 0.6s var(--transition-smooth); - opacity: 0; - transform: translateY(40px); -} - -.product-card.visible { - opacity: 1; - transform: translateY(0); -} - -.product-card:hover { - transform: translateY(-12px); - box-shadow: 0 30px 60px rgba(0,0,0,0.4); -} - -.product-image { - width: 100%; - height: 280px; - object-fit: cover; - transition: transform 0.6s var(--transition-smooth); -} - -.product-card:hover .product-image { - transform: scale(1.08); -} - -.product-content { - padding: 32px; -} - -.product-name { - font-size: 28px; - font-weight: 600; - margin-bottom: 8px; - letter-spacing: -0.01em; -} - -.product-tagline { - color: var(--text-secondary); - font-size: 16px; - margin-bottom: 16px; -} - -.product-price { - font-size: 20px; - font-weight: 600; - color: var(--accent); - margin-bottom: 20px; -} - -.product-features { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.feature-tag { - background: var(--bg-tertiary); - color: var(--text-secondary); - padding: 6px 14px; - border-radius: 980px; - font-size: 13px; - font-weight: 500; -} - -/* Features Section */ -.features { - position: relative; - padding: 140px 24px; - overflow: hidden; -} - -.features-bg { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 120%; - background: url('https://picsum.photos/1920/1200?random=100') center/cover; - opacity: 0.15; - will-change: transform; -} - -.features-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(180deg, var(--bg-primary) 0%, rgba(0,0,0,0.8) 100%); -} - -.features-content { - position: relative; - z-index: 2; - max-width: 1400px; - margin: 0 auto; -} - -.section-title { - font-size: clamp(40px, 6vw, 64px); - font-weight: 700; - text-align: center; - margin-bottom: 80px; - letter-spacing: -0.02em; -} - -.features-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 32px; -} - -.feature-card { - background: rgba(29, 29, 31, 0.6); - backdrop-filter: blur(10px); - border-radius: 20px; padding: 40px 32px; - text-align: center; - border: 1px solid rgba(255,255,255,0.05); - transition: all 0.4s var(--transition-smooth); + position: relative; + transition: var(--transition); } -.feature-card:hover { - background: rgba(29, 29, 31, 0.8); +.bundle-card.popular { + border-color: var(--accent-purple); + box-shadow: 0 0 40px rgba(123, 44, 191, 0.3); +} + +.bundle-card.popular::before { + content: 'Выгодно'; + position: absolute; + top: 16px; + right: 16px; + padding: 6px 14px; + background: var(--gradient-2); + border-radius: 980px; + font-size: 12px; + font-weight: 600; +} + +.bundle-card:hover { transform: translateY(-8px); } -.feature-icon { - font-size: 48px; - margin-bottom: 20px; -} - -.feature-card h3 { - font-size: 22px; +.bundle-name { + font-size: 28px; + font-weight: 700; margin-bottom: 12px; - font-weight: 600; } -.feature-card p { +.bundle-description { color: var(--text-secondary); + margin-bottom: 24px; font-size: 16px; } -/* Vision Section */ -.vision { - padding: 140px 24px; - background: var(--bg-secondary); - display: grid; - grid-template-columns: 1fr 1fr; - gap: 60px; - max-width: 1400px; - margin: 0 auto; - align-items: center; +.bundle-pricing { + margin-bottom: 24px; } -.vision-content { - padding: 40px; +.bundle-original { + font-size: 20px; + color: var(--text-muted); + text-decoration: line-through; + margin-bottom: 8px; } -.vision-title { - font-size: clamp(40px, 5vw, 64px); - font-weight: 700; - margin-bottom: 16px; - letter-spacing: -0.02em; +.bundle-price { + font-size: 48px; + font-weight: 800; + background: var(--gradient-2); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -.vision-subtitle { - font-size: clamp(20px, 3vw, 26px); - color: var(--text-secondary); - margin-bottom: 48px; +.bundle-discount { + display: inline-block; + margin-left: 12px; + padding: 6px 14px; + background: rgba(255, 0, 110, 0.2); + border: 1px solid var(--accent-pink); + border-radius: 980px; + font-size: 14px; + font-weight: 600; + color: var(--accent-pink); } -.vision-features { +.bundle-includes { display: flex; flex-direction: column; - gap: 24px; -} - -.vision-feature { - display: flex; - gap: 20px; - align-items: flex-start; -} - -.vision-feature-icon { - font-size: 32px; - flex-shrink: 0; -} - -.vision-feature h4 { - font-size: 20px; - margin-bottom: 6px; - font-weight: 600; -} - -.vision-feature p { - color: var(--text-secondary); - font-size: 16px; -} - -.vision-image { - border-radius: 24px; - overflow: hidden; -} - -.vision-image img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} - -/* Scroll Gallery */ -.scroll-gallery { - position: relative; -} - -.gallery-item { - position: relative; - height: 80vh; - overflow: hidden; -} - -.gallery-item img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} - -.gallery-caption { - position: absolute; - bottom: 60px; - left: 60px; - z-index: 2; -} - -.gallery-caption h3 { - font-size: clamp(32px, 5vw, 56px); - font-weight: 700; - margin-bottom: 8px; - text-shadow: 0 4px 20px rgba(0,0,0,0.5); -} - -.gallery-caption p { - font-size: 18px; - color: rgba(255,255,255,0.9); - text-shadow: 0 2px 10px rgba(0,0,0,0.5); -} - -/* CTA Section */ -.cta-section { - padding: 140px 24px; - text-align: center; - background: var(--gradient-2); -} - -.cta-title { - font-size: clamp(40px, 6vw, 72px); - font-weight: 700; - margin-bottom: 16px; - letter-spacing: -0.02em; -} - -.cta-subtitle { - font-size: clamp(20px, 3vw, 26px); - color: var(--text-secondary); - max-width: 600px; - margin: 0 auto 48px; -} - -.cta-buttons { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -/* Footer */ -footer { - padding: 60px 24px; - border-top: 1px solid rgba(255,255,255,0.1); - background: var(--bg-secondary); -} - -.footer-content { - max-width: 1200px; - margin: 0 auto; -} - -.footer-links { - display: flex; - justify-content: center; - gap: 32px; - flex-wrap: wrap; + gap: 12px; margin-bottom: 32px; } -.footer-links a { +.bundle-item { + display: flex; + align-items: center; + gap: 12px; + font-size: 15px; color: var(--text-secondary); - text-decoration: none; - font-size: 13px; - transition: color 0.3s; } -.footer-links a:hover { +.bundle-item::before { + content: '📦'; + font-size: 18px; +} + +/* Stats */ +.stats { + background: linear-gradient(180deg, transparent 0%, rgba(0, 102, 204, 0.05) 100%); + border-top: 1px solid var(--glass-border); + border-bottom: 1px solid var(--glass-border); +} + +.stats-container { + max-width: 1400px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 60px; + padding: 60px 40px; +} + +.stat-item { + text-align: center; +} + +.stat-number { + font-size: clamp(48px, 6vw, 72px); + font-weight: 800; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 12px; +} + +.stat-number::after { + content: '+'; + -webkit-text-fill-color: var(--accent); +} + +.stat-label { + font-size: 16px; + color: var(--text-secondary); +} + +/* News */ +.news-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + gap: 32px; +} + +.news-card { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 24px; + overflow: hidden; + transition: var(--transition); + cursor: pointer; +} + +.news-card:hover { + transform: translateY(-8px); + border-color: rgba(255, 255, 255, 0.2); +} + +.news-image { + width: 100%; + height: 220px; + object-fit: cover; +} + +.news-content { + padding: 28px; +} + +.news-meta { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + font-size: 13px; + color: var(--text-muted); +} + +.news-category { + padding: 4px 12px; + background: rgba(0, 102, 204, 0.2); + border-radius: 980px; + color: var(--accent); + text-transform: capitalize; +} + +.news-card h3 { + font-size: 20px; + font-weight: 600; + margin-bottom: 12px; + line-height: 1.4; +} + +.news-card p { + color: var(--text-secondary); + font-size: 15px; +} + +/* FAQ */ +.faq-container { + max-width: 900px; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: 16px; +} + +.faq-item { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 16px; + overflow: hidden; + transition: var(--transition); +} + +.faq-item.active { + border-color: var(--primary); + background: rgba(0, 102, 204, 0.05); +} + +.faq-question { + width: 100%; + padding: 24px 28px; + background: transparent; + border: none; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + text-align: left; + font-size: 18px; + font-weight: 600; color: var(--text-primary); } -.copyright { - text-align: center; - color: var(--text-tertiary); +.faq-icon { + font-size: 24px; + transition: var(--transition); +} + +.faq-item.active .faq-icon { + transform: rotate(45deg); +} + +.faq-answer { + max-height: 0; + overflow: hidden; + transition: max-height 0.4s ease; +} + +.faq-item.active .faq-answer { + max-height: 300px; +} + +.faq-answer p { + padding: 0 28px 24px; + color: var(--text-secondary); + line-height: 1.7; +} + +/* Contact Form */ +.contact-form-container { + max-width: 600px; + margin: 0 auto; +} + +.contact-form { + background: var(--glass-bg); + backdrop-filter: var(--blur); + border: 1px solid var(--glass-border); + border-radius: 24px; + padding: 40px; +} + +.form-group { + margin-bottom: 24px; +} + +.form-group label { + display: block; + font-size: 14px; + color: var(--text-secondary); + margin-bottom: 8px; + font-weight: 500; +} + +.form-group input, +.form-group select { + width: 100%; + padding: 16px 20px; + background: rgba(0, 0, 0, 0.3); + border: 1px solid var(--glass-border); + border-radius: 12px; + color: var(--text-primary); + font-size: 16px; + transition: var(--transition); +} + +.form-group input:focus, +.form-group select:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1); +} + +.btn-submit { + width: 100%; + padding: 18px; + background: var(--gradient-1); + border: none; + border-radius: 12px; + color: white; + font-size: 17px; + font-weight: 600; + cursor: pointer; + transition: var(--transition); +} + +.btn-submit:hover { + transform: translateY(-2px); + box-shadow: 0 10px 40px rgba(0, 102, 204, 0.4); +} + +/* Footer */ +.footer { + background: rgba(5, 8, 16, 0.8); + backdrop-filter: var(--blur); + border-top: 1px solid var(--glass-border); + padding: 80px 40px 40px; +} + +.footer-container { + max-width: 1400px; + margin: 0 auto; + display: grid; + grid-template-columns: 2fr 1fr 1fr 1.5fr; + gap: 60px; + margin-bottom: 60px; +} + +.footer-logo { + display: flex; + align-items: center; + gap: 12px; + font-size: 24px; + font-weight: 700; + background: var(--gradient-1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 16px; +} + +.footer-description { + color: var(--text-secondary); + margin-bottom: 24px; + font-size: 15px; +} + +.social-links { + display: flex; + gap: 12px; + flex-wrap: wrap; +} + +.social-link { + padding: 10px 18px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 980px; + color: var(--text-secondary); font-size: 13px; + text-decoration: none; + transition: var(--transition); } -/* Reveal Animation */ -.reveal { - opacity: 0; - transform: translateY(50px); - transition: all 1s var(--transition-smooth); +.social-link:hover { + border-color: var(--primary); + color: var(--text-primary); + background: rgba(0, 102, 204, 0.1); } -.reveal.active { - opacity: 1; - transform: translateY(0); +.footer-column h4 { + font-size: 16px; + font-weight: 600; + margin-bottom: 20px; + color: var(--text-primary); +} + +.footer-column a { + display: block; + color: var(--text-secondary); + text-decoration: none; + font-size: 15px; + margin-bottom: 12px; + transition: var(--transition); +} + +.footer-column a:hover { + color: var(--text-primary); + transform: translateX(4px); +} + +.contact-info { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 16px; +} + +.contact-label { + font-size: 13px; + color: var(--text-muted); +} + +.contact-value { + font-size: 15px; + color: var(--text-primary); + text-decoration: none; +} + +.contact-value:hover { + color: var(--accent); +} + +.footer-bottom { + max-width: 1400px; + margin: 0 auto; + padding-top: 40px; + border-top: 1px solid var(--glass-border); + text-align: center; + color: var(--text-muted); + font-size: 14px; } /* Responsive */ -@media (max-width: 768px) { - .nav-links { - gap: 20px; - } - - .vision { - grid-template-columns: 1fr; - } - - .products-container { - grid-template-columns: 1fr; - } - - .gallery-caption { - left: 24px; - bottom: 40px; +@media (max-width: 1024px) { + .footer-container { + grid-template-columns: 1fr 1fr; + } +} + +@media (max-width: 768px) { + .nav-links { + display: none; + } + + .hero-card { + padding: 40px 24px; + } + + section { + padding: 80px 24px; + } + + .services-grid, + .tariffs-grid, + .bundles-grid, + .news-grid { + grid-template-columns: 1fr; + } + + .footer-container { + grid-template-columns: 1fr; + gap: 40px; + } + + .stat-number::after { + content: '+'; } }