diff --git a/labs/lab-07/img/main.jpg b/labs/lab-07/img/main.jpg new file mode 100644 index 0000000..f9f6558 Binary files /dev/null and b/labs/lab-07/img/main.jpg differ diff --git a/labs/lab-07/index.html b/labs/lab-07/index.html new file mode 100644 index 0000000..a608c75 --- /dev/null +++ b/labs/lab-07/index.html @@ -0,0 +1,101 @@ + + + + + + + ЭкоЛанч - Доставка здоровых бизнес-ланчей в Москве + + + + + + +
+

ЭкоЛанч

+ +
+ +
+
+

О компании ЭкоЛанч

+ Здоровая еда в контейнерах +

ЭкоЛанч - это современная компания по доставке здоровых бизнес-ланчей в офисы Москвы. Наша миссия - сделать правильное питание доступным для занятых людей, которые ценят свое время и здоровье.

+

Мы используем только свежие продукты от проверенных поставщиков, а наши повара готовят блюда каждое утро. Все ланчи упакованы в экологичную биоразлагаемую упаковку, потому что мы заботимся не только о вашем здоровье, но и о здоровье нашей планеты.

+
+ +
+

Наши преимущества

+ +
+ + +
+ + + + \ No newline at end of file diff --git a/labs/lab-07/menu.html b/labs/lab-07/menu.html new file mode 100644 index 0000000..c8d4ba6 --- /dev/null +++ b/labs/lab-07/menu.html @@ -0,0 +1,245 @@ + + + + + + + Собрать ланч - ЭкоЛанч + + + + + + +
+

ЭкоЛанч

+ +
+
+
+

Доступные для заказа варианты ланча

+
+
+
+ 🍲 +

Суп

+
+
+ 🍽️ +

Главное блюдо

+
+
+ 🥗 +

Салат

+
+
+ 🥤 +

Напиток

+
+
+ +
+
+ 🍲 +

Суп

+
+
+ 🍽️ +

Главное блюдо

+
+
+ 🥤 +

Напиток

+
+
+ +
+
+ 🍲 +

Суп

+
+
+ 🥗 +

Салат

+
+
+ 🥤 +

Напиток

+
+
+ +
+
+ 🍽️ +

Главное блюдо

+
+
+ 🥗 +

Салат

+
+
+ 🥤 +

Напиток

+
+
+ +
+
+ 🍽️ +

Главное блюдо

+
+
+ 🥤 +

Напиток

+
+
+ +
+
+ 🍰 +

Десерт

+
+

(Можно добавить к любому заказу)

+
+
+

Десерты можно добавить к любому варианту ланча

+
+ +
+
+

Супы

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

Главные блюда

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

Салаты и стартеры

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

Напитки

+
+ + +
+
+
+
+ +
+
+

Десерты

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

Оформить заказ

+
+
+ +
+

Ваш заказ

+
+

Ничего не выбрано

+
+
+ +
+

Данные для доставки

