sektamarketingcourse-files/bootcamp2026/bootcamp_nedelya1_2w.html
Blamebootcamp_nedelya1_2w.html2925 lines
<!doctype html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Секта Буткемп — неделя 2: продукты, заготовки, рецепты</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Oswald:wght@500;600;700&family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"> <style> :root { --bg-page-1: #d8f0f6; --bg-page-2: #bfe8f2; --bg-page-3: #eaf7fb; --bg-panel: #ffffff; --bg-card: #ffffff; --bg-soft: #eef8fb; --bg-yellow: #f5e8a8; --bg-yellow-soft: #faf3c8; --text-main: #1c2333; --text-muted: #5a6a75; --line: #d5e8ee; --line-strong: #b5d4de; --accent: #1a9db0; --accent-soft: #2bb8cc; --accent-2: #e85d6f; --accent-2-soft: #ffb3bd; --ok: #2bb673; --warn: #f0dc7a; --radius-xl: 28px; --radius-lg: 18px; --font-display: "Oswald", "Inter", "Segoe UI", Roboto, Arial, sans-serif; --font-body: "Inter", "Segoe UI", Roboto, Arial, sans-serif; --shadow-card: 0 12px 30px rgba(26, 157, 176, 0.12); --shadow-soft: 0 6px 18px rgba(26, 157, 176, 0.08); } * { box-sizing: border-box; min-width: 0; } html, body { max-width: 100%; overflow-x: hidden; } body { margin: 0; font-family: var(--font-body); color: var(--text-main); background: radial-gradient(80% 60% at 50% 0%, var(--bg-page-2) 0%, transparent 70%), linear-gradient(180deg, var(--bg-page-1) 0%, var(--bg-page-3) 100%); min-height: 100vh; -webkit-text-size-adjust: 100%; word-wrap: break-word; overflow-wrap: break-word; } .page { width: min(1180px, 94vw); margin: 0 auto; padding: 20px 0 calc(92px + env(safe-area-inset-bottom, 0)); } @media (max-width: 560px) { .page { width: min(1180px, 92vw); padding-top: 14px; } } .app-screen { display: none; animation: screenIn 0.22s ease; } .app-screen.is-active { display: block; } @keyframes screenIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .subscreen-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; } .subscreen-head h2 { margin: 0; font-family: var(--font-display); font-size: clamp(22px, 6.5vw, 42px); font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent); line-height: 1; flex: 1; min-width: 0; } .link-back { border: 0; background: transparent; color: var(--text-muted); font-weight: 600; font-size: 14px; cursor: pointer; padding: 6px 0; } .link-back:hover { color: var(--text-main); } /* ——— Главная: карточки как в приложении ——— */ .dash-cards { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; } @media (min-width: 720px) { .dash-cards { display: grid; grid-template-columns: repeat(3, 1fr); } } .dash-card { display: flex; flex-direction: column; align-items: flex-start; width: 100%; text-align: left; border: 1px solid var(--line); border-radius: var(--radius-xl); padding: 16px 18px 14px; cursor: pointer; position: relative; overflow: hidden; transition: transform 0.15s ease, box-shadow 0.15s ease; color: var(--text-main); font-family: inherit; background: var(--bg-card); box-shadow: var(--shadow-card); min-height: 108px; } .dash-card:hover { transform: translateY(-2px); box-shadow: 0 18px 36px rgba(26, 157, 176, 0.16); } .dash-card__idx { position: absolute; top: 16px; right: 18px; font-family: var(--font-display); font-size: 14px; font-weight: 600; color: var(--text-muted); letter-spacing: 0.04em; } .dash-card h3 { margin: 0; } .dash-card__title { margin: 0; font-family: var(--font-display); font-size: clamp(18px, 2.4vw, 22px); font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; line-height: 1; } .dash-card__meta { margin: 8px 0 0; font-size: 13px; font-weight: 500; color: var(--text-muted); line-height: 1.4; margin-top: auto; padding-top: 8px; } .dash-card__accent { width: 36px; height: 5px; border-radius: 999px; background: var(--accent); margin-bottom: 10px; } .dash-card--shop { background: linear-gradient(160deg, var(--bg-yellow-soft) 0%, #ffffff 78%); } .dash-card--shop .dash-card__accent { background: var(--accent); } .dash-card--prep { background: linear-gradient(160deg, var(--bg-yellow) 0%, #ffffff 78%); } .dash-card--prep .dash-card__accent { background: var(--accent-2); } .dash-card--recipes { background: linear-gradient(160deg, #ffe8ec 0%, #ffffff 78%); } .dash-card--recipes .dash-card__accent { background: var(--accent-2); } .home-days-section { margin-top: 22px; } .home-days-section .section-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 12px; } .home-days-section h3 { margin: 0; font-family: var(--font-display); font-size: clamp(20px, 3vw, 26px); font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent); } .day-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } @media (min-width: 560px) { .day-grid { grid-template-columns: repeat(3, 1fr); } } @media (min-width: 900px) { .day-grid { grid-template-columns: repeat(4, 1fr); } } .day-tile { border: 1px solid var(--line); border-radius: 22px; padding: 18px 16px 16px; cursor: pointer; text-align: left; font-family: inherit; color: var(--text-main); background: var(--bg-card); transition: 0.15s ease; box-shadow: var(--shadow-soft); } .day-tile:hover { border-color: var(--line-strong); transform: translateY(-2px); box-shadow: var(--shadow-card); } .day-tile__num { font-family: var(--font-display); font-size: 36px; font-weight: 700; font-style: italic; line-height: 1; color: var(--accent); } .day-tile__num small { font-family: var(--font-display); font-size: 12px; font-weight: 600; font-style: italic; text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent-2); margin-left: 4px; vertical-align: super; } .day-tile__status { margin-top: 14px; font-size: 12px; font-weight: 600; color: var(--text-muted); } .day-tile--a .day-tile__num { color: var(--accent); } .day-tile--a .day-tile__num small { color: var(--accent-2); } .day-tile--b .day-tile__num { color: var(--accent-2); } .day-tile--b .day-tile__num small { color: var(--accent); } .day-tile--c .day-tile__num { color: var(--accent); } .day-tile--c .day-tile__num small { color: var(--accent-2); } /* Лента «все рецепты» — светлые карточки */ .recipe-feed-card { border-radius: 22px; border: 1px solid var(--line); background: var(--bg-card); margin-bottom: 12px; overflow: hidden; box-shadow: var(--shadow-soft); } .recipe-feed-card.meal-breakfast { border-left: 4px solid var(--warn); } .recipe-feed-card.meal-lunch { border-left: 4px solid var(--accent); } .recipe-feed-card.meal-dinner { border-left: 4px solid var(--accent-2); } .recipe-feed-card__row { display: flex; align-items: flex-start; gap: 14px; padding: 16px 18px 14px; } .recipe-feed-card__icon { flex-shrink: 0; width: 46px; height: 46px; border-radius: 50%; background: var(--bg-soft); border: 1px solid var(--line); display: grid; place-items: center; font-size: 20px; color: var(--text-muted); } .recipe-feed-card__body { flex: 1; min-width: 0; } .recipe-feed-card__meta { font-family: var(--font-display); font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-2); margin-bottom: 4px; } .recipe-feed-card__title { font-family: var(--font-display); font-size: 18px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; line-height: 1.05; color: var(--accent); margin: 0 0 8px; } .recipe-feed-card__time { font-size: 13px; color: var(--text-muted); font-weight: 500; } .recipe-feed-done { flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; border: 2px solid var(--line-strong); background: var(--bg-soft); cursor: pointer; margin-top: 4px; padding: 0; font-family: inherit; } .recipe-feed-done.is-done { border-color: var(--ok); background: rgba(43, 182, 115, 0.15); } .recipe-feed-done.is-done::after { content: ""; display: block; width: 6px; height: 11px; border: solid var(--ok); border-width: 0 2px 2px 0; transform: rotate(45deg); margin: 2px 0 0 9px; } .recipe-feed-card details { border-top: 1px solid var(--line); padding: 0 18px 16px; } .recipe-feed-card summary { cursor: pointer; font-size: 13px; font-weight: 700; color: var(--accent-2); padding: 12px 0 4px; list-style: none; } .recipe-feed-card summary::-webkit-details-marker { display: none; } .recipe-feed-card .meal-detail-inner { padding-top: 8px; } .recipe-feed-card[data-hidden-filter="1"] { display: none; } /* Нижняя навигация */ .bottom-nav { position: fixed; left: 0; right: 0; bottom: 0; width: 100%; z-index: 100; display: flex; justify-content: space-around; align-items: stretch; gap: 4px; padding: 10px 8px calc(10px + env(safe-area-inset-bottom, 0)); background: rgba(255, 255, 255, 0.92); border-top: 1px solid var(--line); backdrop-filter: blur(14px); box-shadow: 0 -8px 24px rgba(38, 78, 130, 0.06); } .bottom-nav__btn { flex: 1; max-width: 120px; border: 0; background: transparent; color: var(--text-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; cursor: pointer; padding: 6px 4px; display: flex; flex-direction: column; align-items: center; gap: 4px; font-family: inherit; border-radius: 14px; transition: 0.15s ease; } .bottom-nav__btn:hover { color: var(--text-main); } .bottom-nav__btn.is-active { color: var(--accent-2); } .bottom-nav__icon-wrap { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font-size: 18px; line-height: 1; transition: 0.15s ease; } .bottom-nav__btn.is-active .bottom-nav__icon-wrap { background: rgba(232, 93, 111, 0.14); color: var(--accent-2); } .bottom-nav__btn:not(.is-active) .bottom-nav__icon-wrap { background: var(--bg-soft); } .hero { background: transparent; padding: 4px 0 12px; } h1 { margin: 0; font-family: var(--font-display); font-size: clamp(30px, 8vw, 68px); font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; line-height: 0.92; color: var(--accent); } h1 .h1-sub { display: block; color: var(--accent-2); font-size: 0.55em; letter-spacing: 0.04em; margin-top: 4px; } .hero p { margin: 14px 0 0; max-width: 56ch; color: var(--text-muted); font-size: clamp(14px, 1.35vw, 16px); line-height: 1.5; font-weight: 500; } .hero-kicker { margin: 0 0 6px; font-family: var(--font-display); font-size: 14px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-2); } .panel-wrap { margin-top: 20px; background: var(--bg-panel); border-radius: var(--radius-xl); border: 1px solid var(--line); box-shadow: var(--shadow-card); overflow: hidden; } .toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--line); } .toolbar h2 { margin: 0; font-family: var(--font-display); font-size: clamp(20px, 4.5vw, 30px); font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent); line-height: 1; } .muted { color: var(--text-muted); font-size: 14px; line-height: 1.45; margin: 0; } .btn { border: 1px solid var(--line); background: var(--bg-soft); color: var(--text-main); font-weight: 600; border-radius: 999px; padding: 9px 16px; cursor: pointer; font-size: 13px; transition: 0.15s ease; font-family: inherit; } .btn:hover { background: #ffffff; border-color: var(--line-strong); } /* ——— Продукты: горизонтальные категории ——— */ .cat-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 8px; margin-bottom: 14px; scrollbar-width: thin; } .cat-strip button { flex: 0 0 auto; border-radius: 999px; border: 1px solid var(--line); background: var(--bg-soft); color: var(--text-main); font-weight: 600; font-size: 14px; padding: 10px 18px; cursor: pointer; white-space: nowrap; transition: 0.15s ease; font-family: inherit; } .cat-strip button:hover { border-color: var(--line-strong); } .cat-strip button[aria-selected="true"] { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 6px 18px rgba(26, 157, 176, 0.28); } .portion-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; padding: 12px 14px; background: linear-gradient(160deg, var(--bg-yellow-soft) 0%, #ffffff 75%); border: 1px solid var(--line); border-radius: 16px; } .portion-bar__label { font-family: var(--font-display); font-size: 13px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent-2); } .portion-bar__btns { display: flex; gap: 6px; } .portion-bar__btns button { border: 1px solid var(--line); background: #ffffff; color: var(--text-main); font-weight: 700; border-radius: 999px; padding: 7px 14px; cursor: pointer; font-size: 13px; transition: 0.15s ease; font-family: inherit; min-width: 44px; } .portion-bar__btns button:hover { border-color: var(--line-strong); } .portion-bar__btns button[aria-selected="true"] { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 6px 16px rgba(26, 157, 176, 0.28); } .portion-bar__hint { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text-muted); line-height: 1.35; } @media (max-width: 560px) { .portion-bar { padding: 10px 12px; gap: 10px; } .portion-bar__hint { flex-basis: 100%; font-size: 12px; } .portion-bar__btns button { padding: 6px 12px; font-size: 12.5px; min-width: 40px; } } .cat-panel { display: none; background: var(--bg-soft); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 16px 16px 12px; } .cat-panel.active { display: block; } .cat-head { margin-bottom: 12px; } .cat-head strong { font-family: var(--font-display); font-size: 20px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent-2); } .prep-subpanel { display: none; background: transparent; border: 0; border-radius: 0; padding: 0; margin-top: 4px; } .prep-subpanel.active { display: block; } .prep-subpanel .prep-intro { margin: 0 0 12px; max-width: 70ch; } #panel-preps .toolbar h2 { color: var(--accent-2); } #prepStrip button { background: #ffffff; border-color: var(--line); } #prepStrip button[aria-selected="true"] { background: var(--accent-2); border-color: var(--accent-2); box-shadow: 0 6px 18px rgba(232, 93, 111, 0.28); } .recipe-day-panel { display: none; margin-top: 12px; border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; background: var(--bg-card); box-shadow: var(--shadow-soft); } .recipe-day-panel.active { display: block; } .items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; } .item { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--line); background: var(--bg-card); } .item input { appearance: none; width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--line-strong); margin-top: 2px; cursor: pointer; flex-shrink: 0; background: #ffffff; } .item input:checked { border-color: var(--ok); background: rgba(43, 182, 115, 0.15); } .item input:checked::after { content: ""; display: block; width: 5px; height: 10px; border: solid var(--ok); border-width: 0 2px 2px 0; transform: rotate(45deg); margin: 2px 0 0 6px; } .item label { cursor: pointer; font-size: 15px; line-height: 1.4; flex: 1; font-weight: 500; color: var(--text-main); } .item.checked label { text-decoration: line-through; color: var(--text-muted); } .tip-box { margin-top: 14px; padding: 14px 16px; border-radius: var(--radius-lg); border: 1px dashed var(--line-strong); background: var(--bg-soft); font-size: 14px; color: var(--text-muted); line-height: 1.5; } /* ——— Заготовки ——— */ .prep-card { scroll-margin-top: 88px; background: var(--bg-yellow-soft); border: 1px solid #e8d98a; border-radius: var(--radius-lg); padding: 16px 18px; margin-bottom: 10px; box-shadow: var(--shadow-soft); } .prep-card h4 { margin: 0 0 10px; font-family: var(--font-display); font-size: 18px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent-2); display: flex; align-items: center; gap: 10px; line-height: 1.05; } .prep-card h4 label { flex: 1; line-height: 1.05; } .prep-card .prep-body { margin: 0; color: var(--text-main); font-size: 14px; line-height: 1.55; white-space: pre-wrap; } .book1 { border-left: 4px solid var(--accent-2); } .book1 h4 { color: var(--accent-2); } .book1-intro { margin-bottom: 12px; background: var(--bg-yellow); } .book1-intro .prep-body { margin: 0; } .week2-freeze-hint { margin: 14px 0 0; padding: 10px 12px; background: var(--bg-soft); border-radius: 12px; font-size: 13px; line-height: 1.45; color: var(--text-main); } /* ——— Рецепты ——— */ .week-bar { height: 8px; border-radius: 999px; background: var(--bg-soft); border: 1px solid var(--line); overflow: hidden; margin-bottom: 14px; } .week-bar > i { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent), var(--accent-soft)); transition: width 0.25s ease; } .day-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 18px; background: var(--bg-soft); border-bottom: 1px solid var(--line); } .day-head strong { font-family: var(--font-display); font-size: 22px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent); line-height: 1; } .day-progress { font-size: 13px; font-weight: 600; color: var(--text-muted); } .day-progress span { color: var(--accent-2); font-weight: 700; } .meal { scroll-margin-top: 96px; padding: 16px 18px; border-top: 1px solid var(--line); } .meal:first-of-type { border-top: 0; } .meal-title { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; } .meal-title h4 { margin: 0; font-family: var(--font-display); font-size: 17px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.005em; color: var(--accent); flex: 1; line-height: 1.1; } .meal-title input[type="checkbox"], .prep-card h4 input[type="checkbox"] { appearance: none; width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--line-strong); cursor: pointer; flex-shrink: 0; background: #ffffff; margin: 0; } .meal-title input[type="checkbox"]:checked, .prep-card h4 input[type="checkbox"]:checked { border-color: var(--ok); background: rgba(43, 182, 115, 0.15); } .meal-title input[type="checkbox"]:checked::after, .prep-card h4 input[type="checkbox"]:checked::after { content: ""; display: block; width: 5px; height: 10px; border: solid var(--ok); border-width: 0 2px 2px 0; transform: rotate(45deg); margin: 2px 0 0 6px; } .time-badge { font-family: var(--font-display); font-size: 13px; font-weight: 600; color: var(--accent-2); white-space: nowrap; letter-spacing: 0.02em; } .meal h5, .recipe-feed-card .meal-detail-inner h5 { margin: 12px 0 6px; font-family: var(--font-display); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent-2); } .recipe-feed-card .meal-detail-inner ul, .recipe-feed-card .meal-detail-inner ol { margin: 0; padding-left: 20px; color: var(--text-main); font-size: 14px; line-height: 1.55; } .recipe-feed-card .meal-detail-inner li { margin-bottom: 4px; } .meal ul, .meal ol { margin: 0; padding-left: 20px; color: var(--text-main); font-size: 14px; line-height: 1.55; } .meal ul li, .meal ol li { margin-bottom: 4px; } .meal-steps { counter-reset: step; list-style: none; padding-left: 0; } .meal-steps li { position: relative; padding-left: 28px; margin-bottom: 6px; counter-increment: step; } .meal-steps li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 20px; height: 20px; border-radius: 50%; background: var(--accent); color: #fff; font-family: var(--font-display); font-weight: 600; font-size: 12px; display: grid; place-items: center; line-height: 1; } .prep-link { color: var(--accent-2); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; } .prep-link:hover { color: var(--accent); } .book-link { color: var(--accent); } /* Блок «Наперёд» — отдельной плашкой после ужина */ .day-ahead { padding: 16px 18px; border-top: 1px solid var(--line); background: linear-gradient(160deg, var(--bg-yellow-soft) 0%, #ffffff 75%); border-left: 4px solid var(--accent-2); } .day-ahead h5 { margin: 0 0 8px; font-family: var(--font-display); font-size: 14px; font-weight: 700; font-style: italic; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent); } .day-ahead p { margin: 0 0 8px; color: var(--text-main); font-size: 14px; line-height: 1.55; } .day-ahead p:last-child { margin-bottom: 0; } /* Назад к рецепту */ #backToRecipe { position: fixed; bottom: calc(76px + env(safe-area-inset-bottom, 0)); left: 50%; transform: translateX(-50%); z-index: 50; display: none; align-items: center; gap: 8px; padding: 12px 22px; border-radius: 999px; border: 0; background: var(--accent-2); color: #fff; font-family: var(--font-display); font-weight: 600; font-size: 14px; text-transform: uppercase; letter-spacing: 0.04em; cursor: pointer; box-shadow: 0 14px 30px rgba(232, 93, 111, 0.35); } #backToRecipe.visible { display: flex; } /* Дополнительные мобильные оптимизации */ @media (max-width: 560px) { .panel-wrap { border-radius: 22px; } .panel-wrap > section, .panel-wrap > div { padding: 16px 14px 18px !important; } .toolbar { gap: 10px; margin-bottom: 12px; padding-bottom: 12px; } .toolbar > div { min-width: 0; flex: 1; } .cat-strip button { padding: 9px 14px; font-size: 13px; } .cat-panel, .prep-subpanel { padding: 14px 12px 10px; } .item { padding: 11px 12px; gap: 10px; } .item label { font-size: 14px; } .prep-card { padding: 14px 14px; } .prep-card h4 { font-size: 16px; } .recipe-feed-card__row { padding: 14px 14px 12px; gap: 10px; } .recipe-feed-card__icon { width: 40px; height: 40px; font-size: 18px; } .recipe-feed-card details { padding: 0 14px 14px; } .recipe-feed-card__title { font-size: 16px; } .meal { padding: 14px 14px; } .day-ahead { padding: 14px 14px; } .day-head { padding: 12px 14px; gap: 8px; } .day-head strong { font-size: 18px; } .meal-title h4, .meal-title h4 * { font-size: 15px; } .meal-title { flex-wrap: wrap; } .meal-title > .time-badge { flex-basis: 100%; order: 3; margin-left: 34px; } .meal ul, .meal ol { padding-left: 18px; font-size: 13.5px; } .meal-steps li { padding-left: 24px; } .meal-steps li::before { width: 18px; height: 18px; font-size: 11px; } .home-days-section, .home-days-section[style] { padding: 16px 14px 18px !important; } .home-days-section h3 { font-size: 20px; } .home-days-section .section-row { gap: 8px; margin-bottom: 10px; } .home-days-section .section-row .muted { font-size: 13px; } .dash-card { padding: 14px 16px 12px; min-height: 96px; } .dash-card__title { font-size: 18px; } .day-tile { padding: 14px 12px 12px; } .day-tile__num { font-size: 30px; } .day-tile__status { margin-top: 10px; } #backToRecipe { bottom: calc(82px + env(safe-area-inset-bottom, 0)); font-size: 12px; padding: 10px 18px; } .bottom-nav__btn { font-size: 10px; } .bottom-nav__icon-wrap { width: 36px; height: 36px; } .hero { padding: 4px 0 8px; } } @media (max-width: 380px) { .toolbar h2 { font-size: 18px; } .recipe-feed-card__title { font-size: 15px; } .day-tile__num { font-size: 26px; } } </style> </head> <body> <main class="page"> <div id="screen-home" class="app-screen is-active"> <header class="hero"> <p class="hero-kicker">Неделя 1</p> <h1>Книга заготовок<span class="h1-sub">и рецептов</span></h1> <p>Список покупок, заготовки воскресенья и меню дней 1–7. Отметки сохраняются на этом устройстве.</p> </header> <div class="dash-cards"> <button type="button" class="dash-card dash-card--shop" data-open="shop"> <span class="dash-card__idx">01</span> <div class="dash-card__accent"></div> <h3 class="dash-card__title">Список покупок</h3> <p class="dash-card__meta" id="dashShopLine">0 / 0 куплено</p> </button> <button type="button" class="dash-card dash-card--prep" data-open="preps"> <span class="dash-card__idx">02</span> <div class="dash-card__accent"></div> <h3 class="dash-card__title">Заготовки</h3> <p class="dash-card__meta" id="dashPrepLine">0 / 0 шагов · воскресенье</p> </button> <button type="button" class="dash-card dash-card--recipes" data-open="recipes-all"> <span class="dash-card__idx">03</span> <div class="dash-card__accent"></div> <h3 class="dash-card__title">Все рецепты</h3> <p class="dash-card__meta" id="dashRecipesLine">21 блюдо</p> </button> </div> <div class="panel-wrap" style="margin-top: 18px;"> <section class="home-days-section" style="padding: 18px 18px 22px;"> <div class="section-row"> <h3>Дни недели</h3> </div> <div id="homeDaysGrid" class="day-grid"></div> </section> </div> </div> <div id="screen-shop" class="app-screen"> <div class="subscreen-head"> <button type="button" class="link-back" data-open="home">← Назад</button> <h2>Покупки</h2> </div> <div class="panel-wrap"> <!-- ПРОДУКТЫ --> <section id="panel-products" style="padding: 18px 18px 22px;" aria-label="Список продуктов"> <div class="toolbar"> <div> <h2>Список на неделю</h2> <p class="muted">Выбери категорию — отмечай купленное.</p> </div> <div style="display:flex;gap:8px;flex-wrap:wrap;"> <button type="button" class="btn" id="prodReset">Сбросить всё</button> <button type="button" class="btn" id="prodAll">Отметить всё</button> </div> </div> <p class="muted" style="margin:0 0 12px;"><strong id="prodGlobal">0%</strong> всего списка</p> <div class="portion-bar" aria-label="Множитель порций"> <span class="portion-bar__label">Готовлю на</span> <div class="portion-bar__btns" id="portionStrip" role="tablist"> <button type="button" data-mult="1" aria-selected="true">×1</button> <button type="button" data-mult="2" aria-selected="false">×2</button> <button type="button" data-mult="3" aria-selected="false">×3</button> <button type="button" data-mult="4" aria-selected="false">×4</button> </div> <span class="portion-bar__hint">количество ингредиентов в списке умножится</span> </div> <div class="cat-strip" id="catStrip" role="tablist"></div> <div id="catPanels"></div> </section> </div> </div> <div id="screen-preps" class="app-screen"> <div class="subscreen-head"> <button type="button" class="link-back" data-open="home">← Назад</button> <h2>Заготовки</h2> </div> <div class="panel-wrap"> <!-- ЗАГОТОВКИ --> <section id="panel-preps" style="padding: 18px 18px 22px;" aria-label="Заготовки"> <div class="toolbar"> <div> <h2>Воскресенье: заготовки</h2> <p class="muted"><strong id="prepProgress">0 из 0</strong> шагов отмечено</p> </div> <button type="button" class="btn" id="prepReset">Сбросить</button> </div> <div class="cat-strip" id="prepStrip" role="tablist" aria-label="Этапы заготовок"> <button type="button" data-prep-tab="stage1" aria-selected="true">Крупы</button> <button type="button" data-prep-tab="stage2" aria-selected="false">Духовка и плита</button> <button type="button" data-prep-tab="stage3" aria-selected="false">Суп-пюре</button> </div> <div class="prep-subpanel active" data-prep-panel="stage1"> <p class="muted prep-intro">Ставим вариться крупы. Поставить на плиту кастрюлю с водой для круп. Варить всё последовательно в одной кастрюле, промывая между варками. Можно также варить параллельно 2–3 крупы, если хватает ёмкостей для варки.</p> <div id="prepsStage1"></div> </div> <div class="prep-subpanel" data-prep-panel="stage2"> <p class="muted prep-intro">Параллельно с варкой круп готовим мясо, рыбу, овощи и базу под завтрак.<br><br><strong>Духовка:</strong> поставить духовку разогреваться до 180 градусов.</p> <div id="prepsStage2"></div> </div> <div class="prep-subpanel" data-prep-panel="stage3"> <p class="muted prep-intro">Варим суп-пюре из брокколи. После заготовок круп, мяса и овощей варим финальный шаг — крем-суп.</p> <div id="prepsStage3"></div> </div> </section> </div> </div> <div id="screen-days" class="app-screen"> <div class="subscreen-head"> <button type="button" class="link-back" data-open="home">← Назад</button> <h2>Меню по дням</h2> </div> <div class="panel-wrap"> <!-- РЕЦЕПТЫ ПО ДНЯМ --> <section id="panel-recipes" style="padding: 18px 18px 22px;" aria-label="Рецепты по дням"> <div class="toolbar"> <div> <p class="muted" style="margin:0;">Выбери день — завтрак, обед и ужин.</p> </div> <button type="button" class="btn" id="mealReset">Сбросить приёмы</button> </div> <div class="week-bar" aria-hidden="true"><i id="weekBarFill"></i></div> <p class="muted" style="margin:-8px 0 14px;"><span id="weekPct">0%</span> приёмов пищи на неделе</p> <div id="recipesRoot"></div> </section> </div> </div> <div id="screen-recipes-all" class="app-screen"> <div class="subscreen-head"> <button type="button" class="link-back" data-open="home">← Назад</button> <h2>Все рецепты</h2> </div> <div class="panel-wrap"> <div style="padding: 18px 18px 22px;"> <div class="cat-strip" id="recipeFilterStrip" role="tablist" aria-label="Фильтр по приёму пищи"> <button type="button" data-meal-filter="all" aria-selected="true">Все</button> <button type="button" data-meal-filter="breakfast" aria-selected="false">Завтрак</button> <button type="button" data-meal-filter="lunch" aria-selected="false">Обед</button> <button type="button" data-meal-filter="dinner" aria-selected="false">Ужин</button> </div> <div id="recipesFeedRoot"></div> </div> </div> </div> </main> <nav class="bottom-nav" aria-label="Основная навигация"> <button type="button" class="bottom-nav__btn is-active" data-nav="home"> <span class="bottom-nav__icon-wrap" aria-hidden="true"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8"/><path d="M5 10v10h14V10"/></svg> </span> Главная </button> <button type="button" class="bottom-nav__btn" data-nav="shop"> <span class="bottom-nav__icon-wrap" aria-hidden="true"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 8h16l-1.4 11.2A2 2 0 0 1 16.6 21H7.4a2 2 0 0 1-2-1.8L4 8z"/><path d="M8 8V6a4 4 0 0 1 8 0v2"/></svg> </span> Покупки </button> <button type="button" class="bottom-nav__btn" data-nav="preps"> <span class="bottom-nav__icon-wrap" aria-hidden="true"> <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 13a4 4 0 1 1 1.5-7.7 4 4 0 0 1 9 0A4 4 0 1 1 18 13"/><path d="M6 13h12v5a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-5z"/><path d="M8 17h8"/></svg> </span> Заготовки </button> <button type="button" class="bottom-nav__btn" data-nav="recipes-all"> <span class="bottom-nav__icon-wrap" aria-hidden="true"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5a2 2 0 0 1 2-2h12v18H6a2 2 0 0 1-2-2V5z"/><path d="M8 7h8M8 11h8M8 15h5"/></svg> </span> Рецепты </button> </nav> <button type="button" id="backToRecipe" aria-label="Вернуться к рецепту">← Вернуться к рецепту</button> <script> (function () { const STORAGE = { prod: "sekta-bc-w1-products", prep: "sekta-bc-w1-preps", meal: "sekta-bc-w1-meals", portion: "sekta-bc-w1-portion", returnMeal: "sekta-bc-w1-return-meal", returnScreen: "sekta-bc-w1-return-screen" }; const productCategories = [ { id: "grains", title: "Крупы и злаки", items: [ "Булгур — 100 г", "Киноа — 130 г", "Гречка — 100 г", "Рис бурый — 60 г", "Амарант — 50 г", "Пшено — 30 г", "Овсяные хлопья (не быстрого приготовления) — 100 г" ] }, { id: "meat", title: "Мясо, птица, рыба", items: [ "Куриная грудка — 320 г", "Фарш или филе индейки — 600 г", "Куриные бёдра без кожи — 1050 г", "Филе белой рыбы (минтай или треска) ❄️ — 800 г", "Тунец консервированный (в собственном соку) — 2 банки", "Скумбрия холодного копчения (филе) — 100 г" ], tip: "Для приготовления фарша рекомендуем использовать блендер или мясорубку. Если нет ни того, ни другого, можно купить готовый.", tipAfter: "Если ты не готовишь или пропускаешь какие-то из заготовок, сырые охлаждённые мясо, рыбу, птицу можно герметично упаковать, подписать датой заморозки и хранить в морозилке до полугода.\n\nРазмороженные продукты лучше сразу приготовить: повторная заморозка может ухудшить текстуру и сделать их менее безопасными для употребления." }, { id: "dairy", title: "Молочные продукты и яйца", items: [ "Творог 5% — 350 г", "Йогурт натуральный 2–5% — 300 г", "Фета или брынза — 130 г", "Яйца куриные — 13 шт." ] }, { id: "veg", title: "Овощи, ягоды, зелень", tip: "Руколу или салатный микс (450 г) рекомендуем купить половину к старту курса и вторую половину в среду или четверг.", items: [ "Помидор — 1 шт.", "Помидоры черри — 850 г", "Огурцы свежие — 300 г (3 шт.)", "Шпинат свежий — 100 г", "Рукола или салатный микс — 450 г", "Авокадо — 2 шт.", "Кабачок — 600 г (3 небольших)", "Баклажан — 600 г (3 небольших)", "Перец сладкий — 600 г (3 больших)", "Лук репчатый — 6 шт.", "Морковь — 3 шт.", "Капуста белокочанная (можно купить в нарезке) — 800 г", "Тыква — 750 г", "Картофель — 300 г (3 шт.)", "Свёкла — 750 г (5 шт.)", "Цветная капуста ❄️ — 250 г", "Брокколи ❄️ — 250 г", "Зелень (укроп, петрушка) — по 1 пучку", "Ягоды (малина, черника, смородина) ❄️ — 250 г" ] }, { id: "seeds", title: "Семечки, орехи, масла", items: [ "Миндаль — 20 г", "Семена чиа — 10 г", "Семечки тыквенные — 20 г", "Оливковое масло extra virgin — 1 бутылка" ] }, { id: "other", title: "Специи и прочее", items: [ "Сушёный орегано, базилик — по вкусу", "Томатная паста (без сахара) — 45 г", "Нут консервированный (без сахара) — 1 банка", "Хумус — 200 г (рекомендуем 2 упаковки по 100 г)" ] } ]; const prepStage1Ids = [ "prep-bulgur", "prep-quinoa", "prep-buckwheat", "prep-rice", "prep-amaranth", "prep-millet" ]; const prepStage3Ids = ["prep-soup-broccoli"]; const prepBlocks = [ { stage: 1, id: "prep-bulgur", title: "Булгур — 80 г сухого → 200 г готового", body: "Промыть, залить водой 1:2, варить 12–15 минут после закипания.\n\nРазделить и подписать контейнеры:\n100 г — «1 день», холодильник;\n100 г — «7 день», заморозить." }, { stage: 1, id: "prep-quinoa", title: "Киноа — 130 г сухого → 400 г готового", body: "Промыть в мелком сите, залить водой 1:2, варить 15 минут после закипания.\n\nРазделить и подписать контейнеры:\n150 г — «1 день», холодильник;\n150 г — «2 день», заморозить;\n100 г — «6 день», заморозить." }, { stage: 1, id: "prep-buckwheat", title: "Гречка — 100 г сухой → 330 г готовой", body: "Промыть, залить водой 1:2, варить 20 минут после закипания.\n\nРазделить и подписать контейнеры:\n150 г — «2 день», заморозить;\n180 г — «7 день», заморозить." }, { stage: 1, id: "prep-rice", title: "Рис бурый — 60 г сухого → 120 г готового", body: "Промыть, залить водой 1:2,5, варить 35–40 минут.\n\nПодписать контейнер:\n120 г — «3 день», заморозить." }, { stage: 1, id: "prep-amaranth", title: "Амарант — 50 г сухого → 120 г готового", body: "Промыть, залить водой 1:3, варить 20 минут на медленном огне (сильно пенится).\n\nПодписать контейнер:\n120 г — «6 день», заморозить." }, { stage: 1, id: "prep-millet", title: "Пшено — 30 г сухого → 150 г готового", body: "Промыть, залить кипятком на 1 минуту, слить, затем залить свежей водой 1:3, варить 20 минут.\n\nПодписать контейнер:\n150 г — «4 день», заморозить." }, { stage: 2, id: "prep-syrniki", title: "Сырники", body: "250 г творога смешать с 1 яйцом, 3 ст. л. овсяных хлопьев, сформовать 6 сырников, запечь 15 минут.\n\nРазделить на две порции по 3 шт. Один контейнер подписать «день 3», второй не подписывай — эти сырники можно съесть после курса. Заморозь всё." }, { stage: 2, id: "prep-chicken-breast", title: "Куриное филе и куриное бедро", body: "Куриное филе — 200 г\nОтбить, посыпать травами, завернуть в фольгу.\nЗапечь 20 минут.\nОхладить, убрать в холодильник.\n\nКуриное бедро — 1050 г\nПосыпать травами, завернуть в фольгу.\nЗапечь 20 минут.\nОхладить, заморозить на доске и расфасовать по зип-пакетам по 175 г / 2 шт. для дней 5, 7, 11, 13, 17, 21." }, { stage: 2, id: "prep-fish-cutlets", title: "Рыбные котлеты", body: "800 г филе, 1 луковицу пробить блендером, добавить 2 яйца, сформировать 8 котлет, запечь 20 минут.\n\nОхладить, заморозить на доске и расфасовать по зип-пакетам по 2 шт. и подписать «день 3», «день 9», «день 14», «день 20»." }, { stage: 2, id: "prep-veggies", title: "Смесь для запекания (запечённые овощи)", body: "Кабачок — 600 г, баклажан — 600 г, перец — 600 г, лук — 300 г нарезать крупными кубиками. Выложить на противень, сбрызнуть маслом, запекать 25 минут.\n\nОстудить, разложить в контейнеры и подписать: «день 1» (250 г), «день 4» (250 г), «день 7» (200 г), «день 8» (250 г), «день 11» (250 г), «день 14» (200 г), «день 15» (250 г), «день 18» (250 г), «день 21» (200 г).\nВсё, кроме контейнера для дня 1, заморозить." }, { stage: 2, id: "prep-pumpkin", title: "Тыква", body: "Рекомендуем купить готовую очищенную или замороженную.\n\n750 г нарезать кубиками 2×2 см, выложить на противень, сбрызнуть маслом, запекать 20 минут.\n\nОстудить, разделить примерно по 150 г, переложить в контейнеры «день 4», «день 10», «день 12», «день 17», «день 19» и заморозить." }, { stage: 2, id: "prep-potato", title: "Картофель", body: "150 г завернуть в фольгу, запечь в мундире 40 минут.\n\nОхладить, порезать на пластинки, положить в зип-пакет «день 6», заморозить." }, { stage: 2, id: "prep-beet", title: "Свёкла", body: "5 штук примерно по 150 г завернуть в фольгу, запекать 40 минут.\n\nОстудить, очистить, нарезать кубиками.\nПереложить в контейнеры: «день 3», «день 9», «день 12», «день 16», «день 19» и заморозить." }, { stage: 2, id: "prep-turkey-mince", title: "Фарш индейки — 600 г", body: "Обжарить на сухой сковороде с крупно нарезанным луком (1 шт.), натёртой морковью (1 шт.), добавить томатную пасту (3 ст. л.) и воду, тушить 10 минут.\n\nОхладить, разложить в контейнеры примерно по 200 г, подписать «день 2», «день 10», «день 16» и заморозить." }, { stage: 2, id: "prep-cabbage", title: "Тушёная капуста", body: "800 г капусты нашинковать, 2 моркови натереть, 2 луковицы нарезать. Обжарить, затем потушить со 100 мл воды 30 минут.\n\nОхладить, разделить примерно по 400 г, подписать контейнеры «день 7», «день 13», заморозить." }, { stage: 3, id: "prep-soup-broccoli", title: "Суп-пюре из брокколи", body: "Ингредиенты:\nкуриная грудка — 120 г;\nброкколи — 250 г;\nцветная капуста — 250 г;\nкартофель — 150 г.\n\nПриготовление:\nКуриную грудку 120 г отварить в 400 мл воды. Снять пену, отставить для варки овощей.\nБрокколи и цветную капусту разобрать на соцветия.\nКартофель очистить и мелко нарезать.\nВ кастрюле сварить овощи до мягкости, добавить курицу, пробить блендером.\nОстудить, переложить в контейнеры по 400 мл, один подписать «день 4», второй «день 5». Оба заморозить." } ]; const days = [ { id: "d1", label: "День 1 — понедельник", meals: [ { id: "d1-b", title: "Завтрак — Омлет с томатами, шпинатом и булгуром", time: "⏱️ 10 минут", ingredients: [ "яйца — 3 шт.", "помидор — 150 г (1 шт.)", "шпинат свежий — 50 г (горсть)", { t: "булгур отварной — 100 г", link: "prep-bulgur" }, "оливковое масло — 10 г (1 ч. л.)", "зелень (укроп, петрушка) — по вкусу" ], steps: [ "Помидор нарезать кубиками, шпинат крупно порвать.", "Яйца взбить вилкой.", "На сковороде с антипригарным покрытием припустить помидор и шпинат с 1 ст. л. воды 2–3 минуты.", "Добавить булгур, прогреть 1 минуту.", "Залить яйцами, готовить под крышкой на среднем огне 5–7 минут.", "Полить маслом, посыпать зеленью." ] }, { id: "d1-l", title: "Обед — Куриная грудка с запечёнными овощами и киноа", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "куриная грудка запечённая — 100 г", link: "prep-chicken-breast" }, { t: "запечённые овощи — 150 г", link: "prep-veggies" }, { t: "киноа отварная — 100 г", link: "prep-quinoa" }, "оливковое масло — 20 г (2 ч. л.)" ], steps: [ "Курицу и овощи разогреть в духовке или микроволновке.", "Киноа разогреть.", "Выложить всё на тарелку, полить маслом." ] }, { id: "d1-d", title: "Ужин — Тарелка с тунцом, салатом и авокадо", time: "⏱️ 5 минут (сборка)", ingredients: [ "тунец консервированный — 120 г (1 банка без жидкости)", "салатный микс — 75 г", "помидоры черри — 150 г", "авокадо — ½ шт.", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Помидоры черри разрезать пополам.", "Авокадо нарезать кубиками.", "На тарелку выложить салатный микс, черри, авокадо.", "Сверху выложить тунец, размяв вилкой.", "Полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 2:\n— киноа — 150 г;\n— ягоды — 120 г (если у тебя замороженные);\n— фарш индейки — 200 г;\n— гречка — 150 г." } ] }, { id: "d2", label: "День 2 — вторник", meals: [ { id: "d2-b", title: "Завтрак — Киноа с йогуртом и ягодами", time: "⏱️ 3 минуты", ingredients: [ { t: "киноа отварная — 150 г", link: "prep-quinoa" }, "йогурт натуральный — 200 г", "ягоды — 120 г", "семена чиа — 10 г (1 ч. л.)" ], steps: [ "Киноа разогреть.", "Смешать с йогуртом, ягодами, семенами чиа." ] }, { id: "d2-l", title: "Обед — Гречка с фаршем индейки и томатным соусом", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "фарш индейки готовый — 200 г", link: "prep-turkey-mince" }, { t: "гречка отварная — 150 г", link: "prep-buckwheat" }, "оливковое масло — 15 г (3 ч. л.)" ], steps: [ "Фарш и гречку разогреть в микроволновке или на сковороде.", "Перемешать, полить маслом." ] }, { id: "d2-d", title: "Ужин — Тарелка с нутом, фетой и огурцом", time: "⏱️ 5 минут (сборка)", ingredients: [ "нут консервированный — 0,5 банки (промыть)", "фета — 70 г", "рукола — 75 г", "помидоры черри — 150 г", "огурец — 100 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Нут промыть в дуршлаге.", "Помидоры черри разрезать пополам, огурец нарезать кружочками.", "На тарелку выложить руколу, нут, черри, огурец.", "Сверху раскрошить фету, полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 3:\n— сырники — 3 шт.;\n— котлеты рыбные — 2 шт.;\n— рис бурый — 120 г;\n— ягоды — 50 г (если у тебя замороженные).\nТакже свари 2 яйца." } ] }, { id: "d3", label: "День 3 — среда", meals: [ { id: "d3-b", title: "Завтрак — Сырники с ягодным соусом", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "сырники запечённые — 3 шт. (150 г)", link: "prep-syrniki" }, "йогурт натуральный — 80 г", "ягоды — 50 г" ], steps: [ "Сырники разогреть в микроволновке или духовке 3–4 минуты.", "Ягоды слегка размять вилкой.", "Подавать с йогуртом и ягодами." ] }, { id: "d3-l", title: "Обед — Рыбные котлеты с бурым рисом и салатом", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "рыбные котлеты — 2 шт.", link: "prep-fish-cutlets" }, { t: "рис бурый отварной — 120 г", link: "prep-rice" }, "помидоры черри — 150 г", "огурец — 100 г", "авокадо — ¼ шт.", "оливковое масло — 15 г (3 ч. л.)" ], steps: [ "Котлеты и рис разогреть.", "Черри разрезать пополам, огурец нарезать кружочками, авокадо нарезать кубиками.", "На тарелку выложить рис, котлеты, овощи.", "Полить маслом." ] }, { id: "d3-d", title: "Ужин — Тарелка со свёклой, яйцом и творогом", time: "⏱️ 5 минут (сборка)", ingredients: [ { t: "свёкла запечённая — 150 г", link: "prep-beet" }, "яйца варёные — 2 шт.", "творог 5% — 100 г", "рукола — 75 г", "оливковое масло — 15 г (3 ч. л.)" ], steps: [ "Яйца очистить, нарезать дольками.", "На тарелку выложить руколу, свёклу, яйца.", "Сверху выложить творог ложкой.", "Полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 4:\n— пшено — 150 г;\n— тыква — 200 г;\n— суп-пюре — 400 мл;\n— запечённые овощи — 250 г." } ] }, { id: "d4", label: "День 4 — четверг", meals: [ { id: "d4-b", title: "Завтрак — Пшённая каша с запечённой тыквой и семечками", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "пшено отварное — 150 г", link: "prep-millet" }, { t: "тыква запечённая — 200 г", link: "prep-pumpkin" }, "семечки тыквенные — 20 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Пшено и тыкву разогреть.", "Смешать, посыпать семечками, полить маслом." ] }, { id: "d4-l", title: "Обед — Суп-пюре из брокколи с курицей", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "суп-пюре из брокколи — 400 г", link: "prep-soup-broccoli" }, "тыквенные семечки — 15 г" ], steps: [ "Суп разогреть.", "Посыпать семечками." ] }, { id: "d4-d", title: "Ужин — Запечённые овощи с хумусом и фетой", time: "⏱️ 3 минуты (сборка)", ingredients: [ { t: "запечённые овощи — 250 г", link: "prep-veggies" }, "хумус — 100 г", "фета — 60 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Овощи разогреть.", "Выложить на тарелку.", "Сверху выложить хумус, раскрошить фету.", "Полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 5:\n— суп-пюре — 400 мл;\n— куриные бёдра — 2 шт / 175 г." } ] }, { id: "d5", label: "День 5 — пятница", meals: [ { id: "d5-b", title: "Завтрак — Овсяная каша с ягодами и миндалём", time: "⏱️ 7 минут", ingredients: [ "овсяные хлопья — 50 г", "вода — 120 мл", "ягоды замороженные — 80 г", "миндаль — 20 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Хлопья залить кипятком, накрыть крышкой, дать настояться 5 минут.", "Добавить ягоды (не размораживать), миндаль, масло." ] }, { id: "d5-l", title: "Обед — Суп-пюре из брокколи с курицей", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "суп-пюре из брокколи — 400 г", link: "prep-soup-broccoli" }, "тыквенные семечки — 15 г" ], steps: [ "Суп разогреть.", "Посыпать семечками." ] }, { id: "d5-d", title: "Ужин — Куриные бёдра с овощным салатом", time: "⏱️ 5 минут (разогреть + нарезка)", ingredients: [ { t: "куриные бёдра — 175 г", link: "prep-chicken-breast" }, "салатный микс — 75 г", "помидоры черри — 150 г", "огурец — 100 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Куриные бёдра разогреть.", "Черри разрезать пополам, огурец нарезать кружочками.", "На тарелку выложить салатный микс, черри, огурец, куриные бёдра.", "Полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 6:\n— амарант — 120 г;\n— киноа — 100 г;\n— картофель — 150 г." } ] }, { id: "d6", label: "День 6 — суббота", meals: [ { id: "d6-b", title: "Завтрак — Овощной омлет с амарантом и зеленью", time: "⏱️ 10 минут", ingredients: [ "яйца — 3 шт.", { t: "амарант отварной — 120 г", link: "prep-amaranth" }, "помидоры черри — 100 г", "шпинат — 50 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Яйца взбить с амарантом.", "Черри разрезать пополам, шпинат порвать.", "Вылить яичную смесь на сковороду, сверху выложить черри и шпинат.", "Готовить под крышкой на среднем огне 7–8 минут.", "Полить маслом." ] }, { id: "d6-l", title: "Обед — Салат с тунцом, яйцом и киноа", time: "⏱️ 7 минут", ingredients: [ "тунец консервированный — 120 г (1 банка без жидкости)", { t: "киноа отварная — 100 г", link: "prep-quinoa" }, "помидоры черри — 150 г", "авокадо — ¼ шт.", "рукола — 75 г", "оливковое масло — 15 г (3 ч. л.)" ], steps: [ "Черри разрезать пополам, авокадо нарезать кубиками.", "В миске смешать руколу, киноа, черри, авокадо, тунец.", "Заправить маслом." ] }, { id: "d6-d", title: "Ужин — Скумбрия с запечённым картофелем и руколой", time: "⏱️ 5 минут (сборка)", ingredients: [ "скумбрия холодного копчения (филе) — 100 г", { t: "картофель запечённый — 150 г", link: "prep-potato" }, "рукола — 75 г", "оливковое масло — 10 г (2 ч. л.)" ], steps: [ "Картофель разогреть, можно слегка обжарить на сухой сковороде.", "На тарелку выложить руколу, картофель, скумбрию.", "Полить маслом." ], note: "Поставить в холодильник размораживаться базу для дня 7:\n— булгур — 100 г;\n— куриные бёдра — 175 г;\n— капуста тушёная — 400 г;\n— гречка — 180 г;\n— запечённые овощи — 200 г." } ] }, { id: "d7", label: "День 7 — воскресенье", meals: [ { id: "d7-b", title: "Завтрак — Яйца пашот с авокадо, руколой и булгуром", time: "⏱️ 10 минут", ingredients: [ "яйца — 2 шт.", "авокадо — 1 шт.", "рукола — 75 г", { t: "булгур отварной — 100 г", link: "prep-bulgur" }, "оливковое масло — 10 г (1 ч. л.)" ], steps: [ "Булгур разогреть.", "В кастрюле вскипятить воду, сделать воронку ложкой, аккуратно влить яйца. Варить 2–3 минуты.", "Авокадо нарезать ломтиками.", "На тарелку выложить руколу, булгур, авокадо.", "Сверху выложить яйца пашот.", "Полить маслом." ] }, { id: "d7-l", title: "Обед — Куриные бёдра, тушёные с капустой, и гречка", time: "⏱️ 5 минут (разогреть)", ingredients: [ { t: "куриные бёдра запечённые — 175 г", link: "prep-chicken-breast" }, { t: "капуста тушёная — 400 г", link: "prep-cabbage" }, { t: "гречка отварная — 180 г", link: "prep-buckwheat" }, "оливковое масло — 15 г (3 ч. л.)" ], steps: [ "Бёдра и капусту разогреть.", "Гречку разогреть.", "Выложить на тарелку, полить маслом." ] }, { id: "d7-d", title: "Ужин — Запечённые овощи с хумусом", time: "⏱️ 3 минуты (сборка)", ingredients: [ { t: "запечённые овощи — 200 г", link: "prep-veggies" }, "хумус — 100 г", "оливковое масло — 10 г (1 ч. л.)" ], steps: [ "Овощи разогреть.", "Выложить на тарелку.", "Сверху выложить хумус.", "Полить маслом." ] } ] } ]; function load(key) { try { return JSON.parse(localStorage.getItem(key) || "{}"); } catch { return {}; } } function save(key, obj) { localStorage.setItem(key, JSON.stringify(obj)); } /* ——— Продукты UI ——— */ const catStrip = document.getElementById("catStrip"); const catPanels = document.getElementById("catPanels"); const prodGlobal = document.getElementById("prodGlobal"); function productCheckboxId(catId, index) { return "p-" + catId + "-" + index; } function buildProducts() { productCategories.forEach((cat, ci) => { const tab = document.createElement("button"); tab.type = "button"; tab.setAttribute("role", "tab"); tab.setAttribute("aria-selected", ci === 0 ? "true" : "false"); tab.textContent = cat.title; tab.dataset.cat = cat.id; catStrip.appendChild(tab); const panel = document.createElement("div"); panel.className = "cat-panel" + (ci === 0 ? " active" : ""); panel.dataset.catPanel = cat.id; panel.setAttribute("role", "tabpanel"); const head = document.createElement("div"); head.className = "cat-head"; head.innerHTML = "<strong>" + escapeHtml(cat.title) + "</strong>"; panel.appendChild(head); if (cat.tip) { const tp = document.createElement("p"); tp.className = "muted"; tp.style.marginTop = "-4px"; tp.style.marginBottom = "10px"; tp.dataset.original = cat.tip; tp.dataset.tipKind = "tip"; tp.textContent = cat.tip; panel.appendChild(tp); } const ul = document.createElement("ul"); ul.className = "items"; cat.items.forEach((text, idx) => { const id = productCheckboxId(cat.id, idx); const li = document.createElement("li"); li.className = "item"; li.dataset.original = text; li.innerHTML = "<input type=\"checkbox\" id=\"" + id + "\" data-store=\"prod\" data-key=\"" + id + "\"><label for=\"" + id + "\">" + escapeHtml(text) + "</label>"; ul.appendChild(li); }); panel.appendChild(ul); if (cat.tipAfter) { const ta = document.createElement("div"); ta.className = "tip-box"; ta.style.whiteSpace = "pre-line"; ta.style.marginTop = "12px"; ta.textContent = cat.tipAfter; panel.appendChild(ta); } catPanels.appendChild(panel); }); catStrip.addEventListener("click", function (e) { const btn = e.target.closest("button[data-cat]"); if (!btn) return; const id = btn.dataset.cat; catStrip.querySelectorAll("button").forEach(function (b) { b.setAttribute("aria-selected", b.dataset.cat === id ? "true" : "false"); }); catPanels.querySelectorAll(".cat-panel").forEach(function (p) { p.classList.toggle("active", p.dataset.catPanel === id); }); }); } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """); } /* ——— Множитель порций для списка покупок ——— */ function getPortionMultiplier() { try { const v = parseInt(localStorage.getItem(STORAGE.portion) || "1", 10); return v && v >= 1 && v <= 4 ? v : 1; } catch (e) { return 1; } } function setPortionMultiplier(m) { try { localStorage.setItem(STORAGE.portion, String(m)); } catch (e) {} } function formatNumber(n) { if (Number.isInteger(n)) return String(n); const fixed = n.toFixed(2).replace(/0+$/, "").replace(/\.$/, ""); return fixed.replace(".", ","); } function multiplyNumbersIn(s, m) { return s.replace( /(по\s+)?(\d+(?:[.,]\d+)?)(\s*[–-]\s*\d+(?:[.,]\d+)?)?(\s*%)?/g, function (match, perPrefix, num, range, percent) { if (perPrefix) return match; if (percent) return match; if (range) return match; const val = parseFloat(num.replace(",", ".")); if (Number.isNaN(val)) return match; return formatNumber(val * m); } ); } function multiplyText(text, m) { if (!m || m === 1) return text; const sep = " — "; const idx = text.indexOf(sep); if (idx >= 0) { return text.slice(0, idx) + sep + multiplyNumbersIn(text.slice(idx + sep.length), m); } return multiplyNumbersIn(text, m); } function applyPortionMultiplier() { const m = getPortionMultiplier(); document.querySelectorAll("#catPanels .item[data-original]").forEach(function (li) { const lab = li.querySelector("label"); if (lab) lab.textContent = multiplyText(li.dataset.original, m); }); document.querySelectorAll("#catPanels [data-tip-kind][data-original]").forEach(function (tp) { tp.textContent = multiplyText(tp.dataset.original, m); }); const strip = document.getElementById("portionStrip"); if (strip) { strip.querySelectorAll("button[data-mult]").forEach(function (b) { b.setAttribute("aria-selected", parseInt(b.dataset.mult, 10) === m ? "true" : "false"); }); } } function bindStorageInputs(storageKey, getAll) { function apply() { const state = load(storageKey); getAll().forEach(function (inp) { const k = inp.dataset.key; inp.checked = Boolean(state[k]); const row = inp.closest(".item"); if (row) row.classList.toggle("checked", inp.checked); }); } getAll().forEach(function (inp) { inp.addEventListener("change", function () { const state = load(storageKey); state[inp.dataset.key] = inp.checked; save(storageKey, state); apply(); refreshProductStats(); refreshPrepStats(); refreshMealStats(); }); }); apply(); } function refreshProductStats() { const inputs = Array.from(document.querySelectorAll('input[data-store="prod"]')); let done = 0; inputs.forEach(function (inp) { if (inp.checked) done++; }); const total = inputs.length; prodGlobal.textContent = total ? Math.round((done / total) * 100) + "%" : "0%"; inputs.forEach(function (inp) { const row = inp.closest(".item"); if (row) row.classList.toggle("checked", inp.checked); }); refreshDashboard(); } function buildPreps() { const stageHost = { 1: document.getElementById("prepsStage1"), 2: document.getElementById("prepsStage2"), 3: document.getElementById("prepsStage3") }; prepBlocks.forEach(function (block) { const wrap = document.createElement("div"); wrap.className = "prep-card"; wrap.id = block.id; const cbId = "prep-cb-" + block.id; const bodyContent = block.bodyHtml ? block.bodyHtml : escapeHtml(block.body); wrap.innerHTML = "<h4><input type=\"checkbox\" id=\"" + cbId + "\" data-store=\"prep\" data-key=\"" + block.id + "\"><label for=\"" + cbId + "\" style=\"cursor:pointer\">" + escapeHtml(block.title) + "</label></h4><p class=\"prep-body\">" + bodyContent + "</p>"; const host = stageHost[block.stage] || stageHost[2]; host.appendChild(wrap); }); } function refreshPrepStats() { const prepInputs = Array.from(document.querySelectorAll('input[data-store="prep"]')); let done = 0; prepInputs.forEach(function (inp) { if (inp.checked) done++; const row = inp.closest(".item"); if (row) row.classList.toggle("checked", inp.checked); }); document.getElementById("prepProgress").textContent = done + " из " + prepInputs.length; refreshDashboard(); } function ingredientLine(ing) { if (typeof ing === "string") return "<li>" + escapeHtml(ing) + "</li>"; const cls = ing.book ? "prep-link book-link" : "prep-link"; return "<li><a href=\"#" + ing.link + "\" class=\"" + cls + "\" data-prep-target=\"" + ing.link + "\">" + escapeHtml(ing.t) + "</a></li>"; } function renderSteps(steps) { if (Array.isArray(steps)) { return '<ol class="meal-steps">' + steps.map(function (s) { return "<li>" + escapeHtml(s) + "</li>"; }).join("") + "</ol>"; } return '<p class="muted" style="margin:0;">' + escapeHtml(steps) + "</p>"; } function renderNote(note) { return '<p class="tip-box" style="margin-top:12px;white-space:pre-line;"><strong>Наперёд:</strong> ' + escapeHtml(note) + "</p>"; } const recipeDayTabLabel = { d1: "1 · пн", d2: "2 · вт", d3: "3 · ср", d4: "4 · чт", d5: "5 · пт", d6: "6 · сб", d7: "7 · вс" }; const homeDayShort = { d1: "пн", d2: "вт", d3: "ср", d4: "чт", d5: "пт", d6: "сб", d7: "вс" }; const mealKindOrder = ["breakfast", "lunch", "dinner"]; const mealKindRu = { breakfast: "Завтрак", lunch: "Обед", dinner: "Ужин" }; function showScreen(name, opts) { opts = opts || {}; document.querySelectorAll(".app-screen").forEach(function (el) { el.classList.toggle("is-active", el.id === "screen-" + name); }); document.querySelectorAll(".bottom-nav__btn[data-nav]").forEach(function (btn) { btn.classList.toggle("is-active", btn.dataset.nav === name); }); if (name === "home" && !opts.keepReturn) { sessionStorage.removeItem(STORAGE.returnMeal); sessionStorage.removeItem(STORAGE.returnScreen); } if (name === "days" && opts.dayId) { switchRecipeDay(opts.dayId); } updateBackVisibility(); } function refreshDashboard() { const prodInputs = document.querySelectorAll('input[data-store="prod"]'); let pDone = 0; prodInputs.forEach(function (i) { if (i.checked) pDone++; }); const dashShop = document.getElementById("dashShopLine"); if (dashShop) dashShop.textContent = pDone + " / " + prodInputs.length + " куплено"; const prepInputs = document.querySelectorAll('input[data-store="prep"]'); let prDone = 0; prepInputs.forEach(function (i) { if (i.checked) prDone++; }); const dashPrep = document.getElementById("dashPrepLine"); if (dashPrep) dashPrep.textContent = prDone + " / " + prepInputs.length + " шагов · воскресенье"; days.forEach(function (day) { let d = 0; day.meals.forEach(function (m) { const el = document.getElementById("m-" + m.id); if (el && el.checked) d++; }); const badge = document.querySelector('[data-home-day-status="' + day.id + '"]'); if (badge) badge.textContent = d + "/3 готово"; }); } function buildHomeDaysGrid() { const grid = document.getElementById("homeDaysGrid"); if (!grid) return; const cycle = ["a", "b", "c"]; days.forEach(function (day, i) { const num = day.id.replace(/^d/, ""); const btn = document.createElement("button"); btn.type = "button"; btn.className = "day-tile day-tile--" + cycle[i % 3]; btn.dataset.dayId = day.id; btn.innerHTML = '<div class="day-tile__num">' + escapeHtml(num) + '<small>' + escapeHtml(homeDayShort[day.id] || "") + "</small></div>" + '<div class="day-tile__status" data-home-day-status="' + day.id + '">0/3 готово</div>'; grid.appendChild(btn); }); grid.addEventListener("click", function (e) { const t = e.target.closest(".day-tile[data-day-id]"); if (!t) return; showScreen("days", { dayId: t.dataset.dayId }); }); } function buildRecipesFeed() { const root = document.getElementById("recipesFeedRoot"); if (!root) return; days.forEach(function (day) { day.meals.forEach(function (meal, idx) { const kind = mealKindOrder[idx]; const ingList = meal.ingredients.map(ingredientLine).join(""); const art = document.createElement("article"); art.className = "recipe-feed-card meal-" + kind; art.dataset.kind = kind; art.dataset.mealAnchor = meal.id; art.innerHTML = '<div class="recipe-feed-card__row">' + '<div class="recipe-feed-card__icon" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M7 3v9a2 2 0 1 0 4 0V3"/><path d="M9 12v9"/><path d="M16 3v18"/><path d="M16 13c-2 0-3-1-3-4s1-5 3-6"/></svg></div>' + '<div class="recipe-feed-card__body">' + '<div class="recipe-feed-card__meta">День ' + escapeHtml(day.id.replace(/^d/, "")) + " · " + escapeHtml(mealKindRu[kind]) + "</div>" + '<h3 class="recipe-feed-card__title">' + escapeHtml(meal.title) + "</h3>" + '<div class="recipe-feed-card__time">🕒 ' + escapeHtml(meal.time) + "</div></div>" + '<button type="button" class="recipe-feed-done" data-meal-id="' + escapeHtml(meal.id) + '" aria-label="Отметить приготовленное"></button></div>' + "<details><summary>Ингредиенты и приготовление</summary>" + '<div class="meal-detail-inner">' + "<h5>Ингредиенты</h5><ul>" + ingList + "</ul>" + "<h5>Приготовление</h5>" + renderSteps(meal.steps) + (meal.note ? renderNote(meal.note) : "") + "</div></details>"; root.appendChild(art); }); }); } function syncRecipeFeedDone() { document.querySelectorAll(".recipe-feed-done[data-meal-id]").forEach(function (btn) { const cb = document.getElementById("m-" + btn.dataset.mealId); btn.classList.toggle("is-done", Boolean(cb && cb.checked)); }); } function switchRecipeDay(dayId) { document.querySelectorAll("#recipeDayStrip button[data-recipe-day]").forEach(function (b) { b.setAttribute("aria-selected", b.dataset.recipeDay === dayId ? "true" : "false"); }); document.querySelectorAll(".recipe-day-panel").forEach(function (p) { p.classList.toggle("active", p.dataset.recipePanel === dayId); }); } function dayIdFromMealId(mealId) { const m = String(mealId || "").match(/^(d\d+)/); return m ? m[1] : null; } function switchPrepTab(tab) { document.querySelectorAll("#prepStrip button[data-prep-tab]").forEach(function (b) { b.setAttribute("aria-selected", b.dataset.prepTab === tab ? "true" : "false"); }); document.querySelectorAll(".prep-subpanel[data-prep-panel]").forEach(function (p) { p.classList.toggle("active", p.dataset.prepPanel === tab); }); } function prepTabForTarget(targetId) { if (prepStage1Ids.indexOf(targetId) !== -1) return "stage1"; if (prepStage3Ids.indexOf(targetId) !== -1) return "stage3"; return "stage2"; } function openPrepTarget(targetId) { switchPrepTab(prepTabForTarget(targetId)); requestAnimationFrame(function () { const dest = document.getElementById(targetId); if (dest) dest.scrollIntoView({ behavior: "smooth", block: "start" }); }); } function buildRecipes() { const root = document.getElementById("recipesRoot"); const strip = document.createElement("div"); strip.className = "cat-strip"; strip.id = "recipeDayStrip"; strip.setAttribute("role", "tablist"); strip.setAttribute("aria-label", "Дни недели"); const panelsHost = document.createElement("div"); panelsHost.id = "recipePanelsHost"; days.forEach(function (day, di) { const btn = document.createElement("button"); btn.type = "button"; btn.dataset.recipeDay = day.id; btn.setAttribute("aria-selected", di === 0 ? "true" : "false"); btn.textContent = recipeDayTabLabel[day.id] || day.id; btn.title = day.label; strip.appendChild(btn); const panel = document.createElement("div"); panel.className = "recipe-day-panel" + (di === 0 ? " active" : ""); panel.dataset.recipePanel = day.id; const head = document.createElement("div"); head.className = "day-head"; head.innerHTML = "<strong>" + escapeHtml(day.label) + "</strong><span class=\"day-progress\" data-day-progress=\"" + day.id + "\"><span>0%</span> дня</span>"; panel.appendChild(head); day.meals.forEach(function (meal) { const section = document.createElement("section"); section.className = "meal"; section.id = meal.id; const ingList = meal.ingredients.map(ingredientLine).join(""); section.innerHTML = "<div class=\"meal-title\"><input type=\"checkbox\" data-store=\"meal\" data-key=\"" + meal.id + "\" id=\"m-" + meal.id + "\"><label for=\"m-" + meal.id + "\" style=\"cursor:pointer;flex:1;\"><h4 style=\"margin:0;font-size:15px;font-weight:800;\">" + escapeHtml(meal.title) + "</h4></label><span class=\"time-badge\">" + escapeHtml(meal.time) + "</span></div>" + "<h5>Ингредиенты</h5><ul>" + ingList + "</ul>" + "<h5>Приготовление</h5>" + renderSteps(meal.steps); panel.appendChild(section); }); const aheadNotes = day.meals .map(function (m) { return m.note; }) .filter(Boolean); if (aheadNotes.length) { const ahead = document.createElement("section"); ahead.className = "day-ahead"; ahead.innerHTML = "<h5>Наперёд</h5>" + aheadNotes .map(function (n) { return '<p>' + escapeHtml(n).replace(/\n/g, "<br>") + "</p>"; }) .join(""); panel.appendChild(ahead); } panelsHost.appendChild(panel); }); strip.addEventListener("click", function (e) { const b = e.target.closest("button[data-recipe-day]"); if (!b) return; switchRecipeDay(b.dataset.recipeDay); }); root.appendChild(strip); root.appendChild(panelsHost); } function refreshMealStats() { const mealInputs = Array.from(document.querySelectorAll('input[data-store="meal"]')); let weekDone = 0; const weekTotal = mealInputs.length; mealInputs.forEach(function (inp) { if (inp.checked) weekDone++; const lab = inp.closest(".meal-title"); if (lab) { const h = lab.querySelector("h4"); if (h) h.style.opacity = inp.checked ? "0.55" : "1"; } }); const pct = weekTotal ? Math.round((weekDone / weekTotal) * 100) : 0; document.getElementById("weekBarFill").style.width = pct + "%"; document.getElementById("weekPct").textContent = pct + "%"; days.forEach(function (day) { let d = 0; day.meals.forEach(function (m) { const el = document.getElementById("m-" + m.id); if (el && el.checked) d++; }); const dp = document.querySelector('[data-day-progress="' + day.id + '"] span'); if (dp) dp.textContent = Math.round((d / 3) * 100) + "%"; }); syncRecipeFeedDone(); refreshDashboard(); } /* Навигация: экраны и рецепт → заготовка → назад */ const backBtn = document.getElementById("backToRecipe"); const screenPreps = document.getElementById("screen-preps"); document.querySelector(".page").addEventListener("click", function (e) { const openBtn = e.target.closest("[data-open]"); if (!openBtn) return; const target = openBtn.dataset.open; if (target === "home") { showScreen("home"); return; } if (target === "shop") { showScreen("shop"); return; } if (target === "preps") { sessionStorage.removeItem(STORAGE.returnMeal); sessionStorage.removeItem(STORAGE.returnScreen); showScreen("preps"); return; } if (target === "recipes-all") { showScreen("recipes-all"); } }); document.querySelectorAll(".bottom-nav__btn[data-nav]").forEach(function (btn) { btn.addEventListener("click", function () { sessionStorage.removeItem(STORAGE.returnMeal); sessionStorage.removeItem(STORAGE.returnScreen); showScreen(btn.dataset.nav); }); }); function updateBackVisibility() { const ret = sessionStorage.getItem(STORAGE.returnMeal); if (ret && screenPreps.classList.contains("is-active")) { backBtn.classList.add("visible"); } else { backBtn.classList.remove("visible"); } } document.querySelector(".page").addEventListener("click", function (e) { const a = e.target.closest("a[data-prep-target]"); if (!a) return; e.preventDefault(); const prepTarget = a.getAttribute("data-prep-target"); const mealEl = a.closest(".meal"); const feedCard = a.closest("[data-meal-anchor]"); const fromPreps = !!a.closest("#panel-preps"); if (mealEl) { sessionStorage.setItem(STORAGE.returnMeal, mealEl.id); sessionStorage.setItem(STORAGE.returnScreen, "days"); } else if (feedCard) { sessionStorage.setItem(STORAGE.returnMeal, feedCard.getAttribute("data-meal-anchor")); sessionStorage.setItem(STORAGE.returnScreen, "recipes-all"); } if (!fromPreps) { showScreen("preps"); } openPrepTarget(prepTarget); updateBackVisibility(); }); (function () { const recipesFeedRoot = document.getElementById("recipesFeedRoot"); if (!recipesFeedRoot) return; recipesFeedRoot.addEventListener("click", function (e) { const btn = e.target.closest(".recipe-feed-done"); if (!btn) return; const mid = btn.dataset.mealId; const cb = document.getElementById("m-" + mid); if (cb) { cb.checked = !cb.checked; cb.dispatchEvent(new Event("change", { bubbles: true })); } }); })(); (function () { const recipeFilterStrip = document.getElementById("recipeFilterStrip"); if (!recipeFilterStrip) return; recipeFilterStrip.addEventListener("click", function (e) { const b = e.target.closest("[data-meal-filter]"); if (!b) return; const f = b.dataset.mealFilter; recipeFilterStrip.querySelectorAll("button").forEach(function (x) { x.setAttribute("aria-selected", x === b ? "true" : "false"); }); document.querySelectorAll("#recipesFeedRoot .recipe-feed-card").forEach(function (card) { const show = f === "all" || card.dataset.kind === f; if (show) { card.removeAttribute("data-hidden-filter"); } else { card.setAttribute("data-hidden-filter", "1"); } }); }); })(); backBtn.addEventListener("click", function () { const id = sessionStorage.getItem(STORAGE.returnMeal); const retScr = sessionStorage.getItem(STORAGE.returnScreen) || "days"; sessionStorage.removeItem(STORAGE.returnMeal); sessionStorage.removeItem(STORAGE.returnScreen); showScreen(retScr); if (retScr === "days") { const dayId = dayIdFromMealId(id); if (dayId) switchRecipeDay(dayId); } updateBackVisibility(); if (id) { requestAnimationFrame(function () { let dest = document.getElementById(id); if (!dest && retScr === "recipes-all") { dest = document.querySelector('[data-meal-anchor="' + id + '"]'); } if (dest) dest.scrollIntoView({ behavior: "smooth", block: "start" }); }); } }); document.getElementById("prodReset").addEventListener("click", function () { save(STORAGE.prod, {}); document.querySelectorAll('input[data-store="prod"]').forEach(function (i) { i.checked = false; }); refreshProductStats(); }); document.getElementById("prodAll").addEventListener("click", function () { const state = {}; document.querySelectorAll('input[data-store="prod"]').forEach(function (i) { i.checked = true; state[i.dataset.key] = true; }); save(STORAGE.prod, state); refreshProductStats(); }); document.getElementById("prepReset").addEventListener("click", function () { save(STORAGE.prep, {}); document.querySelectorAll('input[data-store="prep"]').forEach(function (i) { i.checked = false; }); refreshPrepStats(); }); document.getElementById("mealReset").addEventListener("click", function () { save(STORAGE.meal, {}); document.querySelectorAll('input[data-store="meal"]').forEach(function (i) { i.checked = false; }); refreshMealStats(); }); buildProducts(); buildPreps(); buildRecipes(); buildHomeDaysGrid(); buildRecipesFeed(); document.getElementById("prepStrip").addEventListener("click", function (e) { const b = e.target.closest("button[data-prep-tab]"); if (!b) return; switchPrepTab(b.dataset.prepTab); }); const portionStrip = document.getElementById("portionStrip"); if (portionStrip) { portionStrip.addEventListener("click", function (e) { const b = e.target.closest("button[data-mult]"); if (!b) return; const m = parseInt(b.dataset.mult, 10) || 1; setPortionMultiplier(m); applyPortionMultiplier(); }); } applyPortionMultiplier(); bindStorageInputs(STORAGE.prod, function () { return Array.from(document.querySelectorAll('input[data-store="prod"]')); }); bindStorageInputs(STORAGE.prep, function () { return Array.from(document.querySelectorAll('input[data-store="prep"]')); }); bindStorageInputs(STORAGE.meal, function () { return Array.from(document.querySelectorAll('input[data-store="meal"]')); }); refreshProductStats(); refreshPrepStats(); refreshMealStats(); syncRecipeFeedDone(); refreshDashboard(); })(); </script> </body> </html>