Compare commits

..

No commits in common. "feature/gui-refactor" and "main" have entirely different histories.

10 changed files with 1065 additions and 2371 deletions

View File

@ -10,24 +10,16 @@
## 📌 О проекте
Лендинг-страница для VPN-сервиса **DmitriyVPN**, полностью переработанная в соответствии с дизайн-системой **Surfshark** (`DESIGN.md`).
Дизайн выполнен в концепции светлого SaaS с контрастными тёмными панелями (ритм «dark → light → dark»), фирменным акцентом Surfshark Teal (`#1ebfbf`), конверсионными кнопками Coral Pulse (`#fa3556`) и промо-баннером Promo Gold (`#ffc200`).
Лендинг-страница для VPN-сервиса **DmitriyVPN**. Сайт рассказывает о тарифах, преимуществах сервиса и помогает пользователям быстро подключиться через Telegram-бот.
---
## 🛠 Технологии и дизайн-система
## 🛠 Технологии
- **HTML5** — семантическая разметка с чистой иерархией
- **CSS3** — полная система токенов Surfshark Design System:
- Шрифтовая система: исключительно **Inter** (веса 400, 600, 700; дисплейный трекинг -1.5px на 60px)
- Цветовая палитра: Surfshark Teal (`#1ebfbf`), Coral Pulse (`#fa3556`), Promo Gold (`#ffc200`), Charcoal Ink (`#16191c`), Tide Tint (`#e8f7f8`), Ash Gray (`#dadadd`), Deep Abyss (`#1e2327`)
- Скругления: 48px для карточек, 12px для стандартных кнопок, 64px для Hero-карточки, 96px для pill-элементов
- Плоский современный стиль без теней (elevation через контраст и 1px hairline-границы)
- **Vanilla JS** — интерактивность:
- Динамический переключатель периодов тарифов (1 мес, 3 мес со скидкой -10%, 12 мес со скидкой -20%)
- Интерактивный плавный аккордеон FAQ
- Адаптивное бургер-меню для мобильных устройств
- Плавная анимация появления контента при скролле (Scroll Reveal)
- **HTML5** — семантическая разметка
- **CSS3** — стили, анимации, адаптив
- **Vanilla JS** — интерактивность (аккордеон FAQ, бургер-меню, тогл тарифов, scroll reveal)
- **Google Fonts** — Inter, Fira Code
---
@ -35,10 +27,9 @@
```
dmitriyvpn/
├── DESIGN.md # Спецификация дизайн-системы Surfshark
├── index.html # Основная HTML-разметка страницы
├── styles.css # Таблица стилей (токены и компоненты)
├── script.js # Клиентские скрипты интерактивности
├── index.html # Основная HTML-разметка
├── styles.css # Таблица стилей
├── script.js # Клиентские скрипты
└── README.md # Документация проекта
```
@ -47,17 +38,19 @@ dmitriyvpn/
## 🚀 Запуск локально
```bash
# Открыть страницу в браузеру
# Открыть страницу в браузере
start index.html
```
Или через расширение **Live Server** в VS Code.
---
## 📋 Тарифы
| Тариф | Устройства | Цена |
|---|---|---|
| Бесплатный | 1 | 0 ₽ / навсегда |
| Бесплатный | 1 | 0 ₽ |
| Standard | 1 | от 87.38 до 109.22 ₽/мес |
| Pro | 5 | от 155.53 до 194.41 ₽/мес |
| Premium | 15 | от 233.30 до 291.62 ₽/мес |

View File

@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<!-- TV Body -->
<rect x="2" y="5" width="28" height="18" rx="3" stroke="#1ebfbf" stroke-width="2.5"/>
<!-- TV Stand -->
<path d="M12 28L14.5 23H17.5L20 28" stroke="#1ebfbf" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Play triangle inside TV -->
<polygon points="14,11 20,14 14,17" fill="#1ebfbf"/>
</svg>

Before

Width:  |  Height:  |  Size: 443 B

View File

