recipes/index.html

917 lines
49 KiB
HTML
Raw Permalink 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" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Вкусно — Рецепты на каждый день</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
[data-theme="light"] {
--ink: #1a1a1a; --bg: #FFFFFF; --bg-card: #f8f9fa; --bg-card-hover: #fff3e0;
--text-secondary: #6b7280; --accent: #ff6b35; --accent-dark: #e55a2b;
--success: #22c55e; --warning: #f59e0b; --error: #ef4444;
--border: #e5e7eb; --shadow: 0 4px 24px rgba(0,0,0,0.08);
--shadow-lg: 0 12px 48px rgba(0,0,0,0.12);
--gradient-hero: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
--gradient-accent: linear-gradient(135deg, #ff6b35 0%, #ff4500 100%);
--glass: rgba(255,255,255,0.9); --glass-border: rgba(255,255,255,0.2);
--bg-hero: #1a1a1a;
}
[data-theme="dark"] {
--ink: #f0f2f5; --bg: #0f0f0f; --bg-card: #1a1a1a; --bg-card-hover: #2a2a2a;
--text-secondary: #9ca3af; --accent: #ff6b35; --accent-dark: #ff8c42;
--success: #4ade80; --warning: #fbbf24; --error: #f87171;
--border: #2a2a2a; --shadow: 0 4px 24px rgba(0,0,0,0.3);
--shadow-lg: 0 12px 48px rgba(0,0,0,0.4);
--gradient-hero: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
--gradient-accent: linear-gradient(135deg, #ff6b35 0%, #ff4500 100%);
--glass: rgba(15,15,15,0.9); --glass-border: rgba(255,255,255,0.08);
--bg-hero: #0a0a0a;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
font: 16px/1.6 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
color: var(--ink); background: var(--bg); transition: background 0.3s, color 0.3s;
}
.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
/* NAV */
.nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
background: var(--glass); backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border); padding: 0 24px;
}
.nav-inner {
max-width: 1200px; margin: 0 auto;
display: flex; align-items: center; justify-content: space-between; height: 64px;
}
.nav-logo {
font-size: 24px; font-weight: 800; cursor: pointer;
color: var(--accent);
}
.nav-logo span { color: var(--ink); }
.nav-links { display: flex; gap: 28px; list-style: none; }
.nav-links a {
color: var(--text-secondary); text-decoration: none; font-weight: 600; font-size: 14px;
transition: color 0.2s; cursor: pointer;
}
.nav-links a:hover { color: var(--accent); }
.nav-actions { display: flex; align-items: center; gap: 12px; }
.theme-toggle {
width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--border);
background: var(--bg-card); cursor: pointer; display: flex; align-items: center;
justify-content: center; font-size: 18px; transition: all 0.2s;
}
.theme-toggle:hover { border-color: var(--accent); }
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 10px 20px; border-radius: 10px; font-weight: 700; font-size: 14px;
text-decoration: none; border: none; cursor: pointer; transition: all 0.2s;
}
.btn-primary {
background: var(--accent); color: #fff;
box-shadow: 0 4px 16px rgba(255,107,53,0.3);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(255,107,53,0.4); }
.btn-secondary { background: var(--bg-card); color: var(--ink); border: 1px solid var(--border); }
.btn-secondary:hover { border-color: var(--accent); }
.btn-sm { padding: 8px 14px; font-size: 13px; }
.btn-lg { padding: 14px 28px; font-size: 16px; }
.hamburger {
display: none; width: 40px; height: 40px; border: none; background: none;
cursor: pointer; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.hamburger span { width: 22px; height: 2px; background: var(--ink); transition: all 0.3s; }
/* HERO */
.hero {
background: var(--gradient-hero); padding: 120px 24px 80px;
text-align: center; position: relative; overflow: hidden;
}
.hero::before {
content: ''; position: absolute; inset: 0;
background: radial-gradient(circle at 30% 50%, rgba(0,0,0,0.1) 0%, transparent 50%),
radial-gradient(circle at 70% 50%, rgba(255,255,255,0.1) 0%, transparent 50%);
}
.hero-content { position: relative; z-index: 1; max-width: 700px; margin: 0 auto; }
.hero-emoji { font-size: 64px; margin-bottom: 20px; }
.hero h1 { font-size: 48px; font-weight: 800; line-height: 1.1; color: #fff; margin-bottom: 16px; }
.hero p { font-size: 18px; color: rgba(255,255,255,0.9); margin-bottom: 32px; }
.hero-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.hero .btn-outline { background: transparent; color: #fff; border: 2px solid rgba(255,255,255,0.5); }
.hero .btn-outline:hover { background: #fff; color: var(--accent); }
.hero-stats {
display: flex; gap: 40px; justify-content: center; margin-top: 48px;
padding-top: 32px; border-top: 1px solid rgba(255,255,255,0.2);
}
.hero-stat h3 { font-size: 28px; font-weight: 800; color: #fff; }
.hero-stat p { font-size: 13px; color: rgba(255,255,255,0.8); }
/* SECTIONS */
.section { padding: 80px 24px; }
.section-header { text-align: center; max-width: 600px; margin: 0 auto 48px; }
.section-header .badge {
display: inline-block; padding: 6px 14px; background: rgba(255,107,53,0.1);
color: var(--accent); border-radius: 100px; font-size: 12px; font-weight: 700; margin-bottom: 12px;
}
.section-header h2 { font-size: 36px; font-weight: 800; margin-bottom: 12px; }
.section-header p { color: var(--text-secondary); font-size: 16px; }
/* CATEGORIES */
.categories-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px;
}
.category-card {
background: var(--bg-card); border-radius: 16px; padding: 20px;
border: 2px solid var(--border); text-align: center; cursor: pointer;
transition: all 0.2s;
}
.category-card:hover { border-color: var(--accent); transform: translateY(-4px); box-shadow: var(--shadow); }
.category-card.active { border-color: var(--accent); background: rgba(255,107,53,0.05); }
.category-card .emoji { font-size: 36px; margin-bottom: 8px; }
.category-card h4 { font-size: 14px; font-weight: 700; }
.category-card p { font-size: 12px; color: var(--text-secondary); }
/* RECIPE GRID */
.recipes-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 24px;
}
.recipe-card {
background: var(--bg-card); border-radius: 16px; overflow: hidden;
border: 1px solid var(--border); transition: all 0.3s; cursor: pointer;
}
.recipe-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.recipe-image {
width: 100%; height: 200px; object-fit: cover;
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
display: flex; align-items: center; justify-content: center;
font-size: 64px;
}
.recipe-body { padding: 20px; }
.recipe-meta {
display: flex; gap: 12px; margin-bottom: 12px; flex-wrap: wrap;
}
.recipe-meta span {
font-size: 12px; color: var(--text-secondary);
display: flex; align-items: center; gap: 4px;
padding: 4px 10px; background: var(--bg); border-radius: 100px;
}
.recipe-card h3 { font-size: 18px; font-weight: 700; margin-bottom: 8px; }
.recipe-card .desc { font-size: 14px; color: var(--text-secondary); margin-bottom: 12px; }
.recipe-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.recipe-tag {
padding: 4px 10px; background: rgba(255,107,53,0.1); color: var(--accent);
border-radius: 100px; font-size: 11px; font-weight: 600;
}
.recipe-fav {
position: absolute; top: 12px; right: 12px; width: 36px; height: 36px;
border-radius: 50%; border: none; background: rgba(255,255,255,0.9);
cursor: pointer; font-size: 18px; transition: all 0.2s;
display: flex; align-items: center; justify-content: center;
}
.recipe-fav:hover { transform: scale(1.1); }
.recipe-fav.active { color: var(--error); }
.recipe-card-wrap { position: relative; }
/* RECIPE DETAIL */
.recipe-detail {
display: none; background: var(--bg-card); border-radius: 20px;
border: 1px solid var(--border); overflow: hidden;
}
.recipe-detail.active { display: block; }
.recipe-detail-header {
background: var(--gradient-hero); padding: 40px; color: #fff; text-align: center;
}
.recipe-detail-header h2 { font-size: 32px; margin-bottom: 8px; }
.recipe-detail-header p { opacity: 0.9; }
.recipe-detail-body { padding: 32px; }
.recipe-info-grid {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 32px;
}
.recipe-info-item {
text-align: center; padding: 16px; background: var(--bg); border-radius: 12px;
}
.recipe-info-item .icon { font-size: 24px; margin-bottom: 8px; }
.recipe-info-item .value { font-size: 18px; font-weight: 700; }
.recipe-info-item .label { font-size: 12px; color: var(--text-secondary); }
.ingredients-list { list-style: none; margin-bottom: 32px; }
.ingredients-list li {
padding: 12px 0; border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 12px; font-size: 15px;
}
.ingredients-list li::before {
content: ''; width: 8px; height: 8px; border-radius: 50%;
background: var(--accent); flex-shrink: 0;
}
.steps-list { list-style: none; counter-reset: step; }
.steps-list li {
padding: 16px 0; border-bottom: 1px solid var(--border);
display: flex; gap: 16px; font-size: 15px; line-height: 1.8;
counter-increment: step;
}
.steps-list li::before {
content: counter(step); width: 32px; height: 32px; border-radius: 50%;
background: var(--accent); color: #fff; display: flex; align-items: center;
justify-content: center; font-weight: 700; font-size: 14px; flex-shrink: 0;
}
.recipe-actions { display: flex; gap: 12px; margin-top: 24px; flex-wrap: wrap; }
/* SHOPPING LIST */
.shopping-section { background: var(--bg-card); }
.shopping-list {
max-width: 600px; margin: 0 auto; background: var(--bg);
border-radius: 16px; border: 1px solid var(--border); padding: 24px;
}
.shopping-item {
display: flex; align-items: center; gap: 12px; padding: 12px 0;
border-bottom: 1px solid var(--border);
}
.shopping-item:last-child { border: none; }
.shopping-checkbox {
width: 24px; height: 24px; border-radius: 6px; border: 2px solid var(--border);
cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: all 0.2s; background: var(--bg);
}
.shopping-checkbox.checked {
background: var(--accent); border-color: var(--accent); color: #fff;
}
.shopping-checkbox.checked + span { text-decoration: line-through; color: var(--text-secondary); }
.shopping-item span { flex: 1; font-size: 15px; }
.shopping-item .remove-btn {
width: 28px; height: 28px; border-radius: 6px; border: none;
background: transparent; cursor: pointer; color: var(--text-secondary);
display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.shopping-item .remove-btn:hover { color: var(--error); background: rgba(239,68,68,0.1); }
.shopping-add {
display: flex; gap: 8px; margin-bottom: 20px;
}
.shopping-add input {
flex: 1; padding: 12px 16px; border-radius: 10px; border: 1px solid var(--border);
background: var(--bg-card); color: var(--ink); font-size: 15px; outline: none;
}
.shopping-add input:focus { border-color: var(--accent); }
/* SEARCH */
.search-bar {
display: flex; gap: 12px; margin-bottom: 32px; flex-wrap: wrap;
}
.search-input {
flex: 1; min-width: 250px; padding: 14px 20px; border-radius: 12px;
border: 1px solid var(--border); background: var(--bg); color: var(--ink);
font-size: 15px; outline: none; transition: border-color 0.2s;
}
.search-input:focus { border-color: var(--accent); }
/* FAVORITES */
.favorites-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 24px;
}
.empty-state {
text-align: center; padding: 60px 20px; color: var(--text-secondary);
}
.empty-state .emoji { font-size: 64px; margin-bottom: 16px; }
.empty-state h3 { font-size: 20px; margin-bottom: 8px; color: var(--ink); }
/* FOOTER */
.footer { background: var(--bg-hero); padding: 48px 24px 24px; color: #fff; }
.footer-content { text-align: center; max-width: 500px; margin: 0 auto; }
.footer-content h3 { font-size: 22px; font-weight: 800; margin-bottom: 8px; color: var(--accent); }
.footer-content p { color: #9ca3af; font-size: 14px; margin-bottom: 20px; }
.footer-bottom { text-align: center; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.1); color: #9ca3af; font-size: 13px; }
/* TOAST */
.toast-container { position: fixed; bottom: 24px; right: 24px; z-index: 3000; display: flex; flex-direction: column; gap: 8px; }
.toast {
padding: 14px 20px; background: var(--bg); border: 1px solid var(--border);
border-radius: 10px; box-shadow: var(--shadow-lg); display: flex; align-items: center;
gap: 10px; animation: slideIn 0.3s ease; min-width: 260px;
}
.toast.success { border-left: 4px solid var(--success); }
.toast.error { border-left: 4px solid var(--error); }
.toast.info { border-left: 4px solid var(--accent); }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
/* MOBILE */
.mobile-menu {
display: none; position: fixed; top: 64px; left: 0; right: 0;
background: var(--bg); border-bottom: 1px solid var(--border); padding: 20px; z-index: 999;
}
.mobile-menu.active { display: block; }
.mobile-menu a { display: block; padding: 12px 0; color: var(--ink); text-decoration: none; font-weight: 600; font-size: 16px; cursor: pointer; }
.page { display: none; }
.page.active { display: block; }
@media (max-width: 1024px) {
.recipes-grid, .favorites-grid { grid-template-columns: repeat(2, 1fr); }
.recipe-info-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
.nav-links { display: none; } .hamburger { display: flex; }
.hero { padding: 100px 20px 60px; } .hero h1 { font-size: 32px; }
.hero-emoji { font-size: 48px; } .hero-stats { flex-direction: column; gap: 16px; }
.section { padding: 48px 20px; } .section-header h2 { font-size: 28px; }
.recipes-grid, .favorites-grid { grid-template-columns: 1fr; }
.recipe-detail-body { padding: 20px; }
.recipe-info-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
.hero h1 { font-size: 26px; } .hero-actions { flex-direction: column; }
.hero-actions .btn { width: 100%; } .categories-grid { grid-template-columns: repeat(2, 1fr); }
}
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.fade-in { opacity: 0; transform: translateY(20px); transition: all 0.6s ease; }
.fade-in.visible { opacity: 1; transform: translateY(0); }
</style>
</head>
<body>
<nav class="nav">
<div class="nav-inner">
<span class="nav-logo" onclick="showPage('home')">🍳 Вкусно<span>Рецепты</span></span>
<ul class="nav-links">
<li><a onclick="showPage('home')">Главная</a></li>
<li><a onclick="showPage('recipes')">Рецепты</a></li>
<li><a onclick="showPage('favorites')">Избранное</a></li>
<li><a onclick="showPage('shopping')">Список покупок</a></li>
</ul>
<div class="nav-actions">
<button class="theme-toggle" onclick="toggleTheme()">🌙</button>
<button class="hamburger" onclick="toggleMobile()"><span></span><span></span><span></span></button>
</div>
</div>
</nav>
<div class="mobile-menu" id="mobileMenu">
<a onclick="showPage('home');toggleMobile()">Главная</a>
<a onclick="showPage('recipes');toggleMobile()">Рецепты</a>
<a onclick="showPage('favorites');toggleMobile()">Избранное</a>
<a onclick="showPage('shopping');toggleMobile()">Список покупок</a>
</div>
<!-- HOME -->
<div class="page active" id="page-home">
<section class="hero">
<div class="hero-content">
<div class="hero-emoji">👨‍🍳</div>
<h1>Рецепты на каждый день</h1>
<p>Простые и вкусные рецепты для завтраков, обедов и ужинов. Готовьте с удовольствием!</p>
<div class="hero-actions">
<a class="btn btn-lg" style="background:#fff;color:var(--accent)" onclick="showPage('recipes')">Смотреть рецепты</a>
<a class="btn btn-outline btn-lg" onclick="showPage('favorites')">Мои избранные</a>
</div>
<div class="hero-stats">
<div class="hero-stat"><h3>50+</h3><p>Рецептов</p></div>
<div class="hero-stat"><h3>8</h3><p>Категорий</p></div>
<div class="hero-stat"><h3>15 мин</h3><p>Среднее время</p></div>
</div>
</div>
</section>
<section class="section" id="categories">
<div class="container">
<div class="section-header fade-in">
<div class="badge">КАТЕГОРИИ</div>
<h2>Выберите категорию</h2>
<p>Рецепты для любого повода</p>
</div>
<div class="categories-grid fade-in" id="categoriesGrid"></div>
</div>
</section>
<section class="section" style="background:var(--bg-card)">
<div class="container">
<div class="section-header fade-in">
<div class="badge">ПОПУЛЯРНЫЕ</div>
<h2>Любимые рецепты</h2>
<p>Самые популярные блюда наших пользователей</p>
</div>
<div class="recipes-grid fade-in" id="popularGrid"></div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="footer-content">
<h3>🍳 Вкусно Рецепты</h3>
<p>Готовьте с любовью и удовольствием</p>
</div>
<div class="footer-bottom">© 2026 Вкусно Рецепты</div>
</div>
</footer>
</div>
<!-- RECIPES PAGE -->
<div class="page" id="page-recipes">
<section class="section" style="padding-top:100px">
<div class="container">
<div class="section-header"><div class="badge">РЕЦЕПТЫ</div><h2>Все рецепты</h2><p>Найдите идеальный рецепт</p></div>
<div class="search-bar">
<input type="text" class="search-input" placeholder="Поиск рецептов..." oninput="searchRecipes()">
<div id="categoryFilters" style="display:flex;gap:8px;flex-wrap:wrap"></div>
</div>
<div class="recipes-grid" id="allRecipesGrid"></div>
</div>
</section>
</div>
<!-- RECIPE DETAIL -->
<div class="page" id="page-detail">
<section class="section" style="padding-top:100px">
<div class="container">
<div style="margin-bottom:20px"><button class="btn btn-secondary" onclick="showPage('recipes')">← Назад к рецептам</button></div>
<div class="recipe-detail active" id="recipeDetail"></div>
</div>
</section>
</div>
<!-- FAVORITES PAGE -->
<div class="page" id="page-favorites">
<section class="section" style="padding-top:100px">
<div class="container">
<div class="section-header"><div class="badge">ИЗБРАННОЕ</div><h2>Мои рецепты</h2><p>Сохранённые рецепты</p></div>
<div id="favoritesContent"></div>
</div>
</section>
</div>
<!-- SHOPPING LIST PAGE -->
<div class="page" id="page-shopping">
<section class="section shopping-section" style="padding-top:100px">
<div class="container">
<div class="section-header"><div class="badge">СПИСОК ПОКУПОК</div><h2>Что купить</h2><p>Добавляйте ингредиенты из рецептов</p></div>
<div class="shopping-list">
<div class="shopping-add">
<input type="text" id="shoppingInput" placeholder="Добавить продукт..." onkeypress="if(event.key==='Enter')addShoppingItem()">
<button class="btn btn-primary" onclick="addShoppingItem()">Добавить</button>
</div>
<div id="shoppingList"></div>
<div style="margin-top:16px;display:flex;gap:8px">
<button class="btn btn-secondary btn-sm" onclick="clearChecked()">Очистить купленные</button>
<button class="btn btn-secondary btn-sm" onclick="clearAll()">Очистить все</button>
</div>
</div>
</div>
</section>
</div>
<div class="toast-container" id="toastContainer"></div>
<script>
const categoriesData = [
{id:'breakfast',emoji:'🥞',name:'Завтраки',count:8},
{id:'lunch',emoji:'🍲',name:'Обеды',count:10},
{id:'dinner',emoji:'🍽️',name:'Ужины',count:12},
{id:'salads',emoji:'🥗',name:'Салаты',count:7},
{id:'soups',emoji:'🍜',name:'Супы',count:6},
{id:'desserts',emoji:'🍰',name:'Десерты',count:8},
{id:'drinks',emoji:'🥤',name:'Напитки',count:5},
{id:'snacks',emoji:'🥪',name:'Закуски',count:6},
];
const recipesData = [
{
id:1,category:'breakfast',emoji:'🥞',title:'Оладьи на кефире',time:'20 мин',servings:'4 порции',difficulty:'Легко',calories:'180 ккал',
desc:'Пышные оладьи на кефире — идеальный завтрак за 20 минут',
tags:['завтрак','быстро','выпечка'],
ingredients:['Кефир — 500 мл','Яйца — 2 шт','Сахар — 2 ст.л','Соль — 0.5 ч.л','Мука — 300 г','Сода — 1 ч.л','Масло растительное'],
steps:['Смешайте кефир с яйцами и сахаром.','Добавьте соль и соду, перемешайте.','Постепенно всыпьте муку, замесите тесто.','Разогрейте сковороду с маслом.','Ложкой выкладывайте тесто, жарьте до золотистого цвета с обеих сторон.','Подавайте со сметаной или вареньем.']
},
{
id:2,category:'breakfast',emoji:'🍳',title:'Яичница с беконом',time:'15 мин',servings:'2 порции',difficulty:'Легко',calories:'320 ккал',
desc:'Классический завтрак с хрустящим беконом и яйцами',
tags:['завтрак','быстро','белковое'],
ingredients:['Яйца — 4 шт','Бекон — 200 г','Масло сливочное — 20 г','Соль, перец','Зелень'],
steps:['Обжарьте бекон на сковороде до хрустящей корочки.','Уберите бекон, в ту же сковороду добавьте масло.','Разбейте яйца, посолите и поперчите.','Жарьте на среднем огне 3-4 минуты.','Подавайте с беконом и свежей зеленью.']
},
{
id:3,category:'breakfast',emoji:'🥣',title:'Овсянка с ягодами',time:'10 мин',servings:'1 порция',difficulty:'Легко',calories:'250 ккал',
desc:'Полезная овсянка с свежими ягодами и мёдом',
tags:['завтрак','полезно','быстро'],
ingredients:['Овсяные хлопья — 80 г','Молоко — 200 мл','Мёд — 1 ст.л','Ягоды — 100 г','Орехи — 30 г'],
steps:['Залейте хлопья молоком.','Варите на среднем огне 5 минут, помешивая.','Добавьте мёд и перемешайте.','Переложите в тарелку, сверху выложите ягоды и орехи.']
},
{
id:4,category:'lunch',emoji:'🍗',title:'Курица с рисом',time:'40 мин',servings:'4 порции',difficulty:'Средне',calories:'450 ккал',
desc:'Сочная курица с ароматным рисом и овощами',
tags:['обед','основное','курица'],
ingredients:['Куриное филе — 500 г','Рис — 300 г','Лук — 1 шт','Морковь — 1 шт','Чеснок — 3 зубчика','Соевый соус — 3 ст.л','Масло растительное','Соль, перец, специи'],
steps:['Нарежьте курицу кубиками, обжарьте на масле.','Добавьте нарезанный лук и морковь.','Пассеруйте 5 минут, добавьте чеснок.','Всыпьте рис, залите водой (1:2).','Добавьте соевый соус, специи.','Накройте крышкой, варите 20 минут на слабом огне.']
},
{
id:5,category:'lunch',emoji:'🥘',title:'Плов',time:'60 мин',servings:'6 порций',difficulty:'Средне',calories:'520 ккал',
desc:'Классический узбекский плов с бараниной',
tags:['обед','основное','плов'],
ingredients:['Баранина — 700 г','Рис — 500 г','Морковь — 3 шт','Лук — 2 шт','Чеснок — 1 головка','Нут — 100 г','Зира, барбарис','Масло растительное','Соль'],
steps:['Нарежьте мясо, обжарьте в казане.','Добавьте лук, затем морковь соломкой.','Всыпьте специи, положите чеснок и нут.','Залите водой, тушите 30 минут.','Промытый рис выложите сверху, залите водой.','Варите на сильном огне, затем на слабом 20 минут.','Перемешайте перед подачей.']
},
{
id:6,category:'dinner',emoji:'🍝',title:'Паста Карбонара',time:'25 мин',servings:'2 порции',difficulty:'Средне',calories:'580 ккал',
desc:'Классическая итальянская паста с беконом и яичным соусом',
tags:['ужин','итальянская','паста'],
ingredients:['Спагетти — 200 г','Бекон — 150 г','Яйца — 3 шт','Пармезан — 50 г','Чеснок — 2 зубчика','Чёрный перец','Соль'],
steps:['Отварите пасту al dente.','Обжарьте бекон с чесноком.','Смешайте яйца с тёртым пармезаном.','Слейте пасту, оставив немного воды.','Добавьте пасту к бекону, снимите с огня.','Влейте яичную смесь, быстро перемешайте.','Подавайте с перцем и пармезаном.']
},
{
id:7,category:'dinner',emoji:'🥩',title:'Стейк с овощами',time:'30 мин',servings:'2 порции',difficulty:'Средне',calories:'480 ккал',
desc:'Сочный стейк на гриле с овощами-гриль',
tags:['ужин','мясо','гриль'],
ingredients:['Говяжий стейк — 400 г','Кабачок — 1 шт','Перец болгарский — 2 шт','Помидоры черри — 200 г','Масло оливковое','Розмарин, тимьян','Соль, перец'],
steps:['Достаньте стейк за 30 минут до готовки.','Нарежьте овощи, сбрызните маслом.','Разогрейте гриль-сковороду.','Обжарьте стейк 3-4 минуты с каждой стороны.','Обжарьте овощи.','Дайте стейку отдохнуть 5 минут.','Подавайте с овощами.']
},
{
id:8,category:'salads',emoji:'🥗',title:'Цезарь с курицей',time:'20 мин',servings:'2 порции',difficulty:'Легко',calories:'380 ккал',
desc:'Классический салат Цезарь с куриной грудкой',
tags:['салат','курица','быстро'],
ingredients:['Куриное филе — 300 г','Салат романо — 1 кочан','Пармезан — 50 г','Сухарики — 100 г','Яйца — 2 шт','Чеснок, лимон','Масло оливковое','Анчоусы (по желанию)'],
steps:['Обжарьте курицу, нарежьте ломтиками.','Приготовьте соус: желтки + масло + чеснок + лимон.','Нарвите салат, выложите в тарелку.','Добавьте курицу, сухарики, пармезан.','Полейте соусом, аккуратно перемешайте.']
},
{
id:9,category:'soups',emoji:'🍜',title:'Борщ',time:'90 мин',servings:'6 порций',difficulty:'Средне',calories:'320 ккал',
desc:'Классический украинский борщ со сметаной',
tags:['суп','основное','украинская'],
ingredients:['Свёкла — 3 шт','Картофель — 3 шт','Морковь — 1 шт','Лук — 1 шт','Капуста — 300 г','Мясо на кости — 500 г','Томатная паста — 2 ст.л','Чеснок — 3 зубчика','Уксус — 1 ст.л','Сметана'],
steps:['Сварите бульон из мяса.','Натрите свёклу, обжарьте с уксусом.','Обжарьте лук и морковь.','Нарежьте картофель, добавьте в бульон.','Добавьте капусту, затем свёклу и зажарку.','Варите 20 минут, добавьте чеснок.','Подавайте со сметаной.']
},
{
id:10,category:'soups',emoji:'🍲',title:'Крем-суп из тыквы',time:'40 мин',servings:'4 порции',difficulty:'Легко',calories:'180 ккал',
desc:'Нежный крем-суп из тыквы с имбирём',
tags:['суп','полезно','вегетарианское'],
ingredients:['Тыква — 700 г','Лук — 1 шт','Картофель — 2 шт','Имбирь — 20 г','Сливки — 100 мл','Масло сливочное','Соль, перец, мускатный орех'],
steps:['Нарежьте тыкву и картофель.','Обжарьте лук с имбирём.','Добавьте овощи, залите водой.','Варите 25 минут до мягкости.','Пробейте блендером.','Добавьте сливки, специи.','Подавайте с тыквенными семечками.']
},
{
id:11,category:'desserts',emoji:'🍰',title:'Чизкейк Нью-Йорк',time:'60 мин',servings:'8 порций',difficulty:'Средне',calories:'420 ккал',
desc:'Классический нью-йоркский чизкейк с ягодным соусом',
tags:['десерт','выпечка','праздничное'],
ingredients:['Сливочный сыр — 800 г','Сахар — 200 г','Яйца — 4 шт','Сметана — 200 г','Ваниль','Печенье — 300 г','Масло сливочное — 100 г','Ягоды для соуса'],
steps:['Измельчите печенье, смешайте с маслом.','Утрамбуйте в форму, поставьте в холодильник.','Взбейте сыр с сахаром.','Добавляйте яйца по одному.','Добавьте сметану и ваниль.','Вылейте на основу.','Выпекайте при 160°C 50 минут.','Остудите в духовке с приоткрытой дверцей.']
},
{
id:12,category:'desserts',emoji:'🍪',title:'Шоколадное печенье',time:'25 мин',servings:'20 шт',difficulty:'Легко',calories:'150 ккал',
desc:'Мягкое шоколадное печенье с кусочками шоколада',
tags:['десерт','быстро','выпечка'],
ingredients:['Масло сливочное — 150 г','Сахар — 150 г','Яйца — 2 шт','Мука — 250 г','Какао — 30 г','Шоколад — 100 г','Сода — 0.5 ч.л','Ваниль'],
steps:['Размягчите масло, взбейте с сахаром.','Добавьте яйца.','Смешайте муку, какао, соду.','Соедините сухие и жидкие ингредиенты.','Добавьте кусочки шоколада.','Выложите ложкой на противень.','Выпекайте при 180°C 12 минут.']
},
{
id:13,category:'drinks',emoji:'🥤',title:'Смузи с бананом',time:'5 мин',servings:'1 порция',difficulty:'Легко',calories:'200 ккал',
desc:'Полезный и вкусный смузи с бананом и ягодами',
tags:['напиток','быстро','полезно'],
ingredients:['Банан — 1 шт','Замороженные ягоды — 150 г','Йогурт — 150 г','Мёд — 1 ч.л','Молоко — 100 мл'],
steps:['Очистите банан, нарежьте.','Сложите все ингредиенты в блендер.','Взбейте до однородности.','Перелейте в стакан.']
},
{
id:14,category:'snacks',emoji:'🥪',title:'Брускетта с авокадо',time:'10 мин',servings:'2 порции',difficulty:'Легко',calories:'280 ккал',
desc:'Хрустящая брускетта с авокадо и помидорами',
tags:['закуска','быстро','вегетарианское'],
ingredients:['Хлеб — 4 ломтика','Авокадо — 2 шт','Помидоры — 2 шт','Лимонный сок','Чеснок','Соль, перец','Масло оливковое'],
steps:['Подсушите хлеб на сковороде или в тостере.','Разомните авокадо с лимонным соком.','Нарежьте помидоры кубиками.','Натрите хлеб чесноком.','Намажьте авокадо, выложите помидоры.','Сбрызните маслом, посолите.']
},
{
id:15,category:'dinner',emoji:'🐟',title:'Лосось в духовке',time:'30 мин',servings:'2 порции',difficulty:'Легко',calories:'380 ккал',
desc:'Нежный лосось с лимоном и зеленью',
tags:['ужин','рыба','быстро'],
ingredients:['Стейк лосося — 400 г','Лимон — 1 шт','Чеснок — 3 зубчика','Укроп','Масло оливковое','Соль, перец'],
steps:['Разогрейте духовку до 200°C.','Выложите рыбу на фольгу.','Сбрызните маслом, положите чеснок и лимон.','Посолите, поперчите, посыпьте укропом.','Заверните фольгу.','Запекайте 20 минут.','Подавайте с овощами.']
},
{
id:16,category:'lunch',emoji:'🥘',title:'Гуляш',time:'50 мин',servings:'4 порции',difficulty:'Средне',calories:'420 ккал',
desc:'Ароматный гуляш с говядиной и овощами',
tags:['обед','мясо','основное'],
ingredients:['Говядина — 600 г','Лук — 2 шт','Морковь — 2 шт','Перец — 2 шт','Томатная паста — 2 ст.л','Паприка — 2 ч.л','Картофель — 3 шт','Соль, перец'],
steps:['Нарежьте мясо кубиками, обжарьте.','Добавьте лук и морковь.','Всыпьте паприку, добавьте томатную пасту.','Нарежьте картофель и перец.','Залите водой, тушите 40 минут.','Подавайте со сметаной.']
},
{
id:17,category:'salads',emoji:'🥗',title:'Греческий салат',time:'15 мин',servings:'2 порции',difficulty:'Легко',calories:'250 ккал',
desc:'Свежий греческий салат с фетой и оливками',
tags:['салат','быстро','вегетарианское'],
ingredients:['Помидоры — 3 шт','Огурцы — 2 шт','Перец — 1 шт','Лук красный — 1 шт','Фета — 150 г','Оливки — 100 г','Масло оливковое','Орегано'],
steps:['Нарежьте овощи крупными кусками.','Добавьте оливки и нарезанный лук.','Положите сверху кусок феты.','Сбрызните маслом, посыпьте орегано.']
},
{
id:18,category:'breakfast',emoji:'🥐',title:'Круассаны',time:'180 мин',servings:'12 шт',difficulty:'Сложно',calories:'280 ккал',
desc:'Домашние масляные круассаны',
tags:['завтрак','выпечка','французская'],
ingredients:['Мука — 500 г','Молоко — 250 мл','Дрожжи — 10 г','Сахар — 50 г','Соль — 8 г','Масло сливочное — 250 г','Яйца — 1 шт'],
steps:['Смешайте муку, дрожжи, сахар, соль.','Добавьте молоко, замесите тесто.','Охладите тесто 1 час.','Раскатайте, положите масло, сложите конвертом.','Раскатайте и сложите 3 раза с охлаждением.','Сформируйте круассаны.','Дайте подняться 1 час.','Выпекайте при 200°C 15 минут.']
},
{
id:19,category:'desserts',emoji:'🍮',title:'Тирамису',time:'30 мин',servings:'6 порций',difficulty:'Средне',calories:'380 ккал',
desc:'Классический итальянский десерт с маскарпоне',
tags:['десерт','итальянская','без выпечки'],
ingredients:['Маскарпоне — 500 г','Яйца — 4 шт','Сахар — 100 г','Савоярди — 200 г','Кофе эспрессо — 300 мл','Какао','Амаретто (по желанию)'],
steps:['Взбейте желтки с сахаром.','Добавьте маскарпоне.','Взбейте белки в крепкую пену.','Аккуратно введите белки.','Обмакните печенье в кофе.','Выложите слоями: печенье, крем.','Посыпьте какао.','Охладите 4 часа.']
},
{
id:20,category:'snacks',emoji:'🧆',title:'Фалафель',time:'40 мин',servings:'4 порции',difficulty:'Средне',calories:'300 ккал',
desc:'Хрустящий фалафель из нута с соусом тахини',
tags:['закуска','вегетарианское','ближневосточная'],
ingredients:['Нут (замоченный) — 400 г','Лук — 1 шт','Чеснок — 4 зубчика','Петрушка — 1 пучок','Киндра — 1 пучок','Кумин, кориандр','Мука — 3 ст.л','Масло для фритюра'],
steps:['Промойте нут, измельчите в блендере.','Добавьте лук, чеснок, зелень, специи.','Добавьте муку, перемешайте.','Сформируйте шарики.','Обжарьте во фритюре 3-4 минуты.','Подавайте с соусом тахини.']
},
];
let currentCategory = 'all';
let currentSearch = '';
function getFavorites() {
try { return JSON.parse(localStorage.getItem('recipe-favorites') || '[]'); } catch { return []; }
}
function toggleFavorite(id, event) {
if (event) event.stopPropagation();
const favs = getFavorites();
const idx = favs.indexOf(id);
if (idx > -1) { favs.splice(idx, 1); showToast('Удалено из избранного', 'info'); }
else { favs.push(id); showToast('Добавлено в избранное!', 'success'); }
localStorage.setItem('recipe-favorites', JSON.stringify(favs));
renderCurrentPage();
}
function getShoppingList() {
try { return JSON.parse(localStorage.getItem('recipe-shopping') || '[]'); } catch { return []; }
}
function saveShoppingList(list) {
localStorage.setItem('recipe-shopping', JSON.stringify(list));
}
function addShoppingItem() {
const input = document.getElementById('shoppingInput');
const text = input.value.trim();
if (!text) return;
const list = getShoppingList();
list.push({ id: Date.now(), text, checked: false });
saveShoppingList(list);
input.value = '';
renderShoppingList();
}
function toggleShoppingItem(id) {
const list = getShoppingList();
const item = list.find(i => i.id === id);
if (item) { item.checked = !item.checked; saveShoppingList(list); renderShoppingList(); }
}
function removeShoppingItem(id) {
const list = getShoppingList().filter(i => i.id !== id);
saveShoppingList(list);
renderShoppingList();
}
function clearChecked() {
const list = getShoppingList().filter(i => !i.checked);
saveShoppingList(list);
renderShoppingList();
showToast('Купленные удалены', 'info');
}
function clearAll() {
saveShoppingList([]);
renderShoppingList();
showToast('Список очищен', 'info');
}
function addToShoppingList(ingredients) {
const list = getShoppingList();
ingredients.forEach(ing => {
if (!list.find(i => i.text === ing)) {
list.push({ id: Date.now() + Math.random(), text: ing, checked: false });
}
});
saveShoppingList(list);
showToast('Ингредиенты добавлены в список!', 'success');
}
function showPage(page) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById('page-' + page).classList.add('active');
window.scrollTo(0, 0);
if (page === 'home') renderHome();
if (page === 'recipes') renderRecipes();
if (page === 'favorites') renderFavorites();
if (page === 'shopping') renderShoppingList();
}
function renderCurrentPage() {
const activePage = document.querySelector('.page.active');
if (activePage.id === 'page-home') renderHome();
if (activePage.id === 'page-recipes') renderRecipes();
if (activePage.id === 'page-favorites') renderFavorites();
}
function toggleMobile() { document.getElementById('mobileMenu').classList.toggle('active'); }
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-theme');
const next = current === 'light' ? 'dark' : 'light';
html.setAttribute('data-theme', next);
localStorage.setItem('recipe-theme', next);
document.querySelector('.theme-toggle').textContent = next === 'dark' ? '☀️' : '🌙';
}
function renderHome() {
const categoriesGrid = document.getElementById('categoriesGrid');
const popularGrid = document.getElementById('popularGrid');
categoriesGrid.innerHTML = categoriesData.map(c => `
<div class="category-card" onclick="filterCategory('${c.id}');showPage('recipes')">
<div class="emoji">${c.emoji}</div>
<h4>${c.name}</h4>
<p>${c.count} рецептов</p>
</div>
`).join('');
const popular = recipesData.slice(0, 6);
popularGrid.innerHTML = popular.map(r => renderRecipeCard(r)).join('');
}
function renderRecipeCard(recipe) {
const favs = getFavorites();
const isFav = favs.includes(recipe.id);
return `
<div class="recipe-card-wrap">
<div class="recipe-card" onclick="showRecipe(${recipe.id})">
<div class="recipe-image">${recipe.emoji}</div>
<div class="recipe-body">
<div class="recipe-meta">
<span>⏱ ${recipe.time}</span>
<span>👥 ${recipe.servings}</span>
<span>🔥 ${recipe.calories}</span>
</div>
<h3>${recipe.title}</h3>
<div class="desc">${recipe.desc}</div>
<div class="recipe-tags">${recipe.tags.map(t => `<span class="recipe-tag">${t}</span>`).join('')}</div>
</div>
</div>
<button class="recipe-fav ${isFav ? 'active' : ''}" onclick="toggleFavorite(${recipe.id}, event)">${isFav ? '❤️' : '🤍'}</button>
</div>
`;
}
function renderRecipes() {
const grid = document.getElementById('allRecipesGrid');
const filters = document.getElementById('categoryFilters');
filters.innerHTML = `
<button class="btn btn-sm ${currentCategory === 'all' ? 'btn-primary' : 'btn-secondary'}" onclick="filterCategory('all')">Все</button>
${categoriesData.map(c => `<button class="btn btn-sm ${currentCategory === c.id ? 'btn-primary' : 'btn-secondary'}" onclick="filterCategory('${c.id}')">${c.emoji} ${c.name}</button>`).join('')}
`;
let recipes = recipesData;
if (currentCategory !== 'all') recipes = recipes.filter(r => r.category === currentCategory);
if (currentSearch) {
const search = currentSearch.toLowerCase();
recipes = recipes.filter(r => r.title.toLowerCase().includes(search) || r.desc.toLowerCase().includes(search) || r.tags.some(t => t.includes(search)));
}
if (recipes.length === 0) {
grid.innerHTML = '<div class="empty-state"><div class="emoji">🔍</div><h3>Ничего не найдено</h3><p>Попробуйте другой запрос</p></div>';
} else {
grid.innerHTML = recipes.map(r => renderRecipeCard(r)).join('');
}
}
function filterCategory(cat) {
currentCategory = cat;
renderRecipes();
}
function searchRecipes() {
currentSearch = document.querySelector('.search-input').value;
renderRecipes();
}
function showRecipe(id) {
const recipe = recipesData.find(r => r.id === id);
if (!recipe) return;
const detail = document.getElementById('recipeDetail');
const favs = getFavorites();
const isFav = favs.includes(recipe.id);
detail.innerHTML = `
<div class="recipe-detail-header">
<div style="font-size:64px;margin-bottom:16px">${recipe.emoji}</div>
<h2>${recipe.title}</h2>
<p>${recipe.desc}</p>
</div>
<div class="recipe-detail-body">
<div class="recipe-info-grid">
<div class="recipe-info-item"><div class="icon">⏱</div><div class="value">${recipe.time}</div><div class="label">Время</div></div>
<div class="recipe-info-item"><div class="icon">👥</div><div class="value">${recipe.servings}</div><div class="label">Порции</div></div>
<div class="recipe-info-item"><div class="icon">📊</div><div class="value">${recipe.difficulty}</div><div class="label">Сложность</div></div>
<div class="recipe-info-item"><div class="icon">🔥</div><div class="value">${recipe.calories}</div><div class="label">Калории</div></div>
</div>
<h3 style="margin-bottom:16px">Ингредиенты</h3>
<ul class="ingredients-list">${recipe.ingredients.map(i => `<li>${i}</li>`).join('')}</ul>
<h3 style="margin-bottom:16px">Приготовление</h3>
<ol class="steps-list">${recipe.steps.map(s => `<li>${s}</li>`).join('')}</ol>
<div class="recipe-actions">
<button class="btn btn-primary" onclick="addToShoppingList(${JSON.stringify(recipe.ingredients).replace(/"/g, '&quot;')})">🛒 Добавить в список покупок</button>
<button class="btn ${isFav ? 'btn-secondary' : 'btn-primary'}" onclick="toggleFavorite(${recipe.id})">${isFav ? '❤️ В избранном' : '🤍 В избранное'}</button>
</div>
</div>
`;
showPage('detail');
}
function renderFavorites() {
const content = document.getElementById('favoritesContent');
const favs = getFavorites();
const recipes = recipesData.filter(r => favs.includes(r.id));
if (recipes.length === 0) {
content.innerHTML = '<div class="empty-state"><div class="emoji">❤️</div><h3>Нет избранных рецептов</h3><p>Нажмите на сердечко на рецепте, чтобы добавить</p></div>';
} else {
content.innerHTML = `<div class="favorites-grid">${recipes.map(r => renderRecipeCard(r)).join('')}</div>`;
}
}
function renderShoppingList() {
const list = getShoppingList();
const container = document.getElementById('shoppingList');
if (list.length === 0) {
container.innerHTML = '<div class="empty-state"><div class="emoji">🛒</div><h3>Список пуст</h3><p>Добавьте продукты из рецептов или вручную</p></div>';
} else {
container.innerHTML = list.map(item => `
<div class="shopping-item">
<div class="shopping-checkbox ${item.checked ? 'checked' : ''}" onclick="toggleShoppingItem(${item.id})">${item.checked ? '✓' : ''}</div>
<span>${item.text}</span>
<button class="remove-btn" onclick="removeShoppingItem(${item.id})">✕</button>
</div>
`).join('');
}
}
function showToast(message, type = 'info') {
const container = document.getElementById('toastContainer');
const toast = document.createElement('div');
toast.className = 'toast ' + type;
toast.innerHTML = `<span>${message}</span>`;
container.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
function init() {
const theme = localStorage.getItem('recipe-theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
document.querySelector('.theme-toggle').textContent = theme === 'dark' ? '☀️' : '🌙';
renderHome();
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => { if (entry.isIntersecting) entry.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));
}
init();
</script>
</body>
</html>