tehnicheskoe-zadanie-sayt-eu/index.html

653 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Europe Trip — 15 дней | Маршрут из Астаны</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700&family=Unbounded:wght@700&display=swap" rel="stylesheet">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🇪🇺</text></svg>">
<style>
:root{
--bg: #F7F7F8;
--ink: #172033;
--accent: #6C5CE7;
--accent-light: #FFB84D;
--white: #FFFFFF;
--gray-100: #E8E8EA;
--gray-200: #D0D0D3;
--gray-500: #6B7280;
--shadow: 0 4px 20px rgba(0,0,0,0.08);
--shadow-lg: 0 10px 40px rgba(0,0,0,0.12);
--radius: 16px;
--radius-sm: 8px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font:17px/1.6 'Manrope',-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--ink);background:var(--bg);overflow-x:hidden}
h1,h2,h3,h4{font:'Unbounded',sans-serif;line-height:1.2}
a{text-decoration:none;color:inherit}
img{max-width:100%;display:block;border-radius:var(--radius)}
.container{max-width:1200px;margin:0 auto;padding:0 20px}
.section{padding:80px 0}
.section-title{font-size:36px;font-weight:700;margin-bottom:40px;text-align:center}
.btn{display:inline-block;background:var(--accent);color:var(--white);padding:16px 32px;border-radius:var(--radius-sm);font-weight:700;font-size:16px;cursor:pointer;border:none;transition:all 0.3s ease}
.btn:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(108,92,231,0.4)}
.btn-secondary{background:var(--white);color:var(--accent);border:2px solid var(--accent)}
.btn-secondary:hover{background:var(--accent);color:var(--white)}
/* Header */
.header{position:fixed;top:0;left:0;right:0;background:rgba(255,255,255,0.95);backdrop-filter:blur(10px);z-index:1000;box-shadow:0 2px 10px rgba(0,0,0,0.05)}
.header-inner{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;max-width:1400px;margin:0 auto}
.logo{font:'Unbounded',sans-serif;font-size:20px;font-weight:700;color:var(--accent)}
.nav{display:flex;gap:24px}
.nav a{font-size:14px;font-weight:600;color:var(--gray-500);transition:color 0.3s}
.nav a:hover{color:var(--accent)}
.menu-toggle{display:none;background:none;border:none;font-size:24px;cursor:pointer}
/* Hero */
.hero{position:relative;height:100vh;min-height:700px;display:flex;align-items:center;justify-content:center;overflow:hidden}
.hero-bg{position:absolute;inset:0;background:linear-gradient(135deg, var(--accent) 0%, #a78bfa 100%)}
.hero-pattern{position:absolute;inset:0;opacity:0.1;background-image:radial-gradient(circle at 2px 2px, white 1px, transparent 0);background-size:40px 40px}
.hero-content{position:relative;z-index:2;text-align:center;color:var(--white);max-width:900px;padding:0 20px}
.hero h1{font-size:clamp(36px, 8vw, 72px);font-weight:700;margin-bottom:24px;line-height:1.1}
.hero p{font-size:clamp(18px, 3vw, 24px);margin-bottom:32px;opacity:0.95}
.hero-route{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin-bottom:40px;font-size:clamp(14px, 2vw, 18px)}
.hero-route span{background:rgba(255,255,255,0.2);padding:8px 16px;border-radius:20px}
.hero-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}
.hero-buttons .btn{padding:18px 36px;font-size:18px}
/* Route */
.route-visual{display:flex;align-items:center;justify-content:center;gap:20px;margin:40px 0;flex-wrap:wrap}
.route-city{text-align:center;padding:20px;background:var(--white);border-radius:var(--radius);box-shadow:var(--shadow);min-width:140px;transition:transform 0.3s}
.route-city:hover{transform:translateY(-5px)}
.route-city .flag{font-size:48px;display:block;margin-bottom:8px}
.route-city .name{font-weight:700;font-size:16px}
.route-arrow{font-size:32px;color:var(--accent);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}
/* City Cards */
.cities-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:24px}
.city-card{background:var(--white);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);transition:all 0.3s ease;cursor:pointer}
.city-card:hover{transform:translateY(-8px);box-shadow:var(--shadow-lg)}
.city-card-img{height:200px;object-fit:cover;width:100%}
.city-card-body{padding:24px}
.city-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
.city-card-title{font-size:22px;font-weight:700}
.city-card-nights{color:var(--accent);font-weight:600}
.city-card-price{color:var(--gray-500);margin-bottom:16px}
.city-card-district{font-size:14px;color:var(--gray-500);margin-bottom:16px}
/* Timeline */
.timeline{position:relative;max-width:800px;margin:0 auto}
.timeline::before{content:'';position:absolute;left:30px;top:0;bottom:0;width:3px;background:linear-gradient(to bottom, var(--accent), var(--accent-light))}
.timeline-item{position:relative;padding-left:80px;margin-bottom:40px;opacity:0;transform:translateY(30px);transition:all 0.6s ease}
.timeline-item.in{opacity:1;transform:none}
.timeline-marker{position:absolute;left:0;width:60px;height:60px;background:var(--white);border:3px solid var(--accent);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;box-shadow:var(--shadow)}
.timeline-content{background:var(--white);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow)}
.timeline-day{font-size:14px;color:var(--accent);font-weight:700;margin-bottom:8px}
.timeline-title{font-size:20px;font-weight:700;margin-bottom:12px}
.timeline-activities{list-style:none;margin-bottom:16px}
.timeline-activities li{padding:6px 0;padding-left:20px;position:relative;color:var(--gray-500)}
.timeline-activities li::before{content:'•';position:absolute;left:0;color:var(--accent)}
.timeline-cost{font-weight:700;color:var(--accent-light)}
/* Attractions */
.attractions-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:24px}
.attraction-card{background:var(--white);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);transition:transform 0.3s}
.attraction-card:hover{transform:translateY(-5px)}
.attraction-img{height:240px;object-fit:cover;width:100%}
.attraction-body{padding:24px}
.attraction-name{font-size:20px;font-weight:700;margin-bottom:8px}
.attraction-rating{color:var(--accent-light);margin-bottom:12px}
.attraction-desc{color:var(--gray-500);margin-bottom:16px;line-height:1.5}
.attraction-price{font-weight:700;color:var(--accent)}
/* Hotels */
.hotels-section{background:var(--white)}
.hotels-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:24px}
.hotel-card{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
.hotel-card-img{height:200px;object-fit:cover;width:100%}
.hotel-card-body{padding:24px}
.hotel-category{display:inline-block;padding:6px 12px;border-radius:20px;font-size:12px;font-weight:700;margin-bottom:12px}
.hotel-category.economy{background:#d1fae5;color:#065f46}
.hotel-category.comfort{background:#dbeafe;color:#1e40af}
.hotel-category.premium{background:#ede9fe;color:#5b21b6}
.hotel-name{font-size:20px;font-weight:700;margin-bottom:8px}
.hotel-district{color:var(--gray-500);margin-bottom:12px}
.hotel-price{font-size:18px;font-weight:700;color:var(--accent)}
/* Transport */
.transport-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:20px}
.transport-card{background:var(--white);border-radius:var(--radius);padding:24px;display:flex;align-items:center;gap:16px;box-shadow:var(--shadow);transition:transform 0.3s}
.transport-card:hover{transform:translateX(5px)}
.transport-icon{font-size:40px}
.transport-info{flex:1}
.transport-route{font-weight:700;margin-bottom:4px}
.transport-type{color:var(--gray-500);font-size:14px}
.transport-details{text-align:right}
.transport-duration{font-size:14px;color:var(--gray-500)}
.transport-price{font-weight:700;color:var(--accent)}
/* Budget Calculator */
.budget-section{background:linear-gradient(135deg, var(--accent) 0%, #a78bfa 100%);color:var(--white)}
.budget-section .section-title{color:var(--white)}
.calculator{background:var(--white);border-radius:var(--radius);padding:40px;box-shadow:var(--shadow-lg);max-width:900px;margin:0 auto}
.calculator-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:24px;margin-bottom:32px}
.calculator-item label{display:block;font-weight:700;margin-bottom:12px;color:var(--ink)}
.calculator-item select,.calculator-item input{width:100%;padding:14px;border:2px solid var(--gray-200);border-radius:var(--radius-sm);font-size:16px;font-family:inherit;cursor:pointer}
.calculator-item select:focus,.calculator-item input:focus{outline:none;border-color:var(--accent)}
.budget-result{background:var(--bg);border-radius:var(--radius);padding:32px}
.budget-breakdown{margin-bottom:24px}
.budget-line{display:flex;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--gray-200)}
.budget-line:last-child{border:none}
.budget-label{display:flex;align-items:center;gap:12px}
.budget-label span:first-child{font-size:20px}
.budget-total{border-top:3px solid var(--ink);margin-top:16px;padding-top:24px}
.budget-total .budget-line{font-size:24px;font-weight:700}
.budget-per-person{font-size:18px;color:var(--accent);margin-top:16px;text-align:center}
/* Budget Cards */
.budget-cards{display:grid;grid-template-columns:repeat(auto-fit, minmax(150px, 1fr));gap:20px}
.budget-card{background:var(--white);border-radius:var(--radius);padding:24px;text-align:center;box-shadow:var(--shadow);transition:all 0.3s}
.budget-card:hover{transform:scale(1.05)}
.budget-card-icon{font-size:48px;margin-bottom:16px}
.budget-card-title{font-weight:700}
/* Tips */
.tips-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));gap:24px}
.tip-card{background:var(--white);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow)}
.tip-icon{font-size:40px;margin-bottom:16px}
.tip-title{font-size:20px;font-weight:700;margin-bottom:12px}
.tip-text{color:var(--gray-500)}
/* Checklist */
.checklist-section{background:var(--white)}
.checklist-container{max-width:600px;margin:0 auto}
.checklist-progress{margin-bottom:32px}
.checklist-progress-bar{height:12px;background:var(--gray-200);border-radius:6px;overflow:hidden;margin-top:12px}
.checklist-progress-fill{height:100%;background:linear-gradient(90deg, var(--accent), var(--accent-light));transition:width 0.3s}
.checklist-items{list-style:none}
.checklist-item{display:flex;align-items:center;padding:16px;border-bottom:1px solid var(--gray-200);cursor:pointer;transition:background 0.3s}
.checklist-item:hover{background:var(--bg)}
.checklist-item:last-child{border:none}
.checklist-checkbox{width:24px;height:24px;border:2px solid var(--gray-500);border-radius:6px;margin-right:16px;display:flex;align-items:center;justify-content:center;transition:all 0.3s}
.checklist-item.done .checklist-checkbox{background:var(--accent);border-color:var(--accent)}
.checklist-item.done .checklist-checkbox::after{content:'✓';color:var(--white);font-size:16px;font-weight:700}
.checklist-item.done .checklist-text{text-decoration:line-through;color:var(--gray-500)}
.checklist-text{flex:1;font-weight:600}
/* Map */
.map-section{background:var(--white)}
.map-container{height:500px;border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
/* Modal */
.modal{position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:2000;display:none;align-items:center;justify-content:center;padding:20px}
.modal.open{display:flex}
.modal-content{background:var(--white);border-radius:var(--radius);max-width:900px;max-height:90vh;overflow-y:auto;position:relative;animation:modalSlide 0.3s ease}
@keyframes modalSlide{from{transform:translateY(50px);opacity:0}to{transform:translateY(0);opacity:1}}
.modal-close{position:absolute;top:16px;right:16px;background:none;border:none;font-size:32px;cursor:pointer;color:var(--gray-500);z-index:10}
.modal-close:hover{color:var(--ink)}
.modal-header{position:sticky;top:0;background:var(--white);padding:24px;border-bottom:1px solid var(--gray-200);z-index:5}
.modal-title{font-size:32px;font-weight:700}
.modal-body{padding:24px}
/* Responsive */
@media (max-width: 768px){
.nav{display:none}
.menu-toggle{display:block}
.nav.active{display:flex;position:absolute;top:100%;left:0;right:0;background:var(--white);flex-direction:column;padding:20px;box-shadow:var(--shadow)}
.hero-buttons{flex-direction:column;align-items:center}
.hero-buttons .btn{width:100%;max-width:300px}
.timeline::before{left:20px}
.timeline-item{padding-left:60px}
.timeline-marker{width:40px;height:40px;font-size:18px}
.calculator{padding:24px}
.section{padding:60px 0}
}
/* Reveal animation */
.reveal{opacity:0;transform:translateY(30px);transition:all 0.6s ease}
.reveal.in{opacity:1;transform:none}
</style>
</head>
<body>
<!-- Header -->
<header class="header">
<div class="header-inner">
<div class="logo">🇪🇺 Europe Trip</div>
<nav class="nav" id="nav">
<a href="#route">🗺️ Маршрут</a>
<a href="#days">📅 15 дней</a>
<a href="#hotels">🏨 Отели</a>
<a href="#transport">🚆 Транспорт</a>
<a href="#budget">💰 Бюджет</a>
<a href="#checklist">🎒 Чек-лист</a>
</nav>
<button class="menu-toggle" id="menuToggle" aria-label="Меню">☰</button>
</div>
</header>
<!-- Hero -->
<section class="hero">
<div class="hero-bg"></div>
<div class="hero-pattern"></div>
<div class="hero-content">
<h1>ЕВРОПА ЗА 15 ДНЕЙ</h1>
<p>Идеальный маршрут для первой самостоятельной поездки из Астаны</p>
<div class="hero-route">
<span>🇰🇿 Астана</span>
<span>→</span>
<span>🇨🇿 Прага</span>
<span>→</span>
<span>🇦🇹 Вена</span>
<span>→</span>
<span>🇸🇰 Братислава</span>
<span>→</span>
<span>🇭🇺 Будапешт</span>
</div>
<div class="hero-buttons">
<button class="btn" onclick="document.getElementById('route').scrollIntoView({behavior:'smooth'})">Посмотреть маршрут</button>
<button class="btn btn-secondary" onclick="document.getElementById('budget').scrollIntoView({behavior:'smooth'})">Рассчитать бюджет</button>
</div>
</div>
</section>
<!-- Route -->
<section class="section" id="route">
<div class="container">
<h2 class="section-title reveal">Ваш маршрут на 15 дней</h2>
<div class="route-visual reveal" id="routeVisual"></div>
<div class="cities-grid" id="citiesGrid"></div>
</div>
</section>
<!-- Days Timeline -->
<section class="section" id="days" style="background:var(--white)">
<div class="container">
<h2 class="section-title reveal">15 дней в Европе</h2>
<div class="timeline" id="timeline"></div>
</div>
</section>
<!-- Hotels -->
<section class="section hotels-section" id="hotels">
<div class="container">
<h2 class="section-title reveal">Где остановиться</h2>
<div class="hotels-grid" id="hotelsGrid"></div>
</div>
</section>
<!-- Transport -->
<section class="section" id="transport">
<div class="container">
<h2 class="section-title reveal">Как перемещаться между городами</h2>
<div class="transport-grid" id="transportGrid"></div>
</div>
</section>
<!-- Budget Calculator -->
<section class="section budget-section" id="budget">
<div class="container">
<h2 class="section-title">Сколько стоит поездка?</h2>
<div class="calculator reveal">
<div class="calculator-grid">
<div class="calculator-item">
<label>Путешественников</label>
<input type="number" id="travelers" value="1" min="1" max="10" onchange="calculateBudget()">
</div>
<div class="calculator-item">
<label>Категория отеля</label>
<select id="hotelCategory" onchange="calculateBudget()">
<option value="economy">Эконом</option>
<option value="comfort" selected>Комфорт</option>
<option value="premium">Премиум</option>
</select>
</div>
<div class="calculator-item">
<label>Питание</label>
<select id="foodCategory" onchange="calculateBudget()">
<option value="economy">Эконом</option>
<option value="medium" selected>Средний</option>
<option value="comfort">Комфорт</option>
</select>
</div>
<div class="calculator-item">
<label>Экскурсии</label>
<select id="attractionsCategory" onchange="calculateBudget()">
<option value="min">Минимум</option>
<option value="standard" selected>Стандарт</option>
<option value="many">Много экскурсий</option>
</select>
</div>
</div>
<div class="budget-result">
<div class="budget-breakdown" id="budgetBreakdown"></div>
<div class="budget-total">
<div class="budget-line">
<span class="budget-label"><span>💰</span> ИТОГО:</span>
<span id="budgetTotal">0 ₸</span>
</div>
</div>
<div class="budget-per-person" id="budgetPerPerson"></div>
</div>
</div>
<div class="budget-cards reveal" style="margin-top:40px">
<div class="budget-card"><div class="budget-card-icon">✈️</div><div class="budget-card-title">Авиабилеты</div></div>
<div class="budget-card"><div class="budget-card-icon">🚆</div><div class="budget-card-title">Переезды</div></div>
<div class="budget-card"><div class="budget-card-icon">🏨</div><div class="budget-card-title">Отели</div></div>
<div class="budget-card"><div class="budget-card-icon">🍽️</div><div class="budget-card-title">Питание</div></div>
<div class="budget-card"><div class="budget-card-icon">🎟️</div><div class="budget-card-title">Достопримечательности</div></div>
<div class="budget-card"><div class="budget-card-icon">🚇</div><div class="budget-card-title">Транспорт</div></div>
</div>
</div>
</section>
<!-- Tips -->
<section class="section" id="tips">
<div class="container">
<h2 class="section-title reveal">Перед поездкой</h2>
<div class="tips-grid" id="tipsGrid"></div>
</div>
</section>
<!-- Checklist -->
<section class="section checklist-section" id="checklist">
<div class="container">
<h2 class="section-title reveal">Чек-лист подготовки</h2>
<div class="checklist-container">
<div class="checklist-progress">
<div style="display:flex;justify-content:space-between;font-weight:700">
<span>Готово:</span>
<span id="checklistCount">0 / 11</span>
</div>
<div class="checklist-progress-bar">
<div class="checklist-progress-fill" id="checklistProgress" style="width:0%"></div>
</div>
</div>
<ul class="checklist-items" id="checklistItems"></ul>
</div>
</div>
</section>
<!-- Modal for City Details -->
<div class="modal" id="cityModal">
<div class="modal-content">
<button class="modal-close" onclick="closeModal()">&times;</button>
<div class="modal-header">
<h2 class="modal-title" id="modalTitle"></h2>
</div>
<div class="modal-body" id="modalBody"></div>
</div>
</div>
<script>
// Load data
let DATA = null;
let checklistData = [];
fetch('data.json')
.then(r => r.json())
.then(data => {
DATA = data;
checklistData = [...data.checklist];
renderAll();
calculateBudget();
updateChecklist();
})
.catch(err => {
console.error('Failed to load data:', err);
document.getElementById('citiesGrid').innerHTML = '<p>Ошибка загрузки данных. Обновите страницу.</p>';
});
function renderAll() {
renderRoute();
renderCities();
renderTimeline();
renderHotels();
renderTransport();
renderTips();
renderChecklist();
}
function renderRoute() {
const route = document.getElementById('routeVisual');
const cities = DATA.cities;
let html = '<div class="route-city"><span class="flag">🇰🇿</span><div class="name">Астана</div></div>';
cities.forEach(city => {
html += '<div class="route-arrow">↓</div>';
html += `<div class="route-city"><span class="flag">${city.flag}</span><div class="name">${city.name}</div></div>`;
});
html += '<div class="route-arrow">↓</div>';
html += '<div class="route-city"><span class="flag">🇰🇿</span><div class="name">Астана</div></div>';
route.innerHTML = html;
}
function renderCities() {
const grid = document.getElementById('citiesGrid');
grid.innerHTML = DATA.cities.map(city => `
<div class="city-card" onclick="openCityModal('${city.id}')">
<img src="${city.image}" alt="${city.name}" class="city-card-img" loading="lazy">
<div class="city-card-body">
<div class="city-card-header">
<div class="city-card-title">${city.name} ${city.flag}</div>
<div class="city-card-nights">${city.nights} ночей</div>
</div>
<div class="city-card-price">${city.priceRange} / ночь</div>
<div class="city-card-district">Лучший район: ${city.district}</div>
<button class="btn" style="width:100%">Подробнее</button>
</div>
</div>
`).join('');
}
function renderTimeline() {
const timeline = document.getElementById('timeline');
timeline.innerHTML = DATA.days.map((day, i) => `
<div class="timeline-item reveal">
<div class="timeline-marker">${day.icon}</div>
<div class="timeline-content">
<div class="timeline-day">День ${day.day}</div>
<div class="timeline-title">${day.title}</div>
<ul class="timeline-activities">
${day.activities.map(a => `<li>${a}</li>`).join('')}
</ul>
${day.cost ? `<div class="timeline-cost">Стоимость дня: ${day.cost}</div>` : ''}
</div>
</div>
`).join('');
}
function renderHotels() {
const grid = document.getElementById('hotelsGrid');
const allHotels = [];
Object.keys(DATA.hotels).forEach(cityId => {
const city = DATA.cities.find(c => c.id === cityId);
DATA.hotels[cityId].forEach(hotel => {
allHotels.push({...hotel, cityName: city.name, cityFlag: city.flag});
});
});
grid.innerHTML = allHotels.map(hotel => `
<div class="hotel-card">
<img src="${hotel.image}" alt="${hotel.name}" class="hotel-card-img" loading="lazy">
<div class="hotel-card-body">
<span class="hotel-category ${hotel.category.toLowerCase()}">${hotel.category}</span>
<div class="hotel-name">${hotel.name}</div>
<div class="hotel-district">📍 ${hotel.cityName} ${hotel.cityFlag} — ${hotel.district}</div>
<div class="hotel-price">${hotel.price}</div>
</div>
</div>
`).join('');
}
function renderTransport() {
const grid = document.getElementById('transportGrid');
grid.innerHTML = DATA.transport.map(t => `
<div class="transport-card">
<div class="transport-icon">${t.type.includes('✈️') ? '✈️' : '🚆'}</div>
<div class="transport-info">
<div class="transport-route">${t.from} → ${t.to}</div>
<div class="transport-type">${t.type.replace(/[✈️🚆]/g, '').trim()}</div>
</div>
<div class="transport-details">
<div class="transport-duration">${t.duration}</div>
<div class="transport-price">${t.price}</div>
</div>
</div>
`).join('');
}
function renderTips() {
const grid = document.getElementById('tipsGrid');
grid.innerHTML = DATA.tips.map(tip => `
<div class="tip-card reveal">
<div class="tip-icon">${tip.icon}</div>
<div class="tip-title">${tip.title}</div>
<div class="tip-text">${tip.text}</div>
</div>
`).join('');
}
function renderChecklist() {
const list = document.getElementById('checklistItems');
list.innerHTML = checklistData.map((item, i) => `
<li class="checklist-item ${item.done ? 'done' : ''}" onclick="toggleChecklist(${i})">
<div class="checklist-checkbox"></div>
<div class="checklist-text">${item.text}</div>
</li>
`).join('');
updateChecklist();
}
function updateChecklist() {
const done = checklistData.filter(i => i.done).length;
const total = checklistData.length;
document.getElementById('checklistCount').textContent = `${done} / ${total}`;
document.getElementById('checklistProgress').style.width = `${(done/total)*100}%`;
}
function toggleChecklist(index) {
checklistData[index].done = !checklistData[index].done;
renderChecklist();
localStorage.setItem('europeTripChecklist', JSON.stringify(checklistData));
}
// Load saved checklist
const saved = localStorage.getItem('europeTripChecklist');
if (saved) {
try { checklistData = JSON.parse(saved); } catch(e) {}
}
function calculateBudget() {
if (!DATA) return;
const travelers = parseInt(document.getElementById('travelers').value) || 1;
const hotelCat = document.getElementById('hotelCategory').value;
const foodCat = document.getElementById('foodCategory').value;
const attractionsCat = document.getElementById('attractionsCategory').value;
const b = DATA.budget;
const flights = b.flights * travelers;
const trains = b.trains * travelers;
const hotels = b.hotels[hotelCat] * travelers;
const food = b.food[foodCat] * travelers;
const attractions = b.attractions[attractionsCat] * travelers;
const transport = b.transport * travelers;
const total = flights + trains + hotels + food + attractions + transport;
document.getElementById('budgetBreakdown').innerHTML = `
<div class="budget-line"><span class="budget-label"><span>✈️</span> Авиабилеты</span><span>${flights.toLocaleString()} ₸</span></div>
<div class="budget-line"><span class="budget-label"><span>🚆</span> Переезды</span><span>${trains.toLocaleString()} ₸</span></div>
<div class="budget-line"><span class="budget-label"><span>🏨</span> Отели</span><span>${hotels.toLocaleString()} ₸</span></div>
<div class="budget-line"><span class="budget-label"><span>🍽️</span> Питание</span><span>${food.toLocaleString()} ₸</span></div>
<div class="budget-line"><span class="budget-label"><span>🎟️</span> Достопримечательности</span><span>${attractions.toLocaleString()} ₸</span></div>
<div class="budget-line"><span class="budget-label"><span>🚇</span> Транспорт</span><span>${transport.toLocaleString()} ₸</span></div>
`;
document.getElementById('budgetTotal').textContent = `${total.toLocaleString()} ₸`;
document.getElementById('budgetPerPerson').textContent = travelers > 1 ? `≈ ${Math.round(total/travelers).toLocaleString()} ₸ на человека` : '';
}
function openCityModal(cityId) {
const city = DATA.cities.find(c => c.id === cityId);
const attractions = DATA.attractions[cityId] || [];
const hotels = DATA.hotels[cityId] || [];
document.getElementById('modalTitle').textContent = `${city.name} ${city.flag}`;
document.getElementById('modalBody').innerHTML = `
<img src="${city.image}" alt="${city.name}" style="width:100%;height:300px;object-fit:cover;margin-bottom:24px;border-radius:var(--radius)">
<p style="font-size:18px;margin-bottom:24px;color:var(--gray-500)">${city.description}</p>
<div style="display:flex;gap:24px;margin-bottom:32px;flex-wrap:wrap">
<div><strong>📅 Ночей:</strong> ${city.nights}</div>
<div><strong>💰 Проживание:</strong> ${city.priceRange} / ночь</div>
<div><strong>📍 Район:</strong> ${city.district}</div>
</div>
<h3 style="margin-bottom:20px">Что посмотреть</h3>
<div class="attractions-grid" style="margin-bottom:32px">
${attractions.map(a => `
<div class="attraction-card">
<img src="${a.image}" alt="${a.name}" class="attraction-img" loading="lazy">
<div class="attraction-body">
<div class="attraction-name">${a.name}</div>
<div class="attraction-rating">${'⭐'.repeat(a.rating)}${'☆'.repeat(5-a.rating)}</div>
<div class="attraction-desc">${a.description}</div>
<div class="attraction-price">Билет: ${a.price}</div>
</div>
</div>
`).join('')}
</div>
<h3 style="margin-bottom:20px">Где жить</h3>
<div class="hotels-grid">
${hotels.map(h => `
<div class="hotel-card">
<img src="${h.image}" alt="${h.name}" class="hotel-card-img" loading="lazy">
<div class="hotel-card-body">
<span class="hotel-category ${h.category.toLowerCase()}">${h.category}</span>
<div class="hotel-name">${h.name}</div>
<div class="hotel-district">📍 ${h.district}</div>
<div class="hotel-price">${h.price}</div>
</div>
</div>
`).join('')}
</div>
`;
document.getElementById('cityModal').classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeModal() {
document.getElementById('cityModal').classList.remove('open');
document.body.style.overflow = '';
}
// Close modal on outside click
document.getElementById('cityModal').addEventListener('click', (e) => {
if (e.target.id === 'cityModal') closeModal();
});
// Mobile menu
document.getElementById('menuToggle').addEventListener('click', () => {
document.getElementById('nav').classList.toggle('active');
});
// Reveal on scroll
const observer = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('in'); });
}, {threshold: 0.15});
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
// Smooth scroll for nav links
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({behavior: 'smooth'});
document.getElementById('nav').classList.remove('active');
}
});
});
</script>
</body>
</html>