@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<!-- Antennae -->
<line x1="10" y1="5" x2="7" y2="1.5" stroke="#1ebfbf" stroke-width="2" stroke-linecap="round"/>
<line x1="22" y1="5" x2="25" y2="1.5" stroke="#1ebfbf" stroke-width="2" stroke-linecap="round"/>
<!-- Head -->
<path d="M7 13C7 8.03 11.03 4 16 4C20.97 4 25 8.03 25 13H7Z" fill="#1ebfbf"/>
<circle cx="12" cy="9" r="1.25" fill="#ffffff"/>
<circle cx="20" cy="9" r="1.25" fill="#ffffff"/>
<!-- Body -->
<path d="M7 15C7 14.45 7.45 14 8 14H24C24.55 14 25 14.45 25 15V24C25 25.1 24.1 26 23 26H9C7.9 26 7 25.1 7 24V15Z" fill="#1ebfbf"/>
<!-- Arms -->
<rect x="3" y="15" width="2.5" height="8" rx="1.25" fill="#1ebfbf"/>
<rect x="26.5" y="15" width="2.5" height="8" rx="1.25" fill="#1ebfbf"/>
<!-- Legs -->
<rect x="11" y="26" width="2.5" height="5" rx="1.25" fill="#1ebfbf"/>
<rect x="18.5" y="26" width="2.5" height="5" rx="1.25" fill="#1ebfbf"/>
</svg>

Before

Width:  |  Height:  |  Size: 990 B

View File

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<rect x="7" y="2" width="18" height="28" rx="4.5" stroke="#1ebfbf" stroke-width="2.5"/>
<line x1="13" y1="5.5" x2="19" y2="5.5" stroke="#1ebfbf" stroke-width="2" stroke-linecap="round"/>
<circle cx="16" cy="25.5" r="1.5" fill="#1ebfbf"/>
</svg>

Before

Width:  |  Height:  |  Size: 347 B

View File

@ -1,12 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<!-- Tux Penguin silhouette -->
<path d="M16 2C12.5 2 10.5 4.5 10.5 8C10.5 9.5 10 11.5 8.5 13C6.5 15 5 18 5 21C5 24.5 7.5 27 11 27C12.5 27 13.5 26.5 16 26.5C18.5 26.5 19.5 27 21 27C24.5 27 27 24.5 27 21C27 18 25.5 15 23.5 13C22 11.5 21.5 9.5 21.5 8C21.5 4.5 19.5 2 16 2Z" fill="#1ebfbf"/>
<ellipse cx="16" cy="18" rx="6" ry="7.5" fill="#ffffff" opacity="0.3"/>
<circle cx="13.5" cy="7.5" r="1.5" fill="#ffffff"/>
<circle cx="18.5" cy="7.5" r="1.5" fill="#ffffff"/>
<!-- Beak -->
<path d="M14 9.5C14 9.5 15.5 11.5 16 11.5C16.5 11.5 18 9.5 18 9.5L14 9.5Z" fill="#ffc200"/>
<!-- Feet -->
<path d="M7 27C6 27 6 29 8 30C10 31 13 30 13 28.5L7 27Z" fill="#ffc200"/>
<path d="M25 27C26 27 26 29 24 30C22 31 19 30 19 28.5L25 27Z" fill="#ffc200"/>
</svg>

Before

Width:  |  Height:  |  Size: 861 B

View File

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<path d="M19.8 4.2C20.9 2.8 21.6 0.9 21.4 0C19.7 0.1 17.6 1.1 16.5 2.5C15.5 3.7 14.6 5.6 14.9 7.4C16.8 7.5 18.7 6.4 19.8 4.2Z" fill="#1ebfbf"/>
<path d="M21.5 16.3C21.5 12.3 24.8 10.3 24.9 10.2C23 7.5 20.2 7.1 19.2 7C16.8 6.8 14.4 8.5 13.2 8.5C11.9 8.5 10 7.1 8 7.1C5.4 7.1 3 8.6 1.7 10.9C-1 15.6 1 22.5 3.6 26.3C4.9 28.1 6.3 30.1 8.3 30C10.2 29.9 11 28.7 13.2 28.7C15.4 28.7 16.1 29.9 18.1 30C20.2 30.1 21.5 28.2 22.8 26.4C24.3 24.2 24.9 22.1 25 22C24.9 21.9 21.5 20.6 21.5 16.3Z" fill="#1ebfbf"/>
</svg>