+ + + + + + + +
+ + +
+ + + + + + + Доставка осуществляется только по Москве + +
+ +
+ + +
+
+ + +
+
+ + + + Доступное время доставки с 7:00 до 23:00 + + + + +
+ + +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/labs/lab-07/menu.js b/labs/lab-07/menu.js new file mode 100644 index 0000000..a8cba6d --- /dev/null +++ b/labs/lab-07/menu.js @@ -0,0 +1,429 @@ +let dishes = []; + +const selectedDishes = { + soup: null, + 'main-course': null, + salad: null, + drink: null, + dessert: null +}; + +let activeFilters = { + soup: null, + 'main-course': null, + salad: null, + drink: null, + dessert: null +}; + +function loadDishes() { + const apiUrl = 'https://edu.std-900.ist.mospolytech.ru/labs/api/dishes'; + const apiKey = '358a63a5-52ae-4ab0-800b-90f75ce5a5c2'; + + fetch(apiUrl) + .then(function(response) { + if (!response.ok) { + throw new Error('Ошибка загрузки данных'); + } + return response.json(); + }) + .then(function(data) { + dishes = data; + sortDishes(); + displayDishes(); + }) + .catch(function(error) { + console.error('Ошибка при загрузке блюд:', error); + }); +} + +function sortDishes() { + dishes.sort(function(a, b) { + return a.name.localeCompare(b.name, 'ru'); + }); +} + +function displayDishes() { + const soupSection = document.getElementById('soup-section'); + const mainCourseSection = document.getElementById('main-course-section'); + const saladSection = document.getElementById('salad-section'); + const drinkSection = document.getElementById('drink-section'); + const dessertSection = document.getElementById('dessert-section'); + + soupSection.innerHTML = ''; + mainCourseSection.innerHTML = ''; + saladSection.innerHTML = ''; + drinkSection.innerHTML = ''; + dessertSection.innerHTML = ''; + + dishes.forEach(function(dish) { + const dishCard = createDishCard(dish); + + if (dish.category === 'soup') { + if (!activeFilters.soup || dish.kind === activeFilters.soup) { + soupSection.insertAdjacentHTML('beforeend', dishCard); + } + } else if (dish.category === 'main-course') { + if (!activeFilters['main-course'] || + dish.kind === activeFilters['main-course']) { + mainCourseSection.insertAdjacentHTML('beforeend', dishCard); + } + } else if (dish.category === 'salad') { + if (!activeFilters.salad || dish.kind === activeFilters.salad) { + saladSection.insertAdjacentHTML('beforeend', dishCard); + } + } else if (dish.category === 'drink') { + if (!activeFilters.drink || dish.kind === activeFilters.drink) { + drinkSection.insertAdjacentHTML('beforeend', dishCard); + } + } else if (dish.category === 'dessert') { + if (!activeFilters.dessert || + dish.kind === activeFilters.dessert) { + dessertSection.insertAdjacentHTML('beforeend', dishCard); + } + } + }); + + addDishClickHandlers(); + restoreSelection(); +} + +function createDishCard(dish) { + return ` +
+ ${dish.name} +

${dish.price} руб.

+

${dish.name}

+

${dish.count}

+ +
+ `; +} + +function addDishClickHandlers() { + const dishCards = document.querySelectorAll('.dish-card'); + + dishCards.forEach(function(card) { + card.addEventListener('click', function() { + const keyword = this.dataset.dish; + const dish = dishes.find(function(d) { + return d.keyword === keyword; + }); + + if (dish) { + selectDish(dish); + } + }); + }); +} + +function selectDish(dish) { + const previousDish = selectedDishes[dish.category]; + + if (previousDish) { + const previousCard = document.querySelector( + `.dish-card[data-dish="${previousDish.keyword}"]` + ); + if (previousCard) { + previousCard.classList.remove('selected'); + } + } + + selectedDishes[dish.category] = dish; + + const currentCard = document.querySelector( + `.dish-card[data-dish="${dish.keyword}"]` + ); + if (currentCard) { + currentCard.classList.add('selected'); + } + + updateOrderSummary(); +} + +function updateOrderSummary() { + const orderSummary = document.getElementById('order-summary'); + const hasSelection = selectedDishes.soup || + selectedDishes['main-course'] || + selectedDishes.salad || + selectedDishes.drink || + selectedDishes.dessert; + + if (!hasSelection) { + orderSummary.innerHTML = + '

Ничего не выбрано

'; + return; + } + + let summaryHTML = ''; + let totalPrice = 0; + + if (selectedDishes.soup) { + summaryHTML += ` +
+

Суп

+
+ ${selectedDishes.soup.name} + ${selectedDishes.soup.price} руб. +
+
+ `; + totalPrice += selectedDishes.soup.price; + } else { + summaryHTML += ` +
+

Суп

+

Блюдо не выбрано

+
+ `; + } + + if (selectedDishes['main-course']) { + summaryHTML += ` +
+

Главное блюдо

+
+ ${selectedDishes['main-course'].name} + ${selectedDishes['main-course'].price} руб. +
+
+ `; + totalPrice += selectedDishes['main-course'].price; + } else { + summaryHTML += ` +
+

Главное блюдо

+

Блюдо не выбрано

+
+ `; + } + + if (selectedDishes.salad) { + summaryHTML += ` +
+

Салат

+
+ ${selectedDishes.salad.name} + ${selectedDishes.salad.price} руб. +
+
+ `; + totalPrice += selectedDishes.salad.price; + } else { + summaryHTML += ` +
+

Салат

+

Блюдо не выбрано

+
+ `; + } + + if (selectedDishes.drink) { + summaryHTML += ` +
+

Напиток

+
+ ${selectedDishes.drink.name} + ${selectedDishes.drink.price} руб. +
+
+ `; + totalPrice += selectedDishes.drink.price; + } else { + summaryHTML += ` +
+

Напиток

+

Напиток не выбран

+
+ `; + } + + if (selectedDishes.dessert) { + summaryHTML += ` +
+

Десерт

+
+ ${selectedDishes.dessert.name} + ${selectedDishes.dessert.price} руб. +
+
+ `; + totalPrice += selectedDishes.dessert.price; + } else { + summaryHTML += ` +
+

Десерт

+

Десерт не выбран

+
+ `; + } + + summaryHTML += ` +
+

Стоимость заказа

+

${totalPrice} руб.

+
+ `; + + orderSummary.innerHTML = summaryHTML; +} + +function setupFilters() { + const filterButtons = document.querySelectorAll('.filter-btn'); + + filterButtons.forEach(function(button) { + button.addEventListener('click', function() { + const kind = this.dataset.kind; + const section = this.closest('section'); + const category = section.querySelector('.dishes-grid').id + .replace('-section', ''); + + if (this.classList.contains('active')) { + this.classList.remove('active'); + activeFilters[category] = null; + } else { + const sectionButtons = section + .querySelectorAll('.filter-btn'); + sectionButtons.forEach(function(btn) { + btn.classList.remove('active'); + }); + this.classList.add('active'); + activeFilters[category] = kind; + } + + displayDishes(); + }); + }); +} + +function isValidCombo() { + const hasSoup = selectedDishes.soup !== null; + const hasMainCourse = selectedDishes['main-course'] !== null; + const hasSalad = selectedDishes.salad !== null; + const hasDrink = selectedDishes.drink !== null; + + if (hasSoup && hasMainCourse && hasSalad && hasDrink) { + return true; + } + + if (hasSoup && hasMainCourse && hasDrink) { + return true; + } + + if (hasSoup && hasSalad && hasDrink) { + return true; + } + + if (hasMainCourse && hasSalad && hasDrink) { + return true; + } + + if (hasMainCourse && hasDrink) { + return true; + } + + return false; +} + +function getValidationMessage() { + const hasSoup = selectedDishes.soup !== null; + const hasMainCourse = selectedDishes['main-course'] !== null; + const hasSalad = selectedDishes.salad !== null; + const hasDrink = selectedDishes.drink !== null; + + if (!hasSoup && !hasMainCourse && !hasSalad && !hasDrink) { + return 'Ничего не выбрано. Выберите блюда для заказа'; + } + + if (!hasDrink) { + return 'Выберите напиток'; + } + + if (hasSoup && !hasMainCourse && !hasSalad) { + return 'Выберите главное блюдо/салат/стартер'; + } + + if (hasSalad && !hasSoup && !hasMainCourse) { + return 'Выберите суп или главное блюдо'; + } + + if (!hasMainCourse && !hasSoup && (hasDrink || selectedDishes.dessert)) { + return 'Выберите главное блюдо'; + } + + return ''; +} + +function showNotification(message) { + const notification = document.createElement('div'); + notification.classList.add('notification'); + + const messageText = document.createElement('p'); + messageText.textContent = message; + + const okButton = document.createElement('button'); + okButton.textContent = 'Окей'; + + okButton.addEventListener('click', function() { + notification.remove(); + }); + + notification.append(messageText); + notification.append(okButton); + + document.body.append(notification); +} + +function setupFormValidation() { + const orderForm = document.getElementById('order-form'); + + orderForm.addEventListener('submit', function(event) { + if (!isValidCombo()) { + event.preventDefault(); + const message = getValidationMessage(); + showNotification(message); + } + }); +} + +function restoreSelection() { + Object.keys(selectedDishes).forEach(function(category) { + const dish = selectedDishes[category]; + if (dish) { + const card = document.querySelector( + `.dish-card[data-dish="${dish.keyword}"]` + ); + if (card) { + card.classList.add('selected'); + } + } + }); +} + +function resetSelection() { + Object.keys(selectedDishes).forEach(function(category) { + if (selectedDishes[category]) { + const card = document.querySelector( + `.dish-card[data-dish="${selectedDishes[category].keyword}"]` + ); + if (card) { + card.classList.remove('selected'); + } + } + selectedDishes[category] = null; + }); + updateOrderSummary(); +} + +document.addEventListener('DOMContentLoaded', function() { + loadDishes(); + setupFilters(); + setupFormValidation(); + + const resetButton = document.querySelector('button[type="reset"]'); + if (resetButton) { + resetButton.addEventListener('click', function() { + resetSelection(); + }); + } +}); \ No newline at end of file diff --git a/labs/lab-07/other/Вопросы для подготовки к защите ЛР №7.txt b/labs/lab-07/other/Вопросы для подготовки к защите ЛР №7.txt new file mode 100644 index 0000000..f37ed48 --- /dev/null +++ b/labs/lab-07/other/Вопросы для подготовки к защите ЛР №7.txt @@ -0,0 +1,22 @@ +Получение данных на стороне клиента +1. Что такое Fetch API + +2. Какие есть свойства и методы у объекта Response в Fetch API + +3. Как работает функция fetch() + +4. На базе чего построен Fetch API + +5. Технология AJAX. XMLHttpRequest и fetch + +6. Перечислите основные свойства объекта XMLHttpRequest. + +7. Может ли XMLHttpRequest работать с кросс-доменными запросами? + +8. Почему XMLHttpRequest считается устаревшим подходом по сравнению с Fetch API? + +9. Формат JSON. + +10. Программный интерфейс приложения API. + +11. Что такое промисы (promises)? Как они используются в Fetch API? \ No newline at end of file diff --git a/labs/lab-07/other/Задание - Лабораторная работа 7.md b/labs/lab-07/other/Задание - Лабораторная работа 7.md new file mode 100644 index 0000000..4ef2a46 --- /dev/null +++ b/labs/lab-07/other/Задание - Лабораторная работа 7.md @@ -0,0 +1,51 @@ +# Лабораторная работа №7. Реализация загрузки данных меню с сервера + +--- +Напишите функцию loadDishes(), которая будет загружать данные о доступных блюдах при помощи запроса к API. + +## Порядок выполнения + +Напишите функцию loadDishes(), добавив в нее запрос к API для получения данных о блюдах. +Данные с сервера приходят в формате JSON. Пример: + +[ + { + "category": "soup", + "count": "350 г", + "image": "http://lab7-api.std-900.ist.mospolytech.ru/images/soups/gazpacho", + "keyword": "gaspacho", + "kind": "veg", + "name": "Гаспачо", + "price": 195 + }, + { + "category": "soup", + "count": "330 г", + "image": "http://lab7-api.std-900.ist.mospolytech.ru/images/soups/mushroom_soup", + "keyword": "gribnoy", + "kind": "veg", + "name": "Грибной суп-пюре", + "price": 185 + } +] + +- Для обращения к серверу используйте fetch или XMLHttpRequest. +- Все блюда должны загружаться на страницу при помощи API. +- Внешнее отображение страницы должно остаться прежним. +- Все остальные функции также должны корректно работать (фильтрация, выбор блюда, расчет итоговой стоимости, проверка состава заказа). + +## API URL +- Для тех, кто использует хостинг Московского Политеха: [http://lab7-api.std-900.ist.mospolytech.ru/api/dishes](http://lab7-api.std-900.ist.mospolytech.ru/api/dishes) +- Для тех, кто использует Netlify или GitHub Pages: [https://edu.std-900.ist.mospolytech.ru/labs/api/dishes](https://edu.std-900.ist.mospolytech.ru/labs/api/dishes + +### Материалы для изучения +[Получение данных с сервера [MDN]](https://developer.mozilla.org/ru/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data) +[Работа с JSON [MDN]](https://developer.mozilla.org/ru/docs/Learn/JavaScript/Objects/JSON) +[Промисы, async/await [Learn JS]](https://learn.javascript.ru/async) +[async/await [Doka]](https://doka.guide/js/async-await/) +[Использование промисов [MDN]](https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Using_promises) +[Fetch [Learn JS]](https://learn.javascript.ru/fetch) +[XMLHttpRequest [Learn JS]](https://learn.javascript.ru/xmlhttprequest)  +[Fetch API [MDN]](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)  +[fetch() [Doka]](https://doka.guide/js/fetch/) +[Cross-Origin Resource Sharing (CORS) [MDN]](https://developer.mozilla.org/ru/docs/Web/HTTP/CORS) \ No newline at end of file diff --git a/labs/lab-07/other/Результат запроса.png b/labs/lab-07/other/Результат запроса.png new file mode 100644 index 0000000..6a9cef4 Binary files /dev/null and b/labs/lab-07/other/Результат запроса.png differ diff --git a/labs/lab-07/styles.css b/labs/lab-07/styles.css new file mode 100644 index 0000000..10d0bc1 --- /dev/null +++ b/labs/lab-07/styles.css @@ -0,0 +1,666 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: 'Montserrat', sans-serif; + display: flex; + flex-direction: column; + min-height: 100vh; +} + +header { + background-color: #2d5016; + color: white; + padding: 20px 40px; + margin: 0; +} + +h1 { + margin: 0 0 15px 0; + font-size: 36px; + color: white; +} + +nav { + margin: 0; + padding: 10px 0; + display: flex; + flex-wrap: wrap; + gap: 15px; +} + +nav a { + text-decoration: none; + color: white; + font-family: 'Montserrat', sans-serif; + font-size: 16px; +} + +nav a:hover { + color: #a8d08d; +} + +nav a#active { + color: tomato; +} + +main { + margin: 0 auto; + padding: 40px 60px; + max-width: 1200px; + flex: 1; +} + +section { + margin: 0 0 50px 0; + padding: 30px; + background-color: #f9f9f9; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + flex-wrap: wrap; + gap: 15px; +} + +.section-header h2 { + margin: 0; +} + +h2 { + font-size: 28px; + color: #2d5016; + margin: 0 0 20px 0; +} + +p { + font-family: 'Montserrat', sans-serif; + font-size: 16px; + color: #333; + line-height: 1.6; + margin: 0 0 15px 0; +} + +img { + width: 100%; + height: 400px; + object-fit: cover; + margin: 0 0 20px 0; +} + +ul { + margin: 20px 0; + padding: 0 0 0 25px; +} + +ul li { + font-family: 'Montserrat', sans-serif; + font-size: 16px; + color: #333; + margin: 0 0 12px 0; + line-height: 1.5; +} + +table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; +} + +table th { + background-color: #2d5016; + color: white; + padding: 15px; + text-align: left; + border: 1px solid #2d5016; + font-size: 16px; +} + +table td { + padding: 12px 15px; + border: 1px solid #ddd; + text-align: left; + font-size: 15px; + color: #333; +} + +table tr:nth-child(even) { + background-color: #f2f2f2; +} + +.combo-section { + margin-bottom: 50px; +} + +.combo-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 20px; + margin-bottom: 20px; +} + +.combo-card { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 20px; + background-color: white; + padding: 20px; + border-radius: 15px; +} + +.combo-dessert { + flex-direction: column; + gap: 10px; +} + +.combo-item { + display: flex; + flex-direction: column; + align-items: center; +} + +.combo-icon { + font-size: 50px; + display: block; + margin: 0 0 8px 0; + transition: transform 0.3s; +} + +.combo-item:hover .combo-icon { + transform: translateY(-5px) scale(1.1); +} + +.combo-item p { + margin: 0; + font-size: 14px; + text-align: center; +} + +.combo-note-inline { + font-size: 12px; + text-align: center; + color: #666; + margin: 0; + font-style: italic; +} + +.filter-buttons { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.filter-btn { + background-color: white; + border: 2px solid #2d5016; + color: #2d5016; + padding: 10px 20px; + border-radius: 10px; + cursor: pointer; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + font-weight: 600; + transition: all 0.3s; +} + +.filter-btn:hover { + background-color: #f1eee9; +} + +.filter-btn.active { + background-color: #2d5016; + color: white; +} + +.dishes-grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 10px; +} + +.dish-card { + display: flex; + flex-direction: column; + padding: 30px 40px; + border-radius: 35px; + cursor: pointer; + filter: drop-shadow(17px 19px 24px rgba(0, 0, 0, 0.13)); + background-color: white; +} + +.dish-card.selected { + border: 3px solid #2d5016; + background-color: #f0f7ec; +} + +.dish-card.selected button { + background-color: #2d5016; + color: white; +} + +.dish-card:hover { + border: 2px solid tomato; +} + +.dish-card:hover button { + background-color: tomato; + color: white; +} + +.dish-card img { + width: 100%; + height: auto; + aspect-ratio: 12 / 9; + object-fit: cover; + border-radius: 35px; + margin: 0 0 15px 0; +} + +.dish-price { + font-size: 20px; + font-weight: 600; + margin: 0 0 10px 0; +} + +.dish-name { + font-size: 18px; + font-weight: 600; + margin: 0 0 10px 0; +} + +.dish-weight { + color: #888; + margin: 0 0 10px 0; + margin-top: auto; +} + +.dish-card button { + background-color: #f1eee9; + border: none; + padding: 10px 30px; + border-radius: 10px; + cursor: pointer; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + margin-top: 10px; +} + +.order-form { + margin: 0 auto; + padding: 40px 60px; + max-width: 1200px; + background-color: white; +} + +.order-form h2 { + text-align: center; + margin-bottom: 30px; +} + +#order-summary { + background-color: #f9f9f9; + padding: 30px; + border-radius: 10px; + margin-bottom: 20px; +} + +.empty-order { + text-align: center; + color: #888; + font-size: 18px; +} + +.order-category { + margin-bottom: 25px; +} + +.order-category h3 { + font-size: 20px; + color: #2d5016; + margin: 0 0 10px 0; +} + +.order-item { + display: flex; + justify-content: space-between; + padding: 10px 0; + font-size: 16px; +} + +.order-item span:first-child { + font-weight: 600; + word-wrap: break-word; + overflow-wrap: break-word; + max-width: 70%; +} + +.order-item span:last-child { + color: #2d5016; + font-weight: 600; + white-space: nowrap; +} + +.not-selected { + color: #888; + font-style: italic; + margin: 5px 0; +} + +.order-total { + margin-top: 30px; + padding-top: 20px; + border-top: 2px solid #2d5016; +} + +.order-total h3 { + font-size: 22px; + color: #2d5016; + margin: 0 0 10px 0; +} + +.total-price { + font-size: 24px; + font-weight: 700; + color: #2d5016; + margin: 0; +} + +.form-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 40px; +} + +.order-section { + display: flex; + flex-direction: column; +} + +.order-section h3 { + font-size: 22px; + color: #2d5016; + margin: 0 0 20px 0; +} + +.customer-section { + display: flex; + flex-direction: column; +} + +.customer-section h3 { + font-size: 22px; + color: #2d5016; + margin: 0 0 20px 0; +} + +.order-form label { + font-size: 16px; + color: #333; + margin: 0 0 8px 0; + font-weight: 600; +} + +.order-form select, +.order-form input[type="text"], +.order-form input[type="email"], +.order-form input[type="tel"], +.order-form input[type="time"], +.order-form textarea { + width: 100%; + padding: 12px 15px; + margin: 0 0 20px 0; + border: 1px solid #ddd; + border-radius: 8px; + font-family: 'Montserrat', sans-serif; + font-size: 15px; +} + +.order-form select { + cursor: pointer; + background-color: white; +} + +.order-form textarea { + height: 100px; + resize: vertical; +} + +.checkbox-group { + display: flex; + align-items: center; + margin: 0 0 20px 0; +} + +.checkbox-group input[type="checkbox"] { + width: 18px; + height: 18px; + margin: 0 10px 0 0; + cursor: pointer; +} + +.checkbox-group label { + margin: 0; + font-weight: 400; + cursor: pointer; +} + +.radio-group { + margin: 0 0 20px 0; +} + +.radio-group > label { + display: block; + margin-bottom: 10px; +} + +.radio-group > div { + display: flex; + align-items: center; + margin: 0 0 8px 0; +} + +.radio-group input[type="radio"] { + width: 18px; + height: 18px; + margin: 0 10px 0 0; + cursor: pointer; +} + +.radio-group input[type="radio"] + label { + margin: 0; + font-weight: 400; + cursor: pointer; +} + +.form-buttons { + display: flex; + gap: 15px; + margin-top: 10px; +} + +.form-buttons button { + flex: 1; + padding: 15px 30px; + border: none; + border-radius: 10px; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.3s; +} + +.form-buttons button[type="reset"] { + background-color: #f1eee9; + color: #333; +} + +.form-buttons button[type="reset"]:hover { + background-color: #e0ddd8; +} + +.form-buttons button[type="submit"] { + background-color: #2d5016; + color: white; +} + +.form-buttons button[type="submit"]:hover { + background-color: #3d6020; +} + +.form-hint { + display: block; + font-size: 13px; + color: #666; + margin: -15px 0 20px 0; + line-height: 1.4; +} + +.notification { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: white; + padding: 40px; + border-radius: 15px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); + z-index: 1000; + text-align: center; + max-width: 500px; +} + +.notification p { + font-size: 18px; + margin: 0 0 25px 0; + color: #333; +} + +.notification button { + background-color: #2d5016; + color: white; + border: none; + padding: 12px 40px; + border-radius: 10px; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s; +} + +.notification button:hover { + background-color: white; + color: #2d5016; + border: 2px solid #2d5016; +} + +footer { + background-color: black; + color: white; + padding: 30px 60px; + margin: 0; +} + +footer p { + color: white; + margin: 0 0 10px 0; +} + +footer a { + text-decoration: none; + color: white; + font-family: 'Montserrat', sans-serif; + font-size: 16px; +} + +footer a:hover { + color: #a8d08d; +} + +@media (max-width: 800px) { + .combo-grid { + grid-template-columns: 1fr 1fr; + } + + .dishes-grid { + grid-template-columns: 1fr 1fr; + } + + nav { + justify-content: space-between; + } + + .about-company img { + width: 500px; + } + + .form-container { + grid-template-columns: 1fr; + gap: 20px; + } +} + +@media (max-width: 600px) { + h1 { + text-align: center; + } + + nav { + flex-direction: column; + align-items: center; + } + + nav a { + font-size: 20px; + } + + section h2 { + text-align: center; + } + + .combo-grid { + grid-template-columns: 1fr; + } + + .dishes-grid { + grid-template-columns: 1fr; + } + + .about-company img { + width: 100%; + } + + table th, + table td { + font-size: 14px; + } + + .form-container { + grid-template-columns: 1fr; + } + + .form-buttons { + flex-direction: column; + } + + .notification { + max-width: 90%; + padding: 30px 20px; + } +} + +@media (max-width: 400px) { + table th, + table td { + font-size: 12px; + } +} \ No newline at end of file