This commit is contained in:
2026-05-03 14:11:53 +05:00
parent d182fa8a37
commit 9d4d0306a9
6 changed files with 94 additions and 107 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
node_modules
package-lock.json
.env
data

Binary file not shown.

View File

@@ -26,6 +26,6 @@ networks:
applications:
external: true
# docker network create applications
# docker-compose up -d
# docker-compose up -d --build
# docker-compose build --no-cache && docker-compose up -d
# docker compose up -d
# docker compose up -d --build
# docker compose build --no-cache && docker compose up -d

View File

@@ -7,8 +7,9 @@
<title>Hotel 777 | Отдых в Абхазии</title>
<link rel="stylesheet" href="style.css">
<link rel="icon" href="img/favicon.ico" type="image/x-icon">
<!-- Яндекс.Карты API (замените YOUR_API_KEY на реальный ключ) -->
<!-- Яндекс.Карты временно отключены (закомментированы)
<script src="https://api-maps.yandex.ru/2.1/?apikey=YOUR_YANDEX_MAP_API_KEY&lang=ru_RU"></script>
-->
</head>
<body>
@@ -38,7 +39,7 @@
<!-- Секция "О нас" (динамически через about.js) -->
<section id="about" class="white-bg"></section>
<!-- Секция "Кухня" -->
<!-- Секция "Кухня" (с карточками питания) -->
<section id="food">
<h2 class="section-title animate" data-i18n="food_title">Вкус Абхазии</h2>
<div class="about-grid">
@@ -48,22 +49,35 @@
<div class="about-text animate delay-2">
<h3 data-i18n="food_subtitle">Домашняя кухня из местных продуктов</h3>
<p data-i18n="food_text">Почувствуйте настоящее гостеприимство! Мы готовим из того, что выросло прямо здесь: свежайший сыр сулугуни, ароматная абыста, сочные овощи с грядки и домашнее вино.</p>
<div class="food-benefits">
<p><strong>✨ Для наших гостей:</strong></p>
<ul>
<li>🍽️ Учитываем пожелания по питанию</li>
<li>👨‍🍳 Приготовим блюдо для Вас спросите у шефа</li>
<li>🕒 Завтрак: с 8:30 до 10:00</li>
<li>🕒 Обед: с 12:00 до 14:00</li>
<li>🕒 Ужин: с 19:00 до 21:00</li>
</ul>
<p class="food-note">📢 <em>Сообщите администратору о любых предпочтениях при заселении мы всё организуем!</em></p>
<div class="facts-grid food-features">
<div class="fact-card">
<div class="fact-icon">🍽️</div>
<div class="fact-text" data-i18n="food_request">Учитываем пожелания по питанию</div>
</div>
<div class="fact-card">
<div class="fact-icon">👨‍🍳</div>
<div class="fact-text" data-i18n="food_chef">Приготовим блюдо для Вас спросите у шефа</div>
</div>
<div class="fact-card">
<div class="fact-icon">🕒</div>
<div class="fact-text" data-i18n="food_breakfast">Завтрак: с 8:30 до 10:00</div>
</div>
<div class="fact-card">
<div class="fact-icon">🕒</div>
<div class="fact-text" data-i18n="food_lunch">Обед: с 12:00 до 14:00</div>
</div>
<div class="fact-card">
<div class="fact-icon">🕒</div>
<div class="fact-text" data-i18n="food_dinner">Ужин: с 19:00 до 21:00</div>
</div>
</div>
<p class="food-note" data-i18n="food_note">📢 <em>Сообщите администратору о любых предпочтениях при заселении мы всё организуем!</em></p>
</div>
</div>
</section>
<!-- Секция "Где мы" (динамически через location.js) -->
<!-- Секция "Где мы" (без карты, только указатель и города) -->
<section id="location" class="white-bg"></section>
<!-- Секция "Бронирование" -->
@@ -76,7 +90,7 @@
</div>
<div class="form-group">
<label for="phone" data-i18n="label_phone">Номер телефона</label>
<input type="tel" id="phone" name="phone" required placeholder="+7 (___) ___-__-__">
<input type="tel" id="phone" name="phone" required placeholder="+ ___ (___) ___-__-__">
</div>
<div class="form-group">
<label for="adults" data-i18n="book_adults">Количество взрослых</label>

View File

