diff --git a/labs/lab-08/img/main.jpg b/labs/lab-08/img/main.jpg
new file mode 100644
index 0000000..f9f6558
Binary files /dev/null and b/labs/lab-08/img/main.jpg differ
diff --git a/labs/lab-08/index.html b/labs/lab-08/index.html
new file mode 100644
index 0000000..7939bad
--- /dev/null
+++ b/labs/lab-08/index.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+ ЭкоЛанч - Доставка здоровых бизнес-ланчей в Москве
+
+
+
+
+
+
+
+
ЭкоЛанч
+
+
+
+
+
+
О компании ЭкоЛанч
+
+
ЭкоЛанч - это современная компания по доставке здоровых бизнес-ланчей в офисы Москвы. Наша миссия - сделать правильное питание доступным для занятых людей, которые ценят свое время и здоровье.
+
Мы используем только свежие продукты от проверенных поставщиков, а наши повара готовят блюда каждое утро. Все ланчи упакованы в экологичную биоразлагаемую упаковку, потому что мы заботимся не только о вашем здоровье, но и о здоровье нашей планеты.
+
+
+
+
Наши преимущества
+
+
Быстрая доставка в течение 60 минут по всей Москве в пределах МКАД
+
Свежие блюда, приготовленные утром из качественных продуктов
+ `;
+
+ orderSummary.innerHTML = summaryHTML;
+}
+
+function isValidCombo() {
+ const hasSoup = orderDishes.soup !== null;
+ const hasMainCourse = orderDishes['main-course'] !== null;
+ const hasSalad = orderDishes.salad !== null;
+ const hasDrink = orderDishes.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 = orderDishes.soup !== null;
+ const hasMainCourse = orderDishes['main-course'] !== null;
+ const hasSalad = orderDishes.salad !== null;
+ const hasDrink = orderDishes.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 || orderDishes.dessert)) {
+ return 'Выберите главное блюдо';
+ }
+
+ return '';
+}
+
+function setupFormSubmission() {
+ const form = document.getElementById('order-form');
+
+ if (!form) {
+ console.error('Форма заказа не найдена');
+ return;
+ }
+
+ form.addEventListener('submit', function(event) {
+ event.preventDefault();
+
+ console.log('Отправка формы заказа');
+
+ if (!isValidCombo()) {
+ const message = getValidationMessage();
+ alert(message);
+ return;
+ }
+
+ const formData = new FormData(form);
+ const orderData = {
+ full_name: formData.get('full_name'),
+ email: formData.get('email'),
+ subscribe: formData.get('subscribe') === '1' ? 1 : 0,
+ phone: formData.get('phone'),
+ delivery_address: formData.get('delivery_address'),
+ delivery_type: formData.get('delivery_type'),
+ delivery_time: formData.get('delivery_time') || '',
+ comment: formData.get('comment') || ''
+ };
+
+ if (orderDishes.soup) {
+ orderData.soup_id = orderDishes.soup.id;
+ }
+ if (orderDishes['main-course']) {
+ orderData.main_course_id = orderDishes['main-course'].id;
+ }
+ if (orderDishes.salad) {
+ orderData.salad_id = orderDishes.salad.id;
+ }
+ if (orderDishes.drink) {
+ orderData.drink_id = orderDishes.drink.id;
+ }
+ if (orderDishes.dessert) {
+ orderData.dessert_id = orderDishes.dessert.id;
+ }
+
+ console.log('Данные заказа:', orderData);
+ sendOrder(orderData);
+ });
+}
+
+function sendOrder(orderData) {
+ const url = API_URL + '/orders?api_key=' + API_KEY;
+
+ console.log('Отправка заказа на сервер:', url);
+
+ fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(orderData)
+ })
+ .then(function(response) {
+ console.log('Ответ от сервера:', response.status);
+ if (!response.ok) {
+ return response.json().then(function(errorData) {
+ throw new Error(errorData.error ||
+ 'Ошибка при оформлении заказа');
+ });
+ }
+ return response.json();
+ })
+ .then(function(data) {
+ console.log('Заказ успешно создан:', data);
+ alert('Заказ успешно оформлен! Номер заказа: ' + data.id);
+ localStorage.removeItem('selectedDishes');
+ window.location.href = '../index.html';
+ })
+ .catch(function(error) {
+ console.error('Ошибка при отправке заказа:', error);
+ alert('Не удалось оформить заказ: ' + error.message);
+ });
+}
+
+function setupResetButton() {
+ const resetButton = document.getElementById('reset-button');
+
+ if (!resetButton) {
+ console.error('Кнопка сброса не найдена');
+ return;
+ }
+
+ resetButton.addEventListener('click', function() {
+ const form = document.getElementById('order-form');
+ form.reset();
+ });
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+ console.log('DOM загружен для страницы заказа');
+ loadDishes();
+ setupFormSubmission();
+ setupResetButton();
+});
\ No newline at end of file
diff --git a/labs/lab-08/other/Вопросы для подготовки к защите ЛР №8.txt b/labs/lab-08/other/Вопросы для подготовки к защите ЛР №8.txt
new file mode 100644
index 0000000..2df061a
--- /dev/null
+++ b/labs/lab-08/other/Вопросы для подготовки к защите ЛР №8.txt
@@ -0,0 +1,19 @@
+Local storage
+1. Что такое localStorage?
+2. Какие ограничения имеет localStorage по объему хранимой информации?
+3. Как добавить данные в localStorage с помощью JavaScript?
+4. Можно ли хранить объекты в localStorage напрямую? Если нет, то как это сделать?
+5. Какой метод используется для удаления конкретного ключа из localStorage?
+6. Чем отличается localStorage от sessionStorage?
+7. Может ли localStorage быть использован для хранения конфиденциальных данных? Почему да/почему нет?
+8. Назовите преимущества и недостатки использования localStorage.
+9. Есть ли возможность очистки всего содержимого localStorage одним вызовом метода?
+10. Когда данные в localStorage могут быть автоматически удалены?
+
+
+Взаимодействие клиента и сервера
+1. Что такое клиент-серверная архитектура? В чем заключается ее суть и какие компоненты она включает?
+2. Какие протоколы передачи данных используются в веб-разработке для взаимодействия клиента с сервером? Перечислите основные протоколы и объясните их назначение.
+3. Что такое RESTful API? Дайте определение и приведите пример реализации простого RESTful сервиса.
+4. Объясните принцип работы сессий и куков. Как они помогают сохранять состояние пользователя на стороне сервера и клиента соответственно?
+5. Как осуществляется аутентификация и авторизация на уровне сервера? Описать подходы к защите доступа к ресурсам и методы обеспечения безопасности данных.
\ No newline at end of file
diff --git a/labs/lab-08/other/Задание - Лабораторная работа 8.md b/labs/lab-08/other/Задание - Лабораторная работа 8.md
new file mode 100644
index 0000000..516d5fe
--- /dev/null
+++ b/labs/lab-08/other/Задание - Лабораторная работа 8.md
@@ -0,0 +1,91 @@
+# Лабораторная работа № 8. Реализация функциональности оформления заказа
+
+---
+
+Реализуйте сохранение выбранных пользователем блюд, чтобы после обновления страницы выбор пользователя не сбрасывался. Добавьте страницу оформления заказа, где будет отображаться текущий состав заказа и форма ввода данных. Реализуйте возможность оформления заказа – отправку данных на сервер.
+
+## Порядок выполнения
+
+1. Реализуйте сохранение данных о выбранных пользователем блюдах в localStorage, чтобы в последующем иметь возможность получить эти данные на странице "Оформить заказ". В localStorage должны храниться только идентификаторы выбранных блюд. Формат хранения данных выбирается на усмотрение студента.
+
+2. Добавьте страницу "Оформить заказ". Внешний вид страницы представлен на макете ниже. Добавьте ссылку на эту страницу в навигационное меню. На странице "Оформить заказ" должно быть два раздела: "Состав заказа" и "Оформление заказа".
+ - В разделе "Состав заказа" должны быть отображены ранее добавленные пользователем в заказ блюда (список выбранных блюд нужно загрузить из localStorage, а данные для отображения – загрузить с сервера).
+ - Формат отображения блюд такой же, как на странице "Собрать ланч".
+ - Вместо кнопки "Добавить" в карточке блюда должна быть кнопка "Удалить", по нажатию на которую соответствующее блюдо удаляется из заказа (т. е. информация о нём удаляется из localStorage) и карточка удаляется со страницы.
+ - Если ни одно блюдо не выбрано, нужно отобразить пользователю текст "Ничего не выбрано. Чтобы добавить блюда в заказ, перейдите на страницу Собрать ланч.", где текст "Собрать ланч" – гиперссылка на соответствующую страницу.
+ - В разделе "Оформление заказа" должна отображаться форма оформления заказа, перенесённая со страницы "Собрать ланч".
+ - Выбранные пользователем блюда отображаются в левой части формы (см. макет выше), напротив каждого блюда отображается его стоимость.
+ - При удалении блюда из заказа в соответствующем ему пункте должен отобразиться текст "Не выбран" (или "Не выбрано" для "Главного блюда") и должна быть пересчитана итоговая стоимость заказа.
+ - Перед отправкой данных формы на сервер должна производиться проверка соответствия состава заказа одному из доступных комбо (см. задание к ЛР 6).
+
+3. Внесите исправления в страницу "Собрать ланч": вместо формы оформления заказа разместите панель для перехода к оформлению заказа, в которой будет отображаться текущая стоимость добавленных в заказ блюд и ссылка "Перейти к оформлению", ведущая на страницу оформления заказа.
+ - Панель для перехода к оформлению заказа должна быть скрыта, если пользователь не добавил в заказ ни одного блюда.
+ - Ссылка "Перейти к оформлению" должна быть недоступна до тех пор, пока сосав заказа не удовлетворяет одному из доступных комбо (см. задание к ЛР 6).
+ - Для размещения элемента на странице используйте position: sticky.
+ - При добавлении очередного блюда в заказ, стоимость должна пересчитываться.
+ - При открытии страницы "Собрать ланч" должны учитываться данные текущего заказа, хранящиеся в localStorage (т. е. в меню должны быть выделены выбранные блюда, рассчитана и отображена текущая суммарная стоимость выбранных блюд и т. д.).
+
+4. Реализуйте отправку данных заказа на сервер по нажатию на кнопку "Отправить".
+ - Запрос должен быть отправлен при помощи fetch.
+ - В случае возникновения ошибки при отправке запроса или его обработке на стороне сервера пользователю должно быть отображено уведомление об ошибке (можно использовать функцию alert или написать кастомное модальное окно – на ваше усмотрение).
+ - После успешного оформления заказа данные о выбранных блюдах из localStorage должны удаляться. Если не удалось оформить заказ – данные не удаляются.
+
+## Инструкция по работе с API
+
+Для получения доступа к API необходимо пройти процедуру авторизации. Для авторизации нужно указать в качестве параметра запроса api_key значение уникального ключа, который выдаётся каждому пользователю. Ключ представляет собой идентификатор UUIDv4, который является случайным 16-байтным номером (например, 123e4567-e89b-12d3-a456-426655440000).
+
+**Обратите внимание, что параметр api_key всегда передаётся в строке запроса.**
+
+Пользователь может просматривать, редактировать и удалять только свои заказы. В один момент времени в базе данных может быть не более 10 заказов, созданных одним и тем же пользователем.
+
+Если пользователь попробует совершить действие, не пройдя авторизацию, в качестве ответа на его запрос придёт сообщение:
+
+{"error": "Для получения доступа к API необходимо пройти процедуру авторизации. Для этого нужно передать в запросе персональный API Key."}
+
+При передаче параметров в POST- и PUT-запросах данные должны передаваться в теле запроса в формате application/json (нужно вручную сериализовать данные и установить значение заголовка Content-Type) или multipart/form-data (достаточно при отправке данных формы воспользоваться объектом FormData).
+
+При оформлении заказа в запросе к серверу должны быть указаны значения обязательных полей:
+
+|Название|Тип|Обязательное|Только для чтения|Примечание|
+|---|---|---|---|---|
+|id|Integer||Да|Устанавливается сервером.|
+|full_name|String|Да|||
+|email|String|Да||
+|subscribe|Boolean|Нет||Допустимы значения 0 и 1.|
+|phone|String|Да|||
+|delivery_address|String|Да|||
+|delivery_type|String|Да||Допустимы значения "now" и "by_time".|
+|delivery_time|Time|Нет||Значение передаётся в формате HH:MM. Доступное время доставки: с 7:00 до 23:00 с шагом 5 минут. Не должно быть пустым, если delivery_type=by_time. Не должно быть раньше текущего времени.|
+|comment|String|Нет|||
+|soup_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
+|main_course_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
+|salad_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
+|drink_id|Integer|Да|||
+|dessert_id|Integer|Нет|||
+|created_at|DateTime||Да|Устанавливается сервером.|
+|updated_at|DateTime||Да|Устанавливается сервером.|
+|student_id|Integer||Да|Устанавливается сервером.|
+
+Доступные пользователю действия представлены в таблице:
+
+|Действие|Метод и путь|Формат ответа|Примечание|
+|---|---|---|---|
+|Получить данные всех блюд|GET /labs/api/dishes|JSON [{item1},{item2},...{itemN}]||
+|Получить данные конкретного блюда|GET /labs/api/dishes/{int:dish_id}|JSON {Item}|Вместо {int:dish_id} нужно подставить целое число – идентификатор блюда.|
+|Получить данные всех заказов|GET /labs/api/orders|JSON [{item1},{item2},...{itemN}]||
+|Получить данные конкретного заказа|GET /labs/api/orders/{int:order_id}|JSON {Item}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа.|
+|Создать новый заказ|POST /labs/api/orders|JSON {newItem}|Нужно передать значения всех обязательных полей.|
+|Изменить заказ|PUT /labs/api/orders/{int:order_id}|JSON {updateItem}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа. Достаточно передать только значения изменившихся полей.|
+|Удалить заказ|DELETE /labs/api/orders/{int:order_id}|JSON {Item}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа.|
+
+### Материалы для изучения
+[localStorage [Doka]](https://doka.guide/js/local-storage/)
+[Получение данных с сервера [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)
+[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-08/styles/menu.css b/labs/lab-08/styles/menu.css
new file mode 100644
index 0000000..6da4b73
--- /dev/null
+++ b/labs/lab-08/styles/menu.css
@@ -0,0 +1,290 @@
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.section-header h2 {
+ margin: 0;
+}
+
+.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;
+}
+
+.combo-note {
+ font-size: 14px;
+ color: #666;
+ font-style: italic;
+ text-align: center;
+ margin: 10px 0 0 0;
+}
+
+.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;
+}
+
+.remove-dish-btn {
+ background-color: tomato;
+ color: white;
+}
+
+.remove-dish-btn:hover {
+ background-color: #ff4500;
+}
+
+.order-panel {
+ position: sticky;
+ bottom: 20px;
+ background-color: white;
+ padding: 20px 40px;
+ margin: 0 auto;
+ max-width: 1200px;
+ border-radius: 15px;
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
+ z-index: 100;
+}
+
+.order-panel.hidden {
+ display: none;
+}
+
+.order-panel-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 20px;
+}
+
+.order-panel-info {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+}
+
+.order-panel-label {
+ font-size: 18px;
+ font-weight: 600;
+ margin: 0;
+}
+
+.order-panel-price {
+ font-size: 24px;
+ font-weight: 700;
+ color: #2d5016;
+ margin: 0;
+}
+
+.order-panel-button {
+ background-color: #2d5016;
+ color: white;
+ text-decoration: none;
+ padding: 15px 40px;
+ border-radius: 10px;
+ font-family: 'Montserrat', sans-serif;
+ font-size: 16px;
+ font-weight: 600;
+ transition: all 0.3s;
+}
+
+.order-panel-button:hover {
+ background-color: #3d6020;
+}
+
+.order-panel-button.disabled {
+ background-color: #ccc;
+ color: #888;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
+@media (max-width: 800px) {
+ .combo-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .dishes-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .order-panel-content {
+ flex-direction: column;
+ }
+}
+
+@media (max-width: 600px) {
+ .combo-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .dishes-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .order-panel {
+ padding: 15px 20px;
+ }
+
+ .order-panel-content {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .order-panel-button {
+ text-align: center;
+ }
+}
\ No newline at end of file
diff --git a/labs/lab-08/styles/order.css b/labs/lab-08/styles/order.css
new file mode 100644
index 0000000..aed29b7
--- /dev/null
+++ b/labs/lab-08/styles/order.css
@@ -0,0 +1,266 @@
+.empty-order-message {
+ text-align: center;
+ padding: 40px 20px;
+}
+
+.empty-order-message.hidden {
+ display: none;
+}
+
+.empty-order-message p {
+ font-size: 18px;
+ color: #666;
+}
+
+.empty-order-message a {
+ color: #2d5016;
+ text-decoration: underline;
+}
+
+.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="button"] {
+ background-color: #f1eee9;
+ color: #333;
+}
+
+.form-buttons button[type="button"]: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;
+}
+
+@media (max-width: 800px) {
+ .form-container {
+ grid-template-columns: 1fr;
+ gap: 20px;
+ }
+}
+
+@media (max-width: 600px) {
+ .form-container {
+ grid-template-columns: 1fr;
+ }
+
+ .form-buttons {
+ flex-direction: column;
+ }
+}
\ No newline at end of file
diff --git a/labs/lab-08/styles/styles.css b/labs/lab-08/styles/styles.css
new file mode 100644
index 0000000..58e6c37
--- /dev/null
+++ b/labs/lab-08/styles/styles.css
@@ -0,0 +1,190 @@
+* {
+ 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;
+}
+
+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;
+}
+
+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) {
+ nav {
+ justify-content: space-between;
+ }
+
+ .about-company img {
+ width: 500px;
+ }
+}
+
+@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;
+ }
+
+ .about-company img {
+ width: 100%;
+ }
+
+ table th,
+ table td {
+ font-size: 14px;
+ }
+}
+
+@media (max-width: 400px) {
+ table th,
+ table td {
+ font-size: 12px;
+ }
+}
\ No newline at end of file
diff --git a/labs/lab-08/templates/about.html b/labs/lab-08/templates/about.html
new file mode 100644
index 0000000..7cee6eb
--- /dev/null
+++ b/labs/lab-08/templates/about.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ О нас - ЭкоЛанч
+
+
+
+
+
+
+
+
ЭкоЛанч
+
+
+
+
+
+
О нашей компании
+
ЭкоЛанч - это команда профессионалов, которые заботятся о вашем здоровье и комфорте. Мы готовим вкусные и полезные блюда из свежих продуктов и доставляем их прямо в ваш офис.
+
Наша миссия - сделать здоровое питание доступным и удобным для каждого занятого человека в Москве.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/labs/lab-08/templates/delivery.html b/labs/lab-08/templates/delivery.html
new file mode 100644
index 0000000..c1d652f
--- /dev/null
+++ b/labs/lab-08/templates/delivery.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ Доставка - ЭкоЛанч
+
+
+
+
+
+
+
+
ЭкоЛанч
+
+
+
+
+
+
Условия доставки
+
Мы осуществляем доставку здоровых бизнес-ланчей по всей Москве. Доставка производится с понедельника по пятницу с 7:00 до 23:00.
+
Доставка по всей Москве в пределах МКАД осуществляется бесплатно!
+
+
+
+
+
+
\ No newline at end of file
diff --git a/labs/lab-08/templates/menu.html b/labs/lab-08/templates/menu.html
new file mode 100644
index 0000000..5c3bea3
--- /dev/null
+++ b/labs/lab-08/templates/menu.html
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+ Собрать ланч - ЭкоЛанч
+
+
+
+
+
+
+
+
+