Before

Width:  |  Height:  |  Size: 606 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none">
<path d="M3 6.8L13.8 5.3V15H3V6.8Z" fill="#1ebfbf"/>
<path d="M15.8 5V3.3L29 1.5V15H15.8V5Z" fill="#1ebfbf"/>
<path d="M3 17H13.8V26.7L3 25.2V17Z" fill="#1ebfbf"/>
<path d="M15.8 17H29V30.5L15.8 28.7V17Z" fill="#1ebfbf"/>
</svg>

Before

Width:  |  Height:  |  Size: 333 B

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,7 @@
/* ==========================================================================
DmitriyVPN Client Scripts
Interactive UI: Theme Toggle, Burger Menu, Billing Toggle, FAQ, Scroll Reveal
========================================================================== */
/* ===== THEME TOGGLE (Light standard, Dark toggleable) ===== */
function initTheme() {
const savedTheme = localStorage.getItem('dmitriyvpn_theme');
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
}
}
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme') || 'light';
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('dmitriyvpn_theme', next);
}
// Initialize theme immediately
initTheme();
/* ===== BURGER & MOBILE NAVIGATION ===== */
/* ===== BURGER MENU ===== */
function toggleMobile() {
const burger = document.getElementById('burger');
const nav = document.getElementById('mobileNav');
if (!burger || !nav) return;
const isOpen = burger.classList.toggle('open');
nav.classList.toggle('open', isOpen);
document.body.style.overflow = isOpen ? 'hidden' : '';
@ -41,11 +15,6 @@ function closeMobile() {
document.body.style.overflow = '';
}
// Close mobile nav on escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeMobile();
});
/* ===== SCROLL REVEAL ===== */
const revealObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
@ -60,18 +29,16 @@ document.querySelectorAll('.reveal').forEach(el => revealObserver.observe(el));
/* ===== BILLING TOGGLE ===== */
const prices = {
month: { standard: 109.22, pro: 194.41, premium: 291.62 },
month: { standard: 109.22, pro: 194.41, premium: 291.62 },
'3month': { standard: 98.30, pro: 174.97, premium: 262.46 },
year: { standard: 87.38, pro: 155.53, premium: 233.30 }
year: { standard: 87.38, pro: 155.53, premium: 233.30 }
};
function setBilling(period, btn) {
document.querySelectorAll('.billing-opt-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.billing-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const p = prices[period];
if (!p) return;
const els = {
'price-from': p.standard,
'price-standard': p.standard,
@ -86,33 +53,27 @@ function setBilling(period, btn) {
setTimeout(() => {
el.textContent = val.toFixed(2);
el.classList.remove('updating');
}, 120);
}, 150);
}
});
}
/* ===== FAQ ACCORDION ===== */
function toggleFaq(btn) {
const item = btn.closest('.faq-item');
if (!item) return;
const answer = item.querySelector('.faq-answer-collapse');
const item = btn.parentElement;
const answer = item.querySelector('.faq-a');
const isOpen = item.classList.contains('open');
// Close all other open FAQ items
// Close all
document.querySelectorAll('.faq-item').forEach(i => {
if (i !== item) {
i.classList.remove('open');
const a = i.querySelector('.faq-answer-collapse');
if (a) a.style.maxHeight = null;
}
i.classList.remove('open');
const a = i.querySelector('.faq-a');
if (a) a.style.maxHeight = null;
});
// Toggle current item
// Open if was closed
if (!isOpen && answer) {
item.classList.add('open');
answer.style.maxHeight = answer.scrollHeight + 'px';
} else if (answer) {
item.classList.remove('open');
answer.style.maxHeight = null;
}
}

2339
styles.css

File diff suppressed because it is too large Load Diff