@@ -1,4 +1,4 @@
// Глобальные переводы (расширены для location, about, booking)
// Глобальные переводы (расширены для location, about, booking, food)
window.translations = {
ru: {
nav_about: "О нас",
@@ -19,6 +19,12 @@ window.translations = {
food_title: "Вкус Абхазии",
food_subtitle: "Домашняя кухня из местных продуктов",
food_text: "Почувствуйте гостеприимство! Свежайший сыр сулугуни, мамалыга, овощи с грядки и домашнее вино.",
food_request: "🍽️ Учитываем пожелания по питанию",
food_chef: "👨‍🍳 Приготовим блюдо для Вас спросите у шефа",
food_breakfast: "🕒 Завтрак: с 8:30 до 10:00",
food_lunch: "🕒 Обед: с 12:00 до 14:00",
food_dinner: "🕒 Ужин: с 19:00 до 21:00",
food_note: "📢 Сообщите администратору о любых предпочтениях при заселении мы всё организуем!",
loc_title: "Удобное расположение",
book_title: "Забронировать отдых",
label_name: "Ваше имя",
@@ -41,7 +47,6 @@ window.translations = {
loc_city_primorsk: "Приморск",
loc_city_gudauta: "Гудаута",
loc_km: "км",
// Новые поля для бронирования
book_adults: "Количество взрослых",
book_children: "Количество детей (до 12 лет)",
book_checkin: "Дата заезда",
@@ -49,7 +54,6 @@ window.translations = {
please_fill_all: "Пожалуйста, заполните все обязательные поля",
booking_success: "Заявка отправлена! Мы свяжемся с вами.",
booking_error: "Ошибка отправки. Попробуйте позже.",
// Факты о городах (русский)
loc_facts_sukhum: [
"🏛️ Один из древнейших городов мира, основан в VI веке до н.э.",
"🌿 Ботанический сад — один из старейших на Кавказе (основан в 1838 году).",
@@ -106,6 +110,12 @@ window.translations = {
food_title: "Taste of Abkhazia",
food_subtitle: "Homemade cuisine from local ingredients",
food_text: "Feel the hospitality! Suluguni cheese, mamalyga, garden vegetables, and homemade wine.",
food_request: "🍽️ We accommodate dietary preferences",
food_chef: "👨‍🍳 We'll cook a dish for you ask the chef",
food_breakfast: "🕒 Breakfast: 8:30 10:00",
food_lunch: "🕒 Lunch: 12:00 14:00",
food_dinner: "🕒 Dinner: 19:00 21:00",
food_note: "📢 Tell the administrator about any preferences at check-in we'll arrange everything!",
loc_title: "Convenient location",
book_title: "Book your holiday",
label_name: "Your name",
@@ -128,7 +138,6 @@ window.translations = {
loc_city_primorsk: "Primorsk",
loc_city_gudauta: "Gudauta",
loc_km: "km",
// New booking fields
book_adults: "Number of adults",
book_children: "Number of children (under 12)",
book_checkin: "Check-in date",
@@ -136,7 +145,6 @@ window.translations = {
please_fill_all: "Please fill all required fields",
booking_success: "Request sent! We will contact you.",
booking_error: "Submission error. Please try again later.",
// City facts (English)
loc_facts_sukhum: [
"🏛️ One of the oldest cities in the world, founded in the 6th century BC.",
"🌿 Botanical Garden one of the oldest in the Caucasus (founded 1838).",
@@ -193,6 +201,12 @@ window.translations = {
food_title: "Аԥсны аҵәа",
food_subtitle: "Аџьа ҭаацәарантәи аҭыԥтә афасаҟәақәа рыла",
food_text: "Ашьааҭра шәаазыр! Ибжьа асулугуни, абаста, аҵиаа ҵаҟатәи нас аҭаацәарантәи аҵаа.",
food_request: "🍽️ Ашәхәаҭаҩра аиҭаҵра шәаазырҵоит",
food_chef: "👨‍🍳 Шәы жәҩаны ахәылҵа ишәаазырҵоит ашеф ишәааҽазыр",
food_breakfast: "🕒 Ашәхә: 8:30 10:00 азы",
food_lunch: "🕒 Абжьара: 12:00 14:00",
food_dinner: "🕒 Аҵх: 19:00 21:00",
food_note: "📢 Администратор ишәааҽазыр ахынчә қәыԥсқәа ҳара иҭабуп!",
loc_title: "Каратә аҭыԥкаара",
book_title: "Ахыҧсыра аҭагалара",
label_name: "Шәхы",
@@ -215,7 +229,6 @@ window.translations = {
loc_city_primorsk: "Приморск",
loc_city_gudauta: "Гәдоуҭа",
loc_km: "км",
// New booking fields in Abkhaz
book_adults: "Аҧшәмаҭааҩцәа рхыҧхьаӡара",
book_children: "Аҵлақәа рхыҧхьаӡара (12 шықәса рҟынӡа)",
book_checkin: "Аҭагалара амш",
@@ -223,7 +236,6 @@ window.translations = {
please_fill_all: "Ишәҧшәа, азықәҭа змоу аҭыԥқәа рымч",
booking_success: "Азаявка ацәыҵит! Ҳара шәыҟазаалак шәааҽазыр.",
booking_error: "Ацәыҵра аҟаҵаразы алшара. Ушәа агәаҧш, ушәа шәааҽаз.",
// City facts Abkhaz (simplified)
loc_facts_sukhum: [
"🏛️ Адунеи аиҳабылакьықәа руакы, VI ашәышықәса рахь нҵа иҟоуп.",
"🌿 Аботаникатә сад — Акавказ аиҳабылакьықәа руакы (1838 ш.).",
@@ -263,7 +275,7 @@ window.translations = {
}
};
// Функция закрытия модального окна (глобальная, используется также в location.js)
// Функция закрытия модального окна
window.closeModal = function() {
const modal = document.getElementById('cityModal');
if (modal) modal.style.display = 'none';
@@ -277,42 +289,31 @@ window.acceptCookies = function() {
};
document.addEventListener("DOMContentLoaded", () => {
// Циклическая смена фона Hero
// --- Установка статичного фона для hero (первое изображение) ---
const hero = document.querySelector('.hero');
const images = ['img/h777.webp', 'img/h777o.webp', 'img/h777l.webp', 'img/h777z.webp'];
let currentImg = 0;
const setHeroBackground = () => {
const img = new Image();
img.src = images[currentImg];
img.onload = () => {
hero.style.backgroundImage = `linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('${img.src}')`;
};
const firstImage = 'img/h777.webp';
const img = new Image();
img.src = firstImage;
img.onload = () => {
hero.style.backgroundImage = `linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('${img.src}')`;
};
setHeroBackground();
setInterval(() => {
currentImg = (currentImg + 1) % images.length;
setHeroBackground();
}, 15000);
// Ошибка 404 tiny.webp больше не возникает, так как мы не используем его в качестве фона по умолчанию
// Локализация основных элементов (с data-i18n)
const langSelect = document.getElementById('langSwitch');
let currentLang = localStorage.getItem('siteLang') || 'ru';
const updateText = (lang) => {
// Обновляем элементы с data-i18n
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
if (window.translations[lang][key]) el.innerHTML = window.translations[lang][key];
});
// Обновляем плейсхолдеры
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const key = el.getAttribute('data-i18n-ph');
if (window.translations[lang][key]) el.placeholder = window.translations[lang][key];
});
localStorage.setItem('siteLang', lang);
// Вызываем обновление динамических секций, если функции определены
if (typeof window.updateLocationLanguage === 'function') {
window.updateLocationLanguage(lang);
}
@@ -330,7 +331,7 @@ document.addEventListener("DOMContentLoaded", () => {
setTimeout(() => document.getElementById('cookieBanner').classList.add('show'), 2000);
}
// Форма бронирования с отправкой на сервер
// Форма бронирования (без intl-tel-input)
const bookingForm = document.getElementById('bookingForm');
if (bookingForm) {
bookingForm.onsubmit = async (e) => {
@@ -373,13 +374,19 @@ document.addEventListener("DOMContentLoaded", () => {
};
}
// Анимации при скролле
const obs = new IntersectionObserver(entries => {
entries.forEach(en => { if (en.isIntersecting) en.target.style.animationPlayState = 'running'; });
});
// Анимации при скролле (один IntersectionObserver)
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.animationPlayState = 'running';
observer.unobserve(entry.target); // отключаем после появления
}
});
}, { threshold: 0.1, rootMargin: '50px' });
document.querySelectorAll('.animate').forEach(el => {
el.style.animationPlayState = 'paused';
obs.observe(el);
observer.observe(el);
});
// Модальное окно: закрытие по крестику и по клику на фон

View File

@@ -27,7 +27,6 @@ body {
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}
/* ШАПКА */
header {
background: rgba(0, 18, 25, 0.95);
color: var(--white);
@@ -76,7 +75,6 @@ nav a:hover {
color: var(--secondary);
}
/* HERO */
.hero {
height: 100vh;
display: flex;
@@ -86,7 +84,7 @@ nav a:hover {
text-align: center;
color: var(--white);
padding: 0 20px;
background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('img/h777-tiny.webp') center/cover;
background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('img/h777.webp') center/cover;
transition: background-image 1.5s ease-in-out;
}
@@ -96,7 +94,6 @@ nav a:hover {
line-height: 1.1;
}
/* СЕКЦИИ */
section {
padding: 6rem 10%;
}
@@ -164,7 +161,6 @@ section {
margin: 0 auto;
}
/* Анимированная картинка-указатель */
.location-img-animate {
transition: transform 0.8s cubic-bezier(0.2, 0.9, 0.4, 1.1), opacity 0.8s ease;
transform: scale(0.85);
@@ -176,7 +172,6 @@ section {
opacity: 1;
}
/* ФОРМА */
.booking-form {
max-width: 550px;
margin: 0 auto;
@@ -236,7 +231,6 @@ section {
width: 100%;
}
/* COOKIE & ANIMATIONS */
.cookie-banner {
position: fixed;
bottom: -200px;
@@ -298,7 +292,6 @@ footer {
padding: 3rem;
}
/* Блок с расстояниями до городов (разноцветные карточки) */
.location-cards {
display: flex;
flex-wrap: wrap;
@@ -307,27 +300,31 @@ footer {
}
.location-card {
padding: 1rem 1.5rem;
border-radius: 12px;
background: var(--white);
padding: 1rem;
border-radius: 20px;
text-align: center;
min-width: 120px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
}
.location-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
box-shadow: 0 20px 30px rgba(0, 0, 0, 0.1);
}
.location-icon {
font-size: 1.5rem;
font-size: 2rem;
margin-bottom: 0.5rem;
}
.location-title {
font-weight: 600;
color: var(--primary);
font-size: 0.9rem;
}
.location-distance {
@@ -335,7 +332,6 @@ footer {
color: #666;
}
/* Кнопка показа карты */
.show-map-btn {
background: var(--primary);
transition: 0.3s;
@@ -346,7 +342,6 @@ footer {
transform: translateY(-2px);
}
/* Карта под картинкой (скрыта по умолчанию) */
.map-container {
width: 100%;
height: 400px;
@@ -363,7 +358,6 @@ footer {
}
}
/* Стили для секции about (модерн) */
.modern-about {
gap: 3rem;
}
@@ -424,17 +418,28 @@ footer {
font-size: 0.9rem;
}
.food-features {
margin: 2rem 0 1rem;
}
.food-note {
background: #fff8e7;
padding: 0.8rem;
border-radius: 16px;
text-align: center;
font-size: 0.9rem;
margin-top: 1rem;
}
@media (max-width: 768px) {
.facts-grid {
grid-template-columns: 1fr 1fr;
}
.about-text h3 {
font-size: 1.4rem;
}
}
/* Модальное окно для фактов о городах */
.modal {
display: none;
position: fixed;
@@ -494,43 +499,3 @@ footer {
border-left: 4px solid var(--secondary);
font-size: 1rem;
}
/* Блок с расстояниями до городов (карточки в стиле fact-card) */
.location-cards {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
}
.location-card {
background: var(--white); /* как у fact-card */
padding: 1rem;
border-radius: 20px; /* как у fact-card */
text-align: center;
min-width: 120px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
}
.location-card:hover {
transform: translateY(-5px); /* как у fact-card */
box-shadow: 0 20px 30px rgba(0, 0, 0, 0.1);
}
.location-icon {
font-size: 2rem; /* увеличенная иконка, как .fact-icon */
margin-bottom: 0.5rem;
}
.location-title {
font-weight: 600;
color: var(--primary);
font-size: 0.9rem; /* под размер .fact-text */
}
.location-distance {
font-size: 0.9rem;
color: #666